Is there an efficient way to find all integer tuples of length 10 that sum to 100

Question:

I am trying to find all tuples containing only positive integers (0 allowed) that add to 100. I have some constraints on the max values for each index. I have the list maxs that include these constraints

maxs = [3, 9, 14, 21, 21, 35, 31, 49, 42, 38]

So far I am simply running this 10 dimensional nested for loop

combinations = []
for s1 in range(maxs[0]+1):
    for s2 in range(maxs[1]+1):
        for s3 in range(maxs[2]+1):
            for s4 in range(maxs[3]+1):
                for s5 in range(maxs[4]+1):
                    for s6 in range(maxs[5]+1):
                        for s7 in range(maxs[6]+1):
                            for s8 in range(maxs[7]+1):
                                for s9 in range(maxs[8]+1):
                                    for s10 in range(maxs[9]+1):
                                        if s1 +s2+s3+s4+s5+s6+s7+s8+s9+s10 == 100:
                                            combinations.append([s1,s2,s3,s4,s5,s6,s7,s8,s9,s10]

I know there are stictly less than 10^11 combinations if that helps at all. Maybe this problem is too large

Thanks

Asked By: oskar szarowicz

||

Answers:

This one is not the best performant one, but it is the easiest.

from itertools import combinations

maxs = [3, 9, 14, 21, 21, 35, 31, 49, 42, 38]

combination = [
    tuple(filter(lambda x: 0 <= x <= 100, item))
    for r in range(1, len(maxs))
    for item in combinations(maxs, r)
]
result = [item for item in combination if sum(item) == 100]

print(result)
Answered By: Igor Dragushhak

Recursive approach. Example uses only three first entries. Acceleration is possible with early bad branch cutting – for example, when cursum exceeds target

maxs = [3, 9, 14, 21, 21, 35, 31, 49, 42, 38]

def sum100(n, target, maxs, level, cursum, curlist):
    if level == n:
        if cursum == target:
            print(curlist)
        return
    for i in range(maxs[level]+1):
        sum100(n, target, maxs, level + 1, cursum + i, curlist + [i])

sum100(3, 15, maxs, 0, 0, [])

[0, 1, 14]
[0, 2, 13]
...
[1, 3, 11]
[1, 4, 10]
[1, 5, 9]
...
[3, 1, 11]
[3, 2, 10]
[3, 3, 9]
Answered By: MBo

I know that there is an accepted solution.

But here is one that allows you to solve a variety of problems like this. It allows you to count solutions, produce solutions in lexicographic order, or produce any random solution. It does that by using dynamic programming to build a structure for all solutions, from which you can do with it as you want.

Here is a demonstration:

import random

def summer (target, choices_list, i=0, cache=None):
    if cache is None:
        cache = {}
    key = (target, i)
    if key not in cache:
        if i == len(choices_list):
            if target == 0:
                cache[key] = {0: [1, None]}
            else:
                cache[key] = None
        elif len(choices_list) < i:
            cache[key] = None
        else:
            answer = {}
            for choice in choices_list[i]:
                next_target = target - choice
                next_summer = summer(next_target, choices_list, i+1, cache)
                if next_summer is not None:
                    count = sum([v[0] for v in next_summer.values()])
                    answer[choice] = [count, next_summer]
            if 0 == len(answer.keys()):
                cache[key] = None
            else:
                cache[key] = answer

    return cache[key]

def max_summer(target, maxes):
    return summer(target, [list(range(0, m+1)) for m in maxes])

def count_solutions(this_summer):
    if this_summer is None:
        return 0
    else:
        answer = 0
        for v in this_summer.values():
            answer += v[0]
        return answer

def solution (this_summer, i, answer = None):
    if answer is None:
        answer = []

    if this_summer is None:
        answer.pop() # Remove trailing 0
        return answer

    for choice, info in this_summer.items():
        if info[0] < i:
            i -= info[0]
        else:
            answer.append(choice)
            return solution(info[1], i, answer)

    return None


s = max_summer(100, [3, 9, 14, 21, 21, 35, 31, 49, 42, 38])
print(count_solutions(s))

for x in range(10):
    i = x
    print(i, solution(s, i))

for x in range(10):
    i = random.randrange(0, count_solutions(s))
    print(i, solution(s, i))
Answered By: btilly