Finding all possible combinations of numbers to reach a given sum

前端 未结 30 3088
一个人的身影
一个人的身影 2020-11-21 06:39

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

30条回答
  •  悲哀的现实
    2020-11-21 07:20

    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.)

提交回复
热议问题