0

Given a list of numbers, find the first combination of numbers that adds up to a certain sum.

Example:

Given list: [1, 2, 3, 4, 5] Given sum: 5 Response: [1, 4] the response can also be [2, 3], it doesn't matter. What matters is that we get a combination of numbers from the given list that adds up to the given sum as fast as possible.

I tried doing this with itertools.combinations in python, but it takes way too long:

from typing import List
import itertools

def test(target_sum, numbers):
    for i in range(len(numbers), 0, -1):
        for seq in itertools.combinations(numbers, i):
            if(sum(seq) == target_sum):
                return seq


if __name__ == "__main__":
    target_sum: int = 616
    numbers: List[int] = [16, 96, 16, 32, 16, 4, 4, 32, 32, 10, 16, 8, 32, 8, 4, 16, 8, 8, 8, 16, 8, 8, 8, 16, 8, 16, 16, 4, 8, 8, 16, 12, 16, 16, 8, 16, 8, 8, 8, 8, 4, 32, 16, 8, 32, 16, 8, 8, 8, 8, 16, 32, 8, 32, 8, 8, 16, 24, 32, 8]

    print(test(target_sum, numbers))
David
  • 624
  • 1
  • 6
  • 21
  • 3
    this is subset sum problem. You can solve it with dynamic programming (if sum value is reasonable) - just make table of size sum+1 and fill cells with possible sums – MBo Nov 11 '22 at 10:44
  • @MBo post your solution if you have one, I tried following this https://stackoverflow.com/questions/34517540/find-all-combinations-of-a-list-of-numbers-with-a-given-sum but it still takes a very long time, it's O^2 – David Nov 11 '22 at 10:45
  • Combinatorics! ;( ..and in worse case, you can't sum up at all (e.g. `target_sum=101`)) ..and there are a lot (1-n) combinations of 60 elements: sum(binom(i,60), i=1..60) – xerx593 Nov 11 '22 at 11:02
  • ... = `pow(2, 60)`^^ – xerx593 Nov 11 '22 at 11:18
  • https://stackoverflow.com/a/64380474/585411 solved this and should be fast enough. If you want, it produces all solutions as well. – btilly Nov 11 '22 at 19:20

1 Answers1

1
def subsum(tsum, numbers):
    a = [0]*(tsum+1)
    a[0] = -1
    for x in numbers:
        for i in range(tsum, x-1,-1):     #reverse to avoid reusing the same item
            if a[i-x] and a[i] == 0:        #we can form sum i with item x and existing sum i-x
                a[i] = x      #remember the last item for given sum
    res = []
    idx = tsum
    while idx:
        res.append(a[idx])
        idx -= a[idx]
    return res

print(subsum(21, [2,3,5,7,11]))

>>>[11, 7, 3]

When the last cell is nonzero, combination does exist, and we can retrieve items.

Complexity is O(target_sum*len(numbers))

MBo
  • 77,366
  • 5
  • 53
  • 86
  • It doesn't workt iwth the set of inputs I have given, i get this list: [8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]. There are not even that many 8's inside that list – David Nov 11 '22 at 12:28
  • 1
    @David missed and a[i]==0 – MBo Nov 11 '22 at 13:12