English 中文(简体)
将一个数字列表的所有组合相加的算法
原标题:
  • 时间:2008-12-31 19:34:31
  •  标签:

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
  • 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

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

问题回答

A simple way to do this is to create a bit set with as much bits as there are numbers. In your example 4.

然后从0001数到1111并求出在这个集合中有1的每个数字的总和:

数字1、4、7、13:

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

1111 = 1+4+7+13 = 25

这里是一个简单的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

最著名的算法需要指数时间。如果存在一个多项式时间的算法,那么你就能解决子集和问题,并因此解决P=NP问题。

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

这里的直觉是,将数字集合的子集表示为比特向量,并生成数字集合的所有可能子集。 当比特 e_i 等于一时, n_i 在子集中,否则不在。

《计算机程序设计艺术》第四卷提供了生成位向量的所有可能值的算法。

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

这是一个简单的递归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)

哪些印花? (Nǎxiē yìnhuā?)

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)

我认为你无法比那更简单了。

数学软件解决方案:

{#, 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

这个Perl程序似乎可以达到您想要的效果。它通过不同的方式选择n个项目中的k个项目。计算有多少种组合很容易,但是获得每个组合的总和意味着您最终必须将它们相加。当我询问如何计算正确的邮票组合时,我在Perlmonks上有一个类似的问题。

Math::Combinatorics模块还可以处理许多其他情况。 即使您不想使用它,文档中也有很多指向有关问题的其他信息的指针。 其他人可能能够建议您所需语言的适当库。

#!/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) . "]
";

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

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

在一个循环中,从0到2 N次方减1(或者如果不关心空集,可以从1开始)。

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

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

PHP:这里是一种非递归实现。我不是说这是最有效的方法(实际上是指数级的2^N - 请参见JasonTrue的回答和评论),但它适用于一小组元素。我只想快速编写一些代码以获得结果。我基于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	%s
", $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)种可能的配置(不包括零)。在这个例子中,算法按总阻力升序返回数据,优先选择高效的带配置,对于每个配置,带子按阻力降序排列。

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

1;1.4;1.7;4.7;1.4.7;... (The translation is already in Chinese characters. This is how you would write it.)

如果我周末有空闲时间,并且觉得有趣,我可以修改它以得出总和。

这只是来自Igor Ostrovsky博客中名为“7个使用LINQ简化您的程序技巧”的有趣的LINQ代码(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];

如果你想避免维护成本,你可能会对查看GNU科学库感兴趣。实际上,对更长的序列求和的过程将变得非常昂贵(比逐步生成单个排列更昂贵)。大多数体系结构都有SIMD/向量指令,可以提供相当惊人的加速(我会提供这些实现的示例,但我还不能发布URL)。

谢谢Zach,

我正在创建一个银行对账解决方案。我将您的代码放入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清单。 (Note: As an AI language model, I am not sure what "ID" refers to, so I just directly translate it to "ID". If it should be translated to a different term in a specific context, please let me know.)

我会用VB.NET回复我的最终解决方案

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。清单现在应该是[1+2+3+4,1+2,1+3,1+4],然后我们递归调用新列表,即现在int = 2,list = [3,4]。检查表现在将附加[2+3+4,2+3,2+4],因此我们附加检查表格,直到列表为空。

集合是和的集合,列表是原始数字的列表。 (Jíhé shì hé de jíhé, liànbǐao shì yuánshǐ shùzì de liànbǐao.)

它是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();
    }
}
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");
        }
    }




相关问题
热门标签