これらは、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)の "Set Partitionの効率的な生成"を参照してください。設定されたパーティションの数は、にすばやく増えます。したがって、反復アルゴリズムでさえ、適度なサイズのセットのすべてのパーティションを列挙するのに時間がかかることに注意してください。
カウント生成されていない設定パーティションの数は、Bell Numbers(OEIS A000110)を参照してください。 PythonでBell番号を計算するには、次のような手順があります(効率的ではありません):
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)