从列表中选择项目以获得总和

时间:2010-09-20 10:42:10

标签: c# .net algorithm list combinations

我有一个包含数值的项目列表,我需要使用这些项目来获得总和。我需要你的帮助来构建这样的算法。下面是一个描述我的问题的示例,用C#编写:

int sum = 21;

List<Item> list = new List<Item>();
list.Add(new Item() { Id = Guid.NewGuid(), Value = 3 });
list.Add(new Item() { Id = Guid.NewGuid(), Value = 5 });
list.Add(new Item() { Id = Guid.NewGuid(), Value = 12 });
list.Add(new Item() { Id = Guid.NewGuid(), Value = 3 });
list.Add(new Item() { Id = Guid.NewGuid(), Value = 2 });
list.Add(new Item() { Id = Guid.NewGuid(), Value = 7 });

List<Item> result = // the items in the list that has the defined sum.

注意:我对结果中的项目数没有限制。

4 个答案:

答案 0 :(得分:7)

这被称为 Subset sum problem ,被认为是计算机科学中的一个难题。不难做,但很难做到 - 你可以轻松编写算法来做到这一点,但对于相当大的输入,它将很容易花费数十亿年。

如果您对一个只能用于小输入的缓慢解决方案感到满意,请尝试以下方法:

  • 生成输入列表的所有子集。

  • 对于每个子集,计算该子集中项目的总和。

  • 返回总和匹配的第一个子集。

这是一个返回所有子集的方法(实际上是子序列,因为它维护了项目的顺序,尽管在你的情况下这没有区别):

/// <summary>
/// Returns all subsequences of the input <see cref="IEnumerable&lt;T&gt;"/>.
/// </summary>
/// <param name="source">The sequence of items to generate
/// subsequences of.</param>
/// <returns>A collection containing all subsequences of the input
/// <see cref="IEnumerable&lt;T&gt;"/>.</returns>
public static IEnumerable<IEnumerable<T>> Subsequences<T>(
        this IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentNullException("source");
    // Ensure that the source IEnumerable is evaluated only once
    return subsequences(source.ToArray());
}

private static IEnumerable<IEnumerable<T>> subsequences<T>(IEnumerable<T> source)
{
    if (source.Any())
    {
        foreach (var comb in subsequences(source.Skip(1)))
        {
            yield return comb;
            yield return source.Take(1).Concat(comb);
        }
    }
    else
    {
        yield return Enumerable.Empty<T>();
    }
}

所以你现在可以写这样的东西......

var result = list.Subsequences()
                 .FirstOrDefault(ss => ss.Sum(item => item.Value) == sum);

答案 1 :(得分:2)

这称为子集求和问题,通过修改 - 您不希望得到零,而是达到特定数字。

以下是Wiki对此的评论 - http://en.wikipedia.org/wiki/Subset_sum_problem

根据您对域名的了解,您可能会想到一些优化。例如,如果最高数字+最低数字大于总数 - >永远不会使用最高的数字,你可以将其排除(并尝试新的最高数字......)。

我记得按照塞缪尔的建议去做 - 这是递归的方式,这并不是那么糟糕(但当然总有堆栈溢出问题......)。

答案 2 :(得分:1)

递归,添加元素直到A)你实现总和或B)你得到太多,如果你已经完成,如果你改变了尝试所有可能配置的元素。如果当前元素已经大于超过总和的最后一个元素

,则可能禁止系统添加元素

答案 3 :(得分:-1)

我不确定你在这之后是哪个太阳。如果要将所有值的总和组合在一起,请使用以下代码:

int result = list.Sum( i => i.Value);

如果您想要所有具有特定值的元素,请使用以下代码:

int x = 3;
List<Item> result = list.Where( i => i.Value == x);