Found a solution: it is based on the idea that a positive number N is a line of units and splitting them in S pieces is a matter of putting (S-1) separators in the list.
These separators can iterated with combinations(range(N + S - 1), S - 1)
. The next step is to calculate the number of units before, between and after the separators:
def partition(N, size):
n = N + size - 1
for splits in combinations(range(n), size - 1):
yield [s1 - s0 - 1 for s0, s1 in zip((-1,) + splits, splits + (n,))]
When you want to limit each item in the result you can filter unwanted out (surely not optimal, but i wanted to use combinations
because it is probably implemented in C, and therefore probably much faster than anything i can come up with in python). The simpole version:
def sized_partition_slow(N, sizes):
size = len(sizes)
n = N + size - 1
for splits in combinations(range(n), size - 1):
result = [s1 - s0 - 1 for s0, s1 in zip((-1,) + splits, splits + (n,))]
if all(r < s for r, s in zip(result, sizes)):
yield result
And the faster but more complicated version:
def sized_partition(N, sizes):
size = len(sizes)
n = N + size - 1
for splits in combinations(range(n), size - 1):
result = []
for s, s0, s1 in zip(sizes, (-1,) + splits, splits + (n,)):
r = s1 - s0 - 1
if r >= s:
break
result.append(r)
else:
yield result
I used this as early test:
for indices in partition(4, 3):
assert sum(indices) == 4
assert all(0 <= i for i in indices)
for indices in sized_partition(4, [3, 3, 3]):
assert sum(indices) == 4
assert all(0 <= i < 3 for i in indices)
BTW: from the hip: you can generate the solution to the integer partitioning problem by iterating over S (size): as in:
def integer_partition(N, order=False):
result = set()
for size in range(1, N+1):
for splits in combinations(range(1, N), size - 1):
if order:
p = tuple(s1 - s0 for s0, s1 in zip((0,) + splits, splits + (N,)))
else:
p = tuple(sorted(s1 - s0 for s0, s1 in zip((0,) + splits, splits + (N,))))
result.add(p)
return sorted(result, key=lambda r: (len(r), r))
I adapted the combinations()
iterator a bit to not give zeros. It dedoubles for same partitions with different orders if order=False
.