Thank you @Jason Orendorff - I further developed your function, because I needed a few more options.
It`s now possible to specify min and max summand count, and to permutate the combinations.
Be careful with max_summand_count: If you set it too high, you end up in recursion hell :)
from itertools import permutations
def get_all_possible_summands_to_n(n, min_summand_count=1, max_summand_count=5, min_summand=1, max_summand=None, permutate=False):
if min_summand_count < 1:
raise ValueError("min_summand_count may not be below 1")
if min_summand < 1:
raise ValueError("min_summand may not be below 1")
if not max_summand_count:
max_summand_count = n
if max_summand is None:
max_summand = n
for size in range(min_summand_count, max_summand_count + 1):
if size == 1 and n <= max_summand:
yield [n]
continue
start = (n + size - 1) // size
stop = min(max_summand, n - size + 1) + 1
for i in range(start, stop):
if (size-1) > 0:
for tail in get_all_possible_summands_to_n(n - i, size - 1, size - 1, min_summand, i):
result = list([i] + list(tail))
if [c for c in result if c >=min_summand] == result:
if permutate:
for combination in list(set(permutations(result, len(result)))):
yield list(combination)
else:
yield result
Here all all the tests it passed:
def test_get_all_possible_summands_to_n(self):
# BASICS
self.assertEqual(
list(get_all_possible_summands_to_n(3)),
[[3], [2,1], [1,1,1]]
)
# permutation
self.assertEqual(
list(get_all_possible_summands_to_n(3, permutate=True)),
[[3], [1,2], [2,1], [1,1,1]]
)
# MIN / MAX SUMMAND
self.assertEqual(
list(get_all_possible_summands_to_n(4)),
[[4], [2, 2], [3, 1], [2, 1, 1], [1, 1, 1, 1]]
)
# min summand
self.assertEqual(
list(get_all_possible_summands_to_n(4, min_summand=2)),
[[4], [2, 2]]
)
self.assertEqual(
list(get_all_possible_summands_to_n(4, min_summand=3)),
[[4]]
)
# max summand
self.assertEqual(
list(get_all_possible_summands_to_n(4, max_summand=2)),
[[2, 2], [2, 1, 1], [1, 1, 1, 1]]
)
self.assertEqual(
list(get_all_possible_summands_to_n(4, max_summand=3)),
[[2, 2], [3, 1], [2, 1, 1], [1, 1, 1, 1]]
)
# min / max summand combination
self.assertEqual(
list(get_all_possible_summands_to_n(5)),
[[5], [3, 2], [4, 1], [2, 2, 1], [3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
)
self.assertEqual(
list(get_all_possible_summands_to_n(5, min_summand=2, max_summand=3)),
[[3, 2]]
)
self.assertEqual(
list(get_all_possible_summands_to_n(6, min_summand=3, max_summand=3)),
[[3, 3]]
)
# MIN / MAX SUMMAND COUND
self.assertEqual(
list(get_all_possible_summands_to_n(6)),
[
[6],
[3, 3],
[4, 2],
[5, 1],
[2, 2, 2],
[3, 2, 1],
[4, 1, 1],
[2, 2, 1, 1],
[3, 1, 1, 1],
[2, 1, 1, 1, 1]
]
)
# min summand count
self.assertEqual(
list(get_all_possible_summands_to_n(6, min_summand_count=3)),
[
[2, 2, 2],
[3, 2, 1],
[4, 1, 1],
[2, 2, 1, 1],
[3, 1, 1, 1],
[2, 1, 1, 1, 1]
]
)
# max summand count
self.assertEqual(
list(get_all_possible_summands_to_n(6, max_summand_count=3)),
[
[6],
[3, 3],
[4, 2],
[5, 1],
[2, 2, 2],
[3, 2, 1],
[4, 1, 1],
]
)
# min max summand count combination
self.assertEqual(
list(get_all_possible_summands_to_n(6, min_summand_count=2, max_summand_count=3)),
[
[3, 3],
[4, 2],
[5, 1],
[2, 2, 2],
[3, 2, 1],
[4, 1, 1],
]
)
self.assertEqual(
list(get_all_possible_summands_to_n(6, min_summand_count=3, max_summand_count=3)),
[
[2, 2, 2],
[3, 2, 1],
[4, 1, 1],
]
)
# ALL COMBINATIONS
self.assertEqual(
list(get_all_possible_summands_to_n(12, min_summand_count=2, max_summand_count=3, min_summand=2, max_summand=3)),
[]
)
self.assertEqual(
list(
get_all_possible_summands_to_n(
12,
min_summand_count=4,
max_summand_count=5,
min_summand=2,
max_summand=4
)
),
[
[3, 3, 3, 3],
[4, 3, 3, 2],
[4, 4, 2, 2],
[3, 3, 2, 2, 2],
[4, 2, 2, 2, 2]
]
)
Feel free to optimise it`s performance and elegancy :) - I`m sure there is some potential to do so