【问题标题】:how to find items in list that their sum is less than or equal a number如何在列表中查找它们的总和小于或等于数字的项目
【发布时间】:2015-08-13 19:54:52
【问题描述】:

我有一个数字列表,我想知道列表中哪个数字组合与特定数字(目标)的总和最接近。(如果有很多组合,找到一个就足够了)

我搜索了很多,知道这里有很多解决方案可以找到它们的总和等于特定数字的组合,但是没有找到与该数字接近的最大的解决方案。

我还写了一段代码,从零循环到“目标”以找到最大的和,但由于这个过程非常耗时,必须针对 100,000 个列表进行计算,我想知道是否有更有效的方法使用最好是linq。

var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;

int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);

for (int UserNo = 1; UserNo <= MaxViewers; UserNo++)
{
    for (int No = 1; No <= target; No++)
    {
        var matches = from subset in MyExtensions.SubSetsOf(List1)
                        where subset.Sum() == target
                        select subset;
    }
}

public static class MyExtensions
{
    public static IEnumerable<IEnumerable<T>> SubSetsOf<T>(this IEnumerable<T> source)
    {
        if (!source.Any())
            return Enumerable.Repeat(Enumerable.Empty<T>(), 1);

        // Grab the first element off of the list
        var element = source.Take(1);

        // Recurse, to get all subsets of the source, ignoring the first item
        var haveNots = SubSetsOf(source.Skip(1));

        // Get all those subsets and add the element we removed to them
        var haves = haveNots.Select(set => element.Concat(set));

        // Finally combine the subsets that didn't include the first item, with those that did.
        return haves.Concat(haveNots);
    }

}

【问题讨论】:

  • 您是否考虑过将MaxBySum 结合使用?
  • @testWell,感谢您的评论,请您提供示例源代码
  • @Ramil89,感谢您的链接。不幸的是,这个算法对于 100,000 个列表非常耗时,我正在寻找更有效的解决方案。
  • 当您说find the greatest close to that number 时,您的意思是要返回在目标的某个范围 内但实际上不是目标的多个匹配项?如果是这样,您可以将您的where 语句更改为更像where subset.Sum() &gt; target - 5 &amp;&amp; subset.Sum() &lt; target,它将返回目标5 以内的所有子集。这将为您提供所有密切的可能性。

标签: c# performance linq


【解决方案1】:

您好,让我们来看看实现这一点的巧妙方法,

var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;

int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);

var closestSubSet = MyExtensions.SubSetsOf(List1)
.Select(o=> new{ SubSet = o, Sum = o.Sum(x=> x)})
.Select(o=> new{ SubSet = o.SubSet, Sum = o.Sum, FromTarget = (target - o.Sum >= 0 ? target - o.Sum : (target - o.Sum) * -1  ) })
.OrderBy(o=> o.FromTarget).FirstOrDefault();

我知道出于某些性能原因(不是多次调用 Sum,而是使用它),我选择了第二个选择很棘手。这应该找到与您指定的目标最接近的总和 ^^ 玩得开心


优化:

var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 }; 变量目标 = 40;

int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
int minClosestTargetRequired = 0;
int closestSum = int.maxValue;
int closestSetIndex = -1;
var subsets = MyExtensions.SubSetsOf(List1);
for(var c = 0 ; c < subsets.Count() ; c++)
{
  int currentSum = subsets[c].Sum(o=> o);
  if(currentSum < closestSum)
  {
    closestSum = currentSum;
    closestSetIndex = c;
  }
  if(closestSum <= minClosestTargetRequired)
    break;
}
Console.WriteLine("Closest Sum Is {0} In Set Index {1},closestSum,closestSetIndex);

【讨论】:

  • 我非常感谢您的智能解决方案,但不幸的是,我担心使用我的解决方案减少遍历 100,000 个列表所需的时间是 15 秒,而上述解决方案大约慢了 10 倍。您对优化代码有什么建议吗?
  • 嗯...我知道我没有意识到这样的列表太多,我将使用优化的解决方案编辑我的答案
  • 我得到错误(不能将索引应用到带有 [] 的表达式到 > 类型的表达式):int currentSum = subsets[c].Sum( o=> o); .请指教
  • 我使用 int currentSum = subsets.ElementAt(c).Sum(o => o); 修复了它但不幸的是,与具有 40 个循环的原始节点相比,它为 100,000 个节点的每个元素循环 100 次,现在它变慢了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
  • 2013-07-16
  • 2014-12-20
  • 2017-11-09
  • 2017-10-07
  • 2022-11-03
  • 1970-01-01
相关资源
最近更新 更多