这些被称为set partitions。可以在此处找到递归函数:Set partitions in Python。一个更有效的“自下而上”递归版本是这样的:
def generate_partitions(a):
a = list(a)
n = len(a)
partition = [] # a list of lists, currently empty
def assign(i):
if i >= n:
yield [list(part) for part in partition]
else:
# assign a[i] to an existing part
for part in partition:
part.append(a[i])
yield from assign(i + 1)
part.pop()
# assign a[i] to a completely new part
partition.append([a[i]])
yield from assign(i + 1)
partition.pop()
if n:
yield from assign(0)
else:
yield [[]]
for partition in generate_partitions([1,2,3]):
print(*partition)
输出:
[1, 2, 3]
[1, 2] [3]
[1, 3] [2]
[1] [2, 3]
[1] [2] [3]
这不会像您的示例中那样生成空框,但增加生成器这样做是微不足道的。
有关迭代算法,请参阅 Michael Orlov (2002) 的“Efficient Generation of Set Partitions”。请注意,集合分区的数量非常快速增长,因此即使是迭代算法也需要一些时间来枚举即使是中等大小的集合的所有分区。
要计算设置分区的数量而不生成它们,请参阅Bell Numbers (OEIS A000110)。这是在 Python 中计算贝尔数的一种可能(不是很有效)的过程:
def bell(n):
"-> the n'th Bell number."
assert n >= 0, n
# loop will execute at least once
for b in bell_sequence(n):
pass
return b
def bell_sequence(n):
"""Yields the Bell numbers b(0),b(1)...b(n).
This function requires O(n) auxiliary storage.
"""
assert n >= 0, n
# compute Bell numbers using the triangle scheme
yield 1 # b(0)
row = [1] + (n-1)*[0]
for i in range(0, n):
row[i] = row[0]
for k in reversed(range(i)):
row[k] += row[k + 1]
yield row[0] # b(i + 1)