【问题标题】:Non-recursive algorithm for determine all sums possible from set of numbers从一组数字中确定所有可能的和的非递归算法
【发布时间】:2015-05-01 18:39:42
【问题描述】:

我正在寻找一种非递归算法(最好在 C# 中),它将从一组正数中生成所有可能和的列表。

例如对于一组三个数字“1,2,3”,可能有以下七个和:

1

2

3

1+2=3

1+3=4

2+3=5

1+2+3=6

最大集合大小约为 50。我知道如何递归处理此问题,但过去在处理类似问题时受到调用堆栈的限制,所以这次想避免它。

【问题讨论】:

  • “过早的优化是万恶之源”
  • 在最坏的情况下,有2 ^ 50 不同的总和。或者是否有任何额外的限制可以减少它们的数量?顺便说一句,在递归版本中,调用深度最多为50。我认为这不是问题。
  • @kraskevich 不只是在最坏的情况下。问题中的示例表明,产生相同总和的不同组合应该为每个组合单独计算,因此给定 N 数字,您将总是得到 2^N 总和。
  • 这是一个很好的观点。在这个特定的例子中实际上只有六个总和

标签: c# algorithm math recursion set


【解决方案1】:

如果您只需要所有可能的总和,那么您可以使用此功能。

public static IEnumerable<int> GetSums(List<int> list)
{
    return from m in Enumerable.Range(0, 1 << list.Count)
           select
               (from i in Enumerable.Range(0, list.Count)
               where (m & (1 << i)) != 0
               select list[i]).Sum();
}

然后,就这样称呼它:

var result = GetSums(myList).ToList();

其他信息

您也可以使用此方法生成组合(source)

public static IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
    return from m in Enumerable.Range(0, 1 << list.Count)
           select
               from i in Enumerable.Range(0, list.Count)
               where (m & (1 << i)) != 0
               select list[i];
}

并借助Sum() 方法从System.Linq 命名空间找到所有组合的总和:

var result = GetPowerSet(myList).Select(x => x.Sum()).ToList();

【讨论】:

  • 你的第一个函数很好用。谢谢!仅供参考,我还发现我可以使用 var result = GetSums(myList).ToList().Distinct().ToList(); 轻松删除重复项;我还必须删除列表中的第一个条目,因为它为零。
  • @Caustix 很高兴为您提供帮助。是的,但您不需要拨打ToList() 两次。您可以使用GetSums(myList).Distinct().ToList(); 获取唯一值。
【解决方案2】:

子集的和与子集直接对应,子集也与二进制序列直接对应。如果您的集合中有五个项目,您希望迭代从 00000 到 11111 的所有位序列。等效地,您希望从 0 迭代到 2^5-1。如果某个位设置为 1,则应在总和中包含该值。所以,是这样的:

for i = 0 to 2^n-1
  sum = 0
  for j = 0 to n - 1
    if i & (1 << j) then 
      sum += items[j]
  yield return sum

显然,这是伪代码,不处理大于 i 使用的位数的 n 值,但这将是一个长时间的迭代。这至少应该让你开始。

【讨论】:

    【解决方案3】:

    如果所有数字的总和受限于可靠值,则存在复杂度 O(N * MaxSum) 的 DP 解,否则存在 O(2^N) 个可能的总和。

    DP 解决方案(Delphi):

    procedure GenerateAllSums(const A: array of Integer);
    var
      ASums: array of Boolean;
      S, i, j: Integer;
    begin
      //find maximal possible sum
      S := 0;
      for i := 0 to High(A) do
        S := S + A[i];
      //make array for possible sums
      SetLength(ASums, S + 1);
      ASums[0] := True; // all others - false
    
      for i := 0 to High(A) do
        for j := S - A[i] downto 0 do
          if ASums[j] then
            ASums[j + A[i]] := True;
      //Now 'True' elements of ASums denote possible sum values
    end;
    

    【讨论】:

      猜你喜欢
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-19
      • 2014-09-07
      相关资源
      最近更新 更多