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