如何找到集合 A 的所有子集组?在 Python 中设置分区
how find all groups of subsets of set A? Set partitions in Python
我想找到一个算法,给定一个集合 A
来找到满足以下条件的所有子集组:
x ∪ y ∪ .... z = A, where x, y, ... z ∈ Group
和
∀ x,y ∈ Group: x ⊆ A, y ⊆ A, x ∩ y = ∅ = {}
和
∀ x ∈ Group: x != ∅
注:希望定义的好,我数学符号不好
我采用以下方法仅搜索两个子集的组:
from itertools import product, combinations
def my_combos(A):
subsets = []
for i in xrange(1, len(A)):
subsets.append(list(combinations(A,i)))
combos = []
for i in xrange(1, 1+len(subsets)/2):
combos.extend(list(product(subsets[i-1], subsets[-i])))
if not len(A) % 2:
combos.extend(list(combinations(subsets[len(A)/2-1], 2)))
return [combo for combo in combos if not set(combo[0]) & set(combo[1])]
my_combos({1,2,3,4})
我得到以下输出,这些都是由两个子集组成的组
[
((1,), (2, 3, 4)),
((2,), (1, 3, 4)),
((3,), (1, 2, 4)),
((4,), (1, 2, 3)),
((1, 2), (3, 4)),
((1, 3), (2, 4)),
((1, 4), (2, 3))
]
..... 但是,由一个、三个、四个子集组成的组....
问题:
我怎样才能找到通用的解决方案?
例如以下预期输出:
my_combos({1,2,3,4})
[
((1,2,3,4)),
((1,2,3),(4,)),
((1,2,4),(3,)),
((1,3,4),(2,)),
((2,3,4),(1,)),
((1,2),(3,4)),
((1,3),(2,4)),
((1,4),(2,3)),
((1,2),(3,),(4,)),
((1,3),(2,),(4,)),
((1,4),(2,),(3,)),
((1,),(2,),(3,4)),
((1,),(3,),(2,4)),
((1,),(4,),(2,3)),
((1,),(4,),(2,),(3,))
]
解决方案:
def partitions(A):
if not A:
yield []
else:
a, *R = A
for partition in partitions(R):
yield partition + [[a]]
for i, subset in enumerate(partition):
yield partition[:i] + [subset + [a]] + partition[i+1:]
解释:
- 空集只有空分区
- 对于非空集合,取出一个元素,然后对于剩余元素的每个分区,将该元素添加为它自己的子集或将其添加到分区的子集之一。
- 请注意,分区实际上是一组集合。我只将它表示为列表列表,因为这样更快,而且我不想使用 frozensets,它们打印效果不佳。元组速度更快,问题是针对它们提出的,但我无法忍受单元素元组的逗号。
测试输出:
for partition in partitions({1, 2, 3, 4}):
print(partition)
[[4], [3], [2], [1]]
[[4, 1], [3], [2]]
[[4], [3, 1], [2]]
[[4], [3], [2, 1]]
[[4, 2], [3], [1]]
[[4, 2, 1], [3]]
[[4, 2], [3, 1]]
[[4], [3, 2], [1]]
[[4, 1], [3, 2]]
[[4], [3, 2, 1]]
[[4, 3], [2], [1]]
[[4, 3, 1], [2]]
[[4, 3], [2, 1]]
[[4, 3, 2], [1]]
[[4, 3, 2, 1]]
带输出的速度测试(在相对较弱的笔记本电脑上):
from time import time
print('elements partitions seconds')
for n in range(14):
t0 = time()
number = sum(1 for partition in partitions(range(n)))
print('{:5}{:10}{:11.2f}'.format(n, number, time() - t0))
elements partitions seconds
0 1 0.00
1 1 0.00
2 2 0.00
3 5 0.00
4 15 0.00
5 52 0.00
6 203 0.00
7 877 0.00
8 4140 0.06
9 21147 0.07
10 115975 0.36
11 678570 2.20
12 4213597 13.56
13 27644437 87.59
我用 OEIS page 确认了那些分区号。
我想找到一个算法,给定一个集合 A
来找到满足以下条件的所有子集组:
x ∪ y ∪ .... z = A, where x, y, ... z ∈ Group
和
∀ x,y ∈ Group: x ⊆ A, y ⊆ A, x ∩ y = ∅ = {}
和
∀ x ∈ Group: x != ∅
注:希望定义的好,我数学符号不好
我采用以下方法仅搜索两个子集的组:
from itertools import product, combinations
def my_combos(A):
subsets = []
for i in xrange(1, len(A)):
subsets.append(list(combinations(A,i)))
combos = []
for i in xrange(1, 1+len(subsets)/2):
combos.extend(list(product(subsets[i-1], subsets[-i])))
if not len(A) % 2:
combos.extend(list(combinations(subsets[len(A)/2-1], 2)))
return [combo for combo in combos if not set(combo[0]) & set(combo[1])]
my_combos({1,2,3,4})
我得到以下输出,这些都是由两个子集组成的组
[
((1,), (2, 3, 4)),
((2,), (1, 3, 4)),
((3,), (1, 2, 4)),
((4,), (1, 2, 3)),
((1, 2), (3, 4)),
((1, 3), (2, 4)),
((1, 4), (2, 3))
]
..... 但是,由一个、三个、四个子集组成的组....
问题:
我怎样才能找到通用的解决方案?
例如以下预期输出:
my_combos({1,2,3,4})
[
((1,2,3,4)),
((1,2,3),(4,)),
((1,2,4),(3,)),
((1,3,4),(2,)),
((2,3,4),(1,)),
((1,2),(3,4)),
((1,3),(2,4)),
((1,4),(2,3)),
((1,2),(3,),(4,)),
((1,3),(2,),(4,)),
((1,4),(2,),(3,)),
((1,),(2,),(3,4)),
((1,),(3,),(2,4)),
((1,),(4,),(2,3)),
((1,),(4,),(2,),(3,))
]
解决方案:
def partitions(A):
if not A:
yield []
else:
a, *R = A
for partition in partitions(R):
yield partition + [[a]]
for i, subset in enumerate(partition):
yield partition[:i] + [subset + [a]] + partition[i+1:]
解释:
- 空集只有空分区
- 对于非空集合,取出一个元素,然后对于剩余元素的每个分区,将该元素添加为它自己的子集或将其添加到分区的子集之一。
- 请注意,分区实际上是一组集合。我只将它表示为列表列表,因为这样更快,而且我不想使用 frozensets,它们打印效果不佳。元组速度更快,问题是针对它们提出的,但我无法忍受单元素元组的逗号。
测试输出:
for partition in partitions({1, 2, 3, 4}):
print(partition)
[[4], [3], [2], [1]]
[[4, 1], [3], [2]]
[[4], [3, 1], [2]]
[[4], [3], [2, 1]]
[[4, 2], [3], [1]]
[[4, 2, 1], [3]]
[[4, 2], [3, 1]]
[[4], [3, 2], [1]]
[[4, 1], [3, 2]]
[[4], [3, 2, 1]]
[[4, 3], [2], [1]]
[[4, 3, 1], [2]]
[[4, 3], [2, 1]]
[[4, 3, 2], [1]]
[[4, 3, 2, 1]]
带输出的速度测试(在相对较弱的笔记本电脑上):
from time import time
print('elements partitions seconds')
for n in range(14):
t0 = time()
number = sum(1 for partition in partitions(range(n)))
print('{:5}{:10}{:11.2f}'.format(n, number, time() - t0))
elements partitions seconds
0 1 0.00
1 1 0.00
2 2 0.00
3 5 0.00
4 15 0.00
5 52 0.00
6 203 0.00
7 877 0.00
8 4140 0.06
9 21147 0.07
10 115975 0.36
11 678570 2.20
12 4213597 13.56
13 27644437 87.59
我用 OEIS page 确认了那些分区号。