如果您要生成所有子集,您最终会为长度为n的列表生成 2 n个子集。一种常见的方法是遍历从 0 到 2 n -1的所有数字i ,并使用在i中设置的位来确定哪些项目在第i个子集中。这是有效的,因为任何项目要么存在或不存在于任何特定子集中,因此通过遍历n位的所有组合,您可以遍历 2 n个子集。
例如,要生成 (1, 2, 3) 的子集,您将遍历数字 0 到 7:
0 = 000 b → ()
1 = 001 b → (1)
2 = 010 b → (2)
3 = 011 b → (1, 2)
4 = 100 b → (3)
5 = 101 b → (1, 3 )
6 = 110 b → (2, 3)
7 = 111 b → (1, 2, 3)
在您的问题中,您可以生成每个子集及其补集,以获得您的一对互斥子集。当你这样做时,每一对都会重复,所以你只需要迭代到 2 n -1 - 1 然后停止。
1 = 001 b → (1) + (2, 3)
2 = 010 b → (2) + (1, 3)
3 = 011 b → (1, 2) + (3)
要处理重复项,您可以生成列表索引的子集而不是列表项的子集。与列表 (1, 2, 2, 3) 一样,生成列表 (0, 1, 2, 3) 的子集,然后将这些数字用作 (1, 2, 2, 3) 列表的索引。基本上,添加一个间接级别。
这是一些将这一切放在一起的 Python 代码。
#!/usr/bin/env python
def split_subsets(items):
subsets = set()
for n in xrange(1, 2 ** len(items) / 2):
# Use ith index if ith bit of n is set.
l_indices = [i for i in xrange(0, len(items)) if n & (1 << i) != 0]
# Use the indices NOT present in l_indices.
r_indices = [i for i in xrange(0, len(items)) if i not in l_indices]
# Get the items corresponding to the indices above.
l = tuple(items[i] for i in l_indices)
r = tuple(items[i] for i in r_indices)
# Swap l and r if they are reversed.
if (len(l), l) > (len(r), r):
l, r = r, l
subsets.add((l, r))
# Sort the subset pairs so the left items are in ascending order.
return sorted(subsets, key = lambda (l, r): (len(l), l))
for l, r in split_subsets([1, 2, 2, 3]):
print l, r
输出:
(1,) (2, 2, 3)
(2,) (1, 2, 3)
(3,) (1, 2, 2)
(1, 2) (2, 3)
(1, 3) (2, 2)