【问题标题】:algorithm to sum up a list of numbers for all combinations总结所有组合的数字列表的算法
【发布时间】:2010-09-29 01:35:19
【问题描述】:

我有一个数字列表,我想将所有不同的组合加起来。 例如:

  • 数字为 1,4,7 和 13
  • 输出将是:


1+4=5
1+7=8
1+13=14
4+7=11
4+13=17
7+13=20
1+4+7=12
1+4+13=18
1+7+13=21
4+7+13=24
1+4+7+13=25

有没有公式可以用不同的数字来计算?

【问题讨论】:

  • 这个 Project Euler 是否相关?
  • 正在编写 Kakuro 求解器(或创建者),是吗?

标签: algorithm


【解决方案1】:
public static void main(String[] args) {
        // this is an example number
        long number = 245L;
        int sum = 0;

        if (number > 0) {
            do {
                int last = (int) (number % 10);
                sum = (sum + last) % 9;
            } while ((number /= 10) > 0);
            System.err.println("s = " + (sum==0 ? 9:sum);
        } else {
            System.err.println("0");
        }
    }

【讨论】:

    【解决方案2】:

    set 是总和的集合,list 是原始数字的列表。

    它的 Java。

    public void subSums() {
        Set<Long> resultSet = new HashSet<Long>();
        for(long l: list) {
            for(long s: set) {
                resultSet.add(s);
                resultSet.add(l + s);
            }
            resultSet.add(l);
            set.addAll(resultSet);
            resultSet.clear();
        }
    }
    

    【讨论】:

      【解决方案3】:
      v=[1,2,3,4]#variables to sum
      i=0
      clis=[]#check list for solution excluding the variables itself
      def iterate(lis,a,b):
          global i
          global clis
          while len(b)!=0 and i<len(lis):
              a=lis[i]
              b=lis[i+1:]
              if len(b)>1:
                  t=a+sum(b)
                  clis.append(t)
              for j in b:
                  clis.append(a+j)
              i+=1
              iterate(lis,a,b)
      iterate(v,0,v)
      

      它是用python编写的。这个想法是将列表分解为一个整数和一个列表,例如。 [1,2,3,4] 转换为 1,[2,3,4]。我们现在通过添加剩余列表的整数和总和来追加总和。我们也采用每个单独的总和,即 1,2;1,3;1,4。 checklist 现在应该是 [1+2+3+4,1+2,​​1+3,1+4] 然后我们递归地调用新列表,即现在 int=2,list=[3,4]。清单现在将追加 [2+3+4,2+3,2+4] 因此我们追加清单直到列表为空。

      【讨论】:

      • 能否请您也发表一些解释。谢谢。
      • 它是用python编写的。这个想法是将列表分解为一个整数和一个列表,例如。 [1,2,3,4] 转换为 1,[2,3,4]。我们现在通过添加剩余列表的整数和总和来追加总和。我们也采用每个单独的总和,即 1,2;1,3;1,4。 checklist 现在应该是 [1+2+3+4,1+2,​​1+3,1+4] 然后我们递归地调用新列表,即现在 int=2,list=[3,4]。清单现在将追加 [2+3+4,2+3,2+4] 因此我们追加清单直到列表为空。如果需要,我可以将流程图表发送给您。
      • 嗨,Niraj,请在您的答案中更新此评论。这将对未来的用户有所帮助。
      【解决方案4】:

      PHP:这是一个非递归实现。我并不是说这是最有效的方法(这确实是指数 2^N - 请参阅 JasonTrue 的响应和 cmets),但它适用于一小部分元素。我只是想快速写一些东西来获得结果。我根据 Toon 的回答建立了算法。

      $set = array(3, 5, 8, 13, 19);
      
      $additions = array();
      for($i = 0; $i < pow(2, count($set)); $i++){
          $sum = 0;
          $addends = array();
          for($j = count($set)-1; $j >= 0; $j--) {
              if(pow(2, $j) & $i) {
                  $sum += $set[$j];
                  $addends[] = $set[$j];
              }
          }
          $additions[] = array($sum, $addends);
      }
      
      sort($additions);
      
      foreach($additions as $addition){
          printf("%d\t%s\n", $addition[0], implode('+', $addition[1]));
      }
      

      将输出:

      0   
      3   3
      5   5
      8   8
      8   5+3
      11  8+3
      13  13
      13  8+5
      16  13+3
      16  8+5+3
      18  13+5
      19  19
      21  13+8
      21  13+5+3
      22  19+3
      24  19+5
      24  13+8+3
      26  13+8+5
      27  19+8
      27  19+5+3
      29  13+8+5+3
      30  19+8+3
      32  19+13
      32  19+8+5
      35  19+13+3
      35  19+8+5+3
      37  19+13+5
      40  19+13+8
      40  19+13+5+3
      43  19+13+8+3
      45  19+13+8+5
      48  19+13+8+5+3
      

      例如,这样的情况可能是一组用于锻炼的阻力带。假设你有 5 个波段,每个波段都有不同的阻力,以磅为单位,你可以组合波段来总结总阻力。带阻力为 3、5、8、13 和 19 磅。这组为您提供了 32 (2^5) 种可能的配置,减去零。在此示例中,该算法首先返回按总阻力升序排序的数据,优先考虑有效的波段配置,并且对于每个配置,波段按阻力降序排序。

      【讨论】:

        【解决方案5】:

        这个 Perl 程序似乎可以做你想做的事。它通过不同的方式选择n items from k items。计算有多少组合很容易,但是获得每个组合的总和意味着您最终必须将它们相加。当我问How can I calculate the right combination of postage stamps? 时,我在Perlmonks 上也有类似的问题。

        Math::Combinatorics 模块还可以处理许多其他情况。即使您不想使用它,文档也有很多指向有关该问题的其他信息的指针。其他人可能会针对您想要的语言向您推荐合适的库。

        #!/usr/bin/perl 使用 List::Util qw(sum); 使用数学::组合学; 我的@n = qw(1 4 7 13); foreach 我的 $count ( 2 .. @n ) { 我的 $c = Math::Combinatorics->new( count => $count, # 要选择的数字 数据 => [@n], ); 打印“$count 的组合来自:[”。加入(“”,@n)。 "]\n"; 而(我的@combo = $c->next_combination){ 打印连接('',@combo),“=”,sum(@combo),“\n”; } }

        【讨论】:

          【解决方案6】:

          谢谢扎克,

          我正在创建银行对帐解决方案。我将您的代码放入 jsbin.com 以进行一些快速测试并用 Javascript 生成:

          function f(numbers,ids, index,  sum, output, outputid, find )
          {
              if (index == numbers.length){
                    var x ="";
                    if (find == sum) {
                        y= output + " } = " + sum + "  " + outputid + " }<br/>" ;
                    }
                  return;
              }
              f(numbers,ids, index + 1, sum + numbers[index], output + " " + numbers[index], outputid + " " + ids[index], find);
              f(numbers,ids, index + 1, sum, output, outputid,find);
          }
          
          var y;
          
          f( [1.2,4,7,13,45,325,23,245,78,432,1,2,6],[1,2,3,4,5,6,7,8,9,10,11,12,13], 0, 0, '{','{', 24.2);
          if (document.getElementById('hello')) {
            document.getElementById('hello').innerHTML = y;
          }
          

          我需要它来生成一个 ID 列表,以便从下一个匹配号码中排除。

          我将使用 vb.net 发回我的最终解决方案

          【讨论】:

            【解决方案7】:

            如果您想避免维护成本,您可能有兴趣查看 GNU 科学图书馆。对较长序列求和的实际过程将变得非常昂贵(比基于步骤生成单个排列更昂贵),大多数架构都有 SIMD/向量指令,可以提供相当令人印象深刻的加速(我会提供这种实现的示例,但我还不能发布网址)。

            【讨论】:

              【解决方案8】:

              这是一个简单的递归 Ruby 实现:

              a = [1, 4, 7, 13]
              
              def add(current, ary, idx, sum)
                  (idx...ary.length).each do |i|
                      add(current + [ary[i]], ary, i+1, sum + ary[i])
                  end
                  puts "#{current.join('+')} = #{sum}" if current.size > 1
              end    
              add([], a, 0, 0)
              

              打印出来的

              1+4+7+13 = 25
              1+4+7 = 12
              1+4+13 = 18
              1+4 = 5
              1+7+13 = 21
              1+7 = 8
              1+13 = 14
              4+7+13 = 24
              4+7 = 11
              4+13 = 17
              7+13 = 20
              

              如果您不需要在每一步都打印数组,代码可以变得更加简单和快速,因为不会创建额外的数组:

              def add(ary, idx, sum)
                  (idx...ary.length).each do |i|
                      add(ary, i+1, sum + ary[i])
                  end
                  puts sum
              end
              add(a, 0, 0)
              

              我认为你不能有比这更简单的了。

              【讨论】:

                【解决方案9】:

                这是一个简单的递归解决方案在 Java 中的样子:

                public static void main(String[] args)
                {
                    f(new int[] {1,4,7,13}, 0, 0, "{");
                }
                
                static void f(int[] numbers, int index, int sum, String output)
                {
                    if (index == numbers.length)
                    {
                        System.out.println(output + " } = " + sum);
                        return;
                    }
                
                    // include numbers[index]
                    f(numbers, index + 1, sum + numbers[index], output + " " + numbers[index]);
                
                    // exclude numbers[index]
                    f(numbers, index + 1, sum, output);
                }
                

                输出:

                { 1 4 7 13 } = 25
                { 1 4 7 } = 12
                { 1 4 13 } = 18
                { 1 4 } = 5
                { 1 7 13 } = 21
                { 1 7 } = 8
                { 1 13 } = 14
                { 1 } = 1
                { 4 7 13 } = 24
                { 4 7 } = 11
                { 4 13 } = 17
                { 4 } = 4
                { 7 13 } = 20
                { 7 } = 7
                { 13 } = 13
                { } = 0
                

                【讨论】:

                  【解决方案10】:

                  数学解法:

                  {#, Total@#}& /@ Subsets[{1, 4, 7, 13}] //MatrixForm

                  输出:

                  {}  0
                  {1} 1
                  {4} 4
                  {7} 7
                  {13}    13
                  {1,4}   5
                  {1,7}   8
                  {1,13}  14
                  {4,7}   11
                  {4,13}  17
                  {7,13}  20
                  {1,4,7} 12
                  {1,4,13}    18
                  {1,7,13}    21
                  {4,7,13}    24
                  {1,4,7,13}  25
                  

                  【讨论】:

                    【解决方案11】:

                    C#:

                    我试图找到更优雅的东西 - 但现在这应该可以解决问题......

                    //Set up our array of integers
                    int[] items = { 1, 3, 5, 7 };
                    
                    //Figure out how many bitmasks we need... 
                    //4 bits have a maximum value of 15, so we need 15 masks.
                    //Calculated as:
                    //    (2 ^ ItemCount) - 1
                    int len = items.Length;
                    int calcs = (int)Math.Pow(2, len) - 1;
                    
                    //Create our array of bitmasks... each item in the array
                    //represents a unique combination from our items array
                    string[] masks = Enumerable.Range(1, calcs).Select(i => Convert.ToString(i, 2).PadLeft(len, '0')).ToArray();
                    
                    //Spit out the corresponding calculation for each bitmask
                    foreach (string m in masks)
                    {
                        //Get the items from our array that correspond to 
                        //the on bits in our mask
                        int[] incl = items.Where((c, i) => m[i] == '1').ToArray();
                    
                        //Write out our mask, calculation and resulting sum
                        Console.WriteLine(
                            "[{0}] {1}={2}", 
                            m, 
                            String.Join("+", incl.Select(c => c.ToString()).ToArray()), 
                            incl.Sum()
                        );
                    }
                    

                    输出为:

                    [0001] 7=7
                    [0010] 5=5
                    [0011] 5+7=12
                    [0100] 3=3
                    [0101] 3+7=10
                    [0110] 3+5=8
                    [0111] 3+5+7=15
                    [1000] 1=1
                    [1001] 1+7=8
                    [1010] 1+5=6
                    [1011] 1+5+7=13
                    [1100] 1+3=4
                    [1101] 1+3+7=11
                    [1110] 1+3+5=9
                    [1111] 1+3+5+7=16
                    

                    【讨论】:

                      【解决方案12】:

                      您可以使用位向量枚举所有子集。

                      在 for 循环中,从 0 到 2 的 N 次方减 1(如果您不关心空集,则从 1 开始)。

                      在每次迭代中,确定设置了哪些位。第 N 位表示集合的第 N 个元素。对于每个集合位,取消引用集合的适当元素并添加到累积值。

                      ETA:由于此问题的性质涉及指数级复杂性,因此您可以枚举的集合的大小存在实际限制。如果事实证明您不需要所有子集,您可以查找“n 选择 k”以了解枚举 k 个元素的子集的方法。

                      【讨论】:

                      • 指数复杂度。不是多项式。
                      • 2^N 变大的速度非常快,很快就会超过可以用大多数编程语言的标准数据类型之一表示的最大整数的大小。因此,对于较大的 N,必须使用另一种算法来生成所有位向量。
                      • Rec:抱歉,是的,已更正。虽然它是二项式... Jason:BitVector 可以是任意长度,但如何实现任意长度可能取决于您的语言。如果与位向量的接口足够抽象,则子集算法不需要针对更大的位向量进行更改
                      • 如果我误读了您的帖子,我深表歉意,但您声明使用从 0 到 2^N - 1 的 for 循环来生成位向量。我是说对于非常大的,2^N 将超过大多数编程语言中最大整数数据类型(例如 C# 中的 Int64)的大小。
                      • 是的,但同样,BitVector 与实现无关。算法是一样的。您可能需要在大多数(非口齿不清的)语言中使用 LargeNumber 类。对于 2^64,您实际上有太多子集无法枚举。
                      【解决方案13】:

                      这不是生成和的代码,而是生成排列的代码。在你的情况下:

                      1; 1,4; 1,7; 4,7; 1,4,7; ...

                      如果我周末有空,如果有趣,我可以修改它以得出总和。

                      这只是 Igor Ostrovsky 博客中的一段有趣的 LINQ 代码,标题为“使用 LINQ 简化程序的 7 个技巧”(http://igoro.com/archive/7-tricks-to-simplify-your-programs-with-linq/)。

                      T[] arr = …;
                      var subsets = from m in Enumerable.Range(0, 1 << arr.Length)
                                    select
                                        from i in Enumerable.Range(0, arr.Length)
                                        where (m & (1 << i)) != 0
                                        select arr[i];
                      

                      【讨论】:

                        【解决方案14】:

                        最著名的算法需要指数时间。如果有多项式时间算法,那么您将求解subset sum problem,从而求解P=NP problem

                        这里的算法是创建长度等于您的一组数字的基数的位向量。修复您的一组数字的枚举(n_i)。然后,枚举位向量的所有可能值。对于位向量的每个枚举(e_i),计算e_i * n_i的总和。

                        这里的直觉是,您用位向量表示数字集的子集,并生成数字集的所有可能子集。当位e_i等于1时,n_i在子集中,否则不在。

                        Knuth 的 TAOCP 第四卷提供了生成位向量所有可能值的算法。

                        【讨论】:

                        • 我认为他想输出每个总和。如果是这种情况,那么问题肯定是非多项式的,因为输出相对于输入是指数的,因此它不属于 P=NP。
                        【解决方案15】:

                        执行此操作的一个简单方法是创建一个位集,其中包含与数字一样多的位。 在您的示例 4 中。

                        然后从 0001 数到 1111 并将集合中每个具有 1 的数字相加:

                        数字 1、4、7、13:

                        0001 = 13=13
                        0010 = 7=7
                        0011 = 7+13 = 20
                        
                        1111 = 1+4+7+13 = 25
                        

                        【讨论】:

                        • 好主意,请注意,如果您想不遗漏一个成员的金额,并且没有成员的金额(例如在您发布的问题中),您必须将其遗漏( 2 和 0 的幂),即如果您不想要无总和,则省略 0000,如果您不想要只有 1 的总和,则省略 0001、0010、0100、1000。
                        • 能不能解释一下或者给个链接
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-04-03
                        • 2013-10-06
                        • 1970-01-01
                        • 2015-03-16
                        • 1970-01-01
                        • 2021-08-21
                        • 1970-01-01
                        相关资源
                        最近更新 更多