如何检查数字是否可以表示为某些给定数字的总和

时间:2012-12-11 16:57:15

标签: numbers sum

我有一些整数的列表,例如[1, 2, 3, 4, 5, 10] 我还有另一个整数(N)。例如,N = 19

我想检查我的整数是否可以表示为列表中任意数量的数字的总和:

19 = 10 + 5 + 4

19 = 10 + 4 + 3 + 2

列表中的每个数字只能使用一次。 N可以筹集2千或更多。列表的大小可以达到200个整数。

有解决这个问题的好方法吗?

3 个答案:

答案 0 :(得分:1)

蛮力方法需要生成2 ^(array_size)-1子集以进行求和并与目标N进行比较。

通过简单地将问题分成两部分,可以显着提高运行时间。以成套方式存储阵列的一半和另一半的所有可能总和。现在可以通过检查一组中的每个数字n来确定补充N - n是否存在于另一组中。

此优化将复杂性降低到大约:2^(array_size/2)-1+2^(array_size/2)-1=2^(array_size/2 + 1)-2 原来的一半。

这是一个使用这个想法的c ++实现。

#include <bits/stdc++.h>
using namespace std; 
bool sum_search(vector<int> myarray, int N) {
        //values for splitting the array in two
        int right=myarray.size()-1,middle=(myarray.size()-1)/2;

        set<int> all_possible_sums1,all_possible_sums2;

        //iterate over the first half of the array
        for(int i=0;i<middle;i++) {
            //buffer set that will hold new possible sums
            set<int> buffer_set;

            //every value currently in the set is used to make new possible sums
            for(set<int>::iterator set_iterator=all_possible_sums1.begin();set_iterator!=all_possible_sums1.end();set_iterator++) 
                buffer_set.insert(myarray[i]+*set_iterator);

            all_possible_sums1.insert(myarray[i]);

            //transfer buffer into the main set
            for(set<int>::iterator set_iterator=buffer_set.begin();set_iterator!=buffer_set.end();set_iterator++) 
                all_possible_sums1.insert(*set_iterator);
        } 

        //iterator over the second half of the array
        for(int i=middle;i<right+1;i++) {
            set<int> buffer_set;    

            for(set<int>::iterator set_iterator=all_possible_sums2.begin();set_iterator!=all_possible_sums2.end();set_iterator++)   
                buffer_set.insert(myarray[i]+*set_iterator);

            all_possible_sums2.insert(myarray[i]);

            for(set<int>::iterator set_iterator=buffer_set.begin();set_iterator!=buffer_set.end();set_iterator++) 
                all_possible_sums2.insert(*set_iterator);
        }

        //for every element in the first set, check if the the second set has the complemenent to make N
        for(set<int>::iterator set_iterator=all_possible_sums1.begin();set_iterator!=all_possible_sums1.end();set_iterator++)  
            if(all_possible_sums2.find(N-*set_iterator)!=all_possible_sums2.end()) 
                return true;                                       
        return false;
}

答案 1 :(得分:1)

4年半以后,这个问题是乔纳森answered。 我想在Python中发布两个实现(bruteforce和Jonathan)以及它们的性能比较。

def check_sum_bruteforce(numbers, n):
    # This bruteforce approach can be improved (for some cases) by
    # returning True as soon as the needed sum is found;

    sums = []

    for number in numbers:
        for sum_ in sums[:]:
            sums.append(sum_ + number)

        sums.append(number)

    return n in sums


def check_sum_optimized(numbers, n):
    sums1, sums2 = [], []
    numbers1 = numbers[:len(numbers) // 2]
    numbers2 = numbers[len(numbers) // 2:]

    for sums, numbers_ in ((sums1, numbers1), (sums2, numbers2)):
        for number in numbers_:
            for sum_ in sums[:]:
                sums.append(sum_ + number)

            sums.append(number)

    for sum_ in sums1:
        if n - sum_ in sums2:
            return True

    return False


assert check_sum_bruteforce([1, 2, 3, 4, 5, 10], 19)
assert check_sum_optimized([1, 2, 3, 4, 5, 10], 19)

import timeit

print(
    "Bruteforce approach (10000 times):",
    timeit.timeit(
        'check_sum_bruteforce([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 200)',
        number=10000,
        globals=globals()
    )
)

print(
    "Optimized approach by Jonathan (10000 times):",
    timeit.timeit(
        'check_sum_optimized([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 200)',
        number=10000,
        globals=globals()
    )
)

输出(浮点数是秒):

Bruteforce approach (10000 times): 1.830944365834205
Optimized approach by Jonathan (10000 times): 0.34162875449254027

答案 2 :(得分:0)

丑陋和暴力的方法:

 a = [1, 2, 3, 4, 5, 10]
 b = []
 a.size.times do |c|
    b << a.combination(c).select{|d| d.reduce(&:+) == 19 }
 end
 puts b.flatten(1).inspect