How would you go about testing all possible combinations of additions from a given set N
of numbers so they add up to a given final number?
A brief exam
There are a lot of solutions so far, but all are of the form generate then filter. Which means that they potentially spend a lot of time working on recursive paths that do not lead to a solution.
Here is a solution that is O(size_of_array * (number_of_sums + number_of_solutions))
. In other words it uses dynamic programming to avoid enumerating possible solutions that will never match.
For giggles and grins I made this work with numbers that are both positive and negative, and made it an iterator. It will work for Python 2.3+.
def subset_sum_iter(array, target):
sign = 1
array = sorted(array)
if target < 0:
array = reversed(array)
sign = -1
last_index = {0: [-1]}
for i in range(len(array)):
for s in list(last_index.keys()):
new_s = s + array[i]
if 0 < (new_s - target) * sign:
pass # Cannot lead to target
elif new_s in last_index:
last_index[new_s].append(i)
else:
last_index[new_s] = [i]
# Now yield up the answers.
def recur (new_target, max_i):
for i in last_index[new_target]:
if i == -1:
yield [] # Empty sum.
elif max_i <= i:
break # Not our solution.
else:
for answer in recur(new_target - array[i], i):
answer.append(array[i])
yield answer
for answer in recur(target, len(array)):
yield answer
And here is an example of it being used with an array and target where the filtering approach used in other solutions would effectively never finish.
def is_prime (n):
for i in range(2, n):
if 0 == n%i:
return False
elif n < i*i:
return True
if n == 2:
return True
else:
return False
def primes (limit):
n = 2
while True:
if is_prime(n):
yield(n)
n = n+1
if limit < n:
break
for answer in subset_sum_iter(primes(1000), 76000):
print(answer)
This prints all 522 answers in under 2 seconds. The previous approaches would be lucky to find any answers in the current lifetime of the universe. (The full space has 2^168 = 3.74144419156711e+50
possible combinations to run through. That...takes a while.)