找到了一个解决方案:它基于这样的想法,即正数 N 是一行单位,将它们分成 S 块是在列表中放置 (S-1) 个分隔符的问题。
这些分隔符可以使用combinations(range(N + S - 1), S - 1) 进行迭代。下一步是计算分隔符之前、之间和之后的单元数:
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,))]
当您想限制结果中的每个项目时,您可以过滤掉不需要的项目(当然不是最佳的,但我想使用combinations,因为它可能是用 C 实现的,因此可能比我想出的任何东西都快得多在 python 中)。简单版:
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
还有更快但更复杂的版本:
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
我用这个作为早期测试:
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)
顺便说一句:从臀部:您可以通过迭代 S(大小)来生成整数分区问题的解决方案:如:
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))
我稍微调整了combinations() 迭代器以不给零。如果order=False,它将为具有不同顺序的相同分区减倍。