如何在数据表中查找匹配记录

4
我有一个数据表,其中包含两列:数字和日期,例如:
125 | 2013/10/20 100 | 2013/10/21 150 | 2013/10/24 225 | 2013/10/24 250 | 2013/10/28 310 | 2013/10/30
现在,我想按日期搜索所有记录,并使数字之和匹配为500。 我可以轻松地看到第一、第三和第四条记录(125 + 150 + 225 = 500)提供了匹配,但是要编写这样的程序,我只能想到通过数据表数百万次,直到找到正确的匹配。
有没有更聪明的主意?

你有多少条记录? - Aage
@bump - 可能超过一千 - Menno
可能是查找所有可能的数字组合以达到给定总和的重复问题。 - mbeckish
2
这是子集和问题 - mbeckish
显示剩余5条评论
1个回答

2
在最坏的情况下,您确实需要遍历数据集的所有2^n个子集,但如果您的所有项都是非负数,则可以从过滤掉“item.Number <= 500”开始。
这里是一个可能的“Subsets”方法(实际上是如何获取数组的所有子集?的答案,但不要告诉他们):
public static IEnumerable<IEnumerable<T>> Subsets(this IEnumerable<T> source)
{
    var first = source.FirstOrDefault();
    if (first == null) return new[] { Enumerable.Empty<T>() };

    var others = source.Skip(1).Subsets();
    return others.Concat(others.Select(s => s.Concat(new { first })));
}

一旦你有了Subsets方法,你可以按以下方式过滤结果,尽管性能仍然是数量级为亿万(或者如果你想挑剔的话,是2^n)。
var sets = items.Where(i => i.Number <= 500)
    .Subsets().Where(s => s.Sum(i => i.Number) == 500);

然而,如果您对“Number”有非负的限制,您可以将“Subsets”操作与搜索目标和结合起来。这意味着您需要定义:
public static IEnumerable<IEnumerable<T>> SubsetsAddingUpTo(this IEnumerable<T> source, int target)
{
    // This stopping condition ensures that you will not have to walk the rest of the tree when you have already hit or exceeded your target.
    // It assumes that the Number values are all non-negative.
    if (target < 0) return Enumerable.Empty<IEnumerable<T>>();

    var first = source.FirstOrDefault();
    if (first == null) return Enumerable.Empty<IEnumerable<T>>();

    var tail = source.Skip(1).Where(i => i.Number <= target).ToList();

    var othersIncludingFirst = tail.SubsetsAddingUpTo(target - first.Number);
    var othersExcludingFirst = tail.SubsetsAddingUpTo(target);

    return othersExcludingFirst.Concat(othersIncludingFirst.Select(s => s.Concat(new { first })));
}

因为方法内部会检查 "<= target",所以你不需要进行任何预过滤。但是,在搜索之前可以对数据进行排序,以按照日期层次顺序给出结果集。调用方式如下:
var sets = items.OrderByDescending(i => i.Date).SubsetsAddingUpTo(500);

这实际上应该给你相当不错的性能。最坏情况(每个项目的数字为0或1)不会很好(顺序2^n),但是如果大多数Number的值与您的目标总和具有类似的数量级,就像您的示例一样,那么停止条件将挺身而出,为您节省大量不必要的操作。

一种缓解这个问题的方法可能是在使用完DataTable后从中删除该行。这样,您就可以过滤和搜索剩余行数量逐渐减少的池。 - SimonGoldstone
1
我的 SubsetsAddingUpTo 方法基本上就是这样做的。即便如此,最坏情况仍然是 2^n - Rob Lyndon
如果Number的值有一个上限,那么停止准则可以扩展,因为你可能会到达链中的某个点,你知道你没有机会达到你的目标。 - Rob Lyndon
谢谢,Rob。我会尝试一下。 - Menno
我是不是做傻事了?在我的测试程序中,我遇到了以下错误:'System.Data.EnumerableRowCollection<System.Data.DataRow>' 不包含 'Subsets' 的定义,并且没有接受类型为 'System.Data.EnumerableRowCollection<System.Data.DataRow>' 的第一个参数的扩展方法 'Subsets' 可以找到(您是否缺少使用指令或程序集引用?) - Menno
你需要在自己的静态类中定义 SubsetsAddingUpTo。这是一个扩展方法。确保你已经引用了包含 SubsetsAddingUpTo 定义的命名空间。 - Rob Lyndon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接