简体   繁体   English

算法总结所有组合的数字列表

[英]algorithm to sum up a list of numbers for all combinations

I have a list of numbers and I want to add up all the different combinations. 我有一个数字列表,我想加起来所有不同的组合。 For example: 例如:

  • number as 1,4,7 and 13 数字为1,4,7和13
  • the output would be: 输出将是:


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

Is there a formula to calculate this with different numbers? 是否有一个公式来计算不同的数字?

A simple way to do this is to create a bit set with as much bits as there are numbers. 一种简单的方法是使用与数字一样多的位来创建位集。 In your example 4. 在你的例子中4。

Then count from 0001 to 1111 and sum each number that has a 1 on the set: 然后从0001到1111计数,并对集合上每个数字1的数字求和:

Numbers 1,4,7,13: 数字1,4,7,13:

0001 = 13=13
0010 = 7=7
0011 = 7+13 = 20

1111 = 1+4+7+13 = 25

Here's how a simple recursive solution would look like, in Java: 在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);
}

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

The best-known algorithm requires exponential time. 最着名的算法需要指数时间。 If there were a polynomial-time algorithm, then you would solve the subset sum problem , and thus the P=NP problem . 如果存在多项式时间算法,那么您将解决子集和问题 ,从而解决P = NP问题

The algorithm here is to create bitvector of length that is equal to the cardinality of your set of numbers. 这里的算法是创建长度等于你的数字基数的bitvector。 Fix an enumeration (n_i) of your set of numbers. 修复一组数字的枚举(n_i) Then, enumerate over all possible values of the bitvector. 然后,枚举位向量的所有可能值。 For each enumeration (e_i) of the bitvector, compute the sum of e_i * n_i . 对于位向量的每个枚举(e_i) ,计算e_i * n_i的总和。

The intuition here is that you are representing the subsets of your set of numbers by a bitvector and generating all possible subsets of the set of numbers. 这里的直觉是,您通过位向量表示数字集的子集,并生成该组数字的所有可能子集。 When bit e_i is equal to one, n_i is in the subset, otherwise it is not. 当位e_i等于1时, n_i在子集中,否则不在。

The fourth volume of Knuth's TAOCP provides algorithms for generating all possible values of the bitvector. Knuth的TAOCP的第四卷提供了用于生成位向量的所有可能值的算法。

C#: C#:

I was trying to find something more elegant - but this should do the trick for now... 我试图找到一些更优雅的东西 - 但是现在应该可以做到这一点......

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

Outputs as: 输出为:

[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

Here is a simple recursive Ruby implementation: 这是一个简单的递归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)

Which prints 哪个打印

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

If you do not need to print the array at each step, the code can be made even simpler and much faster because no additional arrays are created: 如果您不需要在每一步都打印数组,则可以使代码更简单,更快,因为不会创建其他数组:

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)

I dont think you can have it much simpler than that. 我不认为你可以比这简单得多。

This Perl program seems to do what you want. 这个Perl程序似乎可以做你想要的。 It goes through the different ways to choose n items from k items . 它通过不同的方式k项中选择n It's easy to calculate how many combinations there are, but getting the sums of each combination means you have to add them eventually. 计算有多少组合很容易,但是获得每个组合的总和意味着你必须最终添加它们。 I had a similar question on Perlmonks when I was asking How can I calculate the right combination of postage stamps? 当我问到Perlmonks时,我有一个类似的问题如何计算邮票的正确组合? .

The Math::Combinatorics module can also handle many other cases. Math :: Combinatorics模块还可以处理许多其他情况。 Even if you don't want to use it, the documentation has a lot of pointers to other information about the problem. 即使您不想使用它,文档也有很多关于该问题的其他信息的指针。 Other people might be able to suggest the appropriate library for the language you'd like to you. 其他人可能会为您喜欢的语言建议适当的库。

#!/usr/bin/perl

use List::Util qw(sum);
use Math::Combinatorics;

my @n = qw(1 4 7 13);

foreach my $count ( 2 .. @n ) {
    my $c = Math::Combinatorics->new(
        count => $count,  # number to choose
        data => [@n],
        );

    print "combinations of $count from: [" . join(" ",@n) . "]\n";

    while( my @combo = $c->next_combination ){
        print join( ' ', @combo ), " = ", sum( @combo ) , "\n";
        }
    }

Mathematica solution: Mathematica解决方案:

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

Output: 输出:

{}  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

You can enumerate all subsets using a bitvector. 您可以使用位向量枚举所有子集。

In a for loop, go from 0 to 2 to the Nth power minus 1 (or start with 1 if you don't care about the empty set). 在for循环中,从0到2到Nth幂减1(或者如果你不关心空集,则从1开始)。

On each iteration, determine which bits are set. 在每次迭代时,确定设置了哪些位。 The Nth bit represents the Nth element of the set. 第N位表示集合的第N个元素。 For each set bit, dereference the appropriate element of the set and add to an accumulated value. 对于每个设置位,取消引用该组的相应元素并添加到累计值。

ETA: Because the nature of this problem involves exponential complexity, there's a practical limit to size of the set you can enumerate on. ETA:因为这个问题的本质涉及指数复杂性,所以你可以枚举的集合的大小存在实际限制。 If it turns out you don't need all subsets, you can look up "n choose k" for ways of enumerating subsets of k elements. 如果事实证明您不需要所有子集,则可以查找“n选择k”以获取枚举k个元素的子集的方法。

PHP: Here's a non-recursive implementation. PHP:这是一个非递归实现。 I'm not saying this is the most efficient way to do it (this is indeed exponential 2^N - see JasonTrue's response and comments), but it works for a small set of elements. 我不是说这是最有效的方法(这确实是指数2 ^ N - 请参阅JasonTrue的回复和评论),但它适用于一小部分元素。 I just wanted to write something quick to obtain results. 我只想快速写一些东西来获得结果。 I based the algorithm off Toon's answer. 我将算法基于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]));
}

Which will output: 哪个会输出:

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

For example, a case for this could be a set of resistance bands for working out. 例如,这种情况可能是一组用于解决问题的阻力带。 Say you get 5 bands each having different resistances represented in pounds and you can combine bands to sum up the total resistance. 假设您获得5个波段,每个波段具有以磅为单位表示的不同阻力,您可以组合波段来总计总阻力。 The bands resistances are 3, 5, 8, 13 and 19 pounds. 带阻力为3,5,5,13和19磅。 This set gives you 32 (2^5) possible configurations, minus the zero. 此设置为您提供32(2 ^ 5)种可能的配置,减零。 In this example, the algorithm returns the data sorted by ascending total resistance favoring efficient band configurations first, and for each configuration the bands are sorted by descending resistance. 在该示例中,算法首先返回有利于有效频带配置的按升序总电阻排序的数据,并且对于每个配置,频带按降序电阻排序。

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)

its written in python. 它用python编写。 the idea is to break the list in a single integer and a list for eg. 我们的想法是在单个整数和列表中打破列表。 [1,2,3,4] into 1,[2,3,4]. [1,2,3,4]分为1,[2,3,4]。 we append the total sum now by adding the integer and sum of remaining list.also we take each individual sum ie 1,2;1,3;1,4. 我们现在通过添加整数和剩余列表的总和来追加总和。此外,我们采用每个单独的总和,即1,2; 1,3; 1,4。 checklist shall now be [1+2+3+4,1+2,1+3,1+4] then we call the new list recursively ie now int=2,list=[3,4]. 检查表现在应为[1 + 2 + 3 + 4,1 + 2,1 + 3,1 + 4]然后我们递归调用新列表,即现在int = 2,list = [3,4]。 checklist will now append [2+3+4,2+3,2+4] accordingly we append the checklist till list is empty. 清单现在将附加[2 + 3 + 4,2 + 3,2 + 4],因此我们附上清单直到清单为空。

set is the set of sums and list is the list of the original numbers. set是sums的集合,list是原始数字的列表。

Its Java. 它的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();
    }
}

This is not the code to generate the sums, but it generates the permutations. 这不是生成总和的代码,但它会生成排列。 In your case: 在你的情况下:

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

If I have a moment over the weekend, and if it's interesting, I can modify this to come up with the sums. 如果我有一个周末的时刻,如果它有趣,我可以修改它来得出总和。

It's just a fun chunk of LINQ code from Igor Ostrovsky's blog titled "7 tricks to simplify your programs with LINQ" ( http://igoro.com/archive/7-tricks-to-simplify-your-programs-with-linq/ ). 这只是来自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];
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");
        }
    }

Thanks Zach, 谢谢Zach,

I am creating a Bank Reconciliation solution. 我正在创建银行对帐解决方案。 I dropped your code into jsbin.com to do some quick testing and produced this in Javascript: 我将你的代码放到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;
}

I need it to produce a list of ID's to exclude from the next matching number. 我需要它来生成一个ID列表,以便从下一个匹配的数字中排除。

I will post back my final solution using vb.net 我将使用vb.net回发我的最终解决方案

You might be interested in checking out the GNU Scientific Library if you want to avoid maintenance costs. 如果您想避免维护成本,您可能有兴趣查看GNU Scientific Library。 The actual process of summing longer sequences will become very expensive (more-so than generating a single permutation on a step basis), most architectures have SIMD/vector instructions that can provide rather impressive speed-up (I would provide examples of such implementations but I cannot post URLs yet). 对较长序列求和的实际过程将变得非常昂贵(比在步骤基础上生成单个置换更多),大多数架构都具有可以提供相当令人印象深刻的加速的SIMD /向量指令(我将提供此类实现的示例但我还不能发布网址)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM