【问题标题】:How to find matching records in data table如何在数据表中找到匹配的记录
【发布时间】:2013-10-31 16:00:14
【问题描述】:

我有一个包含两个列的数据表:一个数字和一个日期,例如

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) 提供了一个匹配,但是要编写这样的程序,我只能想到要遍历数据表无数次,直到找到正确的匹配。

有人有更聪明的主意吗?

【问题讨论】:

标签: c# datatable sum


【解决方案1】:

在最坏的情况下,您必须遍历数据集的所有 2^n 子集,但如果您的所有项目都是非负数,您可以从过滤 item.Number <= 500 开始。

这是一个可能的Subsets 方法(实际上是How to get all subsets of an array? 的答案,但不要告诉他们):

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 })));
}

因为&lt;= target 的检查发生在方法内部,所以您不必进行任何预过滤。但是,您可以在进行搜索之前执行排序,以按分层日期顺序为您提供集合。电话将是

var sets = items.OrderByDescending(i => i.Date).SubsetsAddingUpTo(500);

这实际上应该会给您带来不错的性能。最坏的情况(每个项目都有数字 0 或 1)不会很好(订单2^n),但如果Number 的大多数值与您的目标总和具有相似的数量级,如在您的示例中就是这种情况,那么停止条件将助您一臂之力,并为您节省大量不必要的操作。

【讨论】:

  • 缓解这种情况的一种方法可能是在使用后从 DataTable 中删除该行。这样,您就可以在不断减少的剩余行池中进行过滤和搜索。
  • 我的SubsetsAddingUpTo 方法基本上就是这样做的。即便如此,最坏的情况还是2^n
  • 如果Number 的值有一个上限,那么可以扩展停止标准,因为这样你就可以在链中找到一个你知道你没有机会的点击中你的目标。
  • 谢谢,罗伯。我试试看。
  • 我是不是装傻了?在我的测试程序中,我收到以下错误:“System.Data.EnumerableRowCollection”不包含“子集”的定义,并且没有扩展方法“子集”接受“System.Data”类型的第一个参数。可以找到 EnumerableRowCollection'(您是否缺少 using 指令或程序集引用?)
猜你喜欢
  • 2011-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多