给定一个集合
{0, 1, 2, 3}
如何生成子集:
[set(),
{0},
{1},
{2},
{3},
{0, 1},
{0, 2},
{0, 3},
{1, 2},
{1, 3},
{2, 3},
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1, 2, 3}]
给定一个集合
{0, 1, 2, 3}
如何生成子集:
[set(),
{0},
{1},
{2},
{3},
{0, 1},
{0, 2},
{0, 3},
{1, 2},
{1, 3},
{2, 3},
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1, 2, 3}]
Python itertools
页面上有一个powerset
函数可以实现此功能:
from itertools import chain, combinations
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
输出:
>>> list(powerset("abcd"))
[(), ('a',), ('b',), ('c',), ('d',), ('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd'), ('a', 'b', 'c', 'd')]
如果你不喜欢在开始时得到一个空元组,你可以将 range
语句更改为 range(1, len(s)+1)
,以避免得到长度为 0 的组合。
s = list(iterable)
? - Shevach__len__
方法;尝试在不使用列表包装的情况下使用 powerset((n for n in range(3)))
。 - hoeflingpowerset(range(3))
可以正常工作,即使没有s = list(iterable)
(https://ideone.com/BiatoT)。 - user2357112这里是更多的幂集代码。这是从零开始编写的:
>>> def powerset(s):
... x = len(s)
... for i in range(1 << x):
... print [s[j] for j in range(x) if (i & (1 << j))]
...
>>> powerset([4,5,6])
[]
[4]
[5]
[4, 5]
[6]
[4, 6]
[5, 6]
[4, 5, 6]
Mark Rushakoff的评论适用于这里:“如果你不喜欢在开头有空元组,那么可以将范围语句更改为range(1,len(s)+1)以避免长度为0的组合”,但在我的情况下,您需要将for i in range(1<<x)
更改为for i in range(1,1<<x)
。
数年后回顾这个问题,现在我会这样写:
def powerset(s):
x = len(s)
masks = [1 << i for i in range(x)]
for i in range(1 << x):
yield [ss for mask, ss in zip(masks, s) if i & mask]
然后,测试代码将会长成这个样子:
print(list(powerset([4, 5, 6])))
使用 yield
意味着您不需要在单个内存块中计算所有结果。外部预先计算掩码被认为是一种值得优化的方式。
如果您正在寻找快速答案,我刚在谷歌上搜索了“python power set”,并找到了这个链接:Python Power Set Generator
以下是该页面代码的复制粘贴:
def powerset(seq):
"""
Returns all the subsets of this set. This is a generator.
"""
if len(seq) <= 1:
yield seq
yield []
else:
for item in powerset(seq[1:]):
yield [seq[0]]+item
yield item
可以这样使用:
l = [1, 2, 3, 4]
r = [x for x in powerset(l)]
现在 r 是包含所有所需元素的列表,可以进行排序并打印:
r.sort()
print r
[[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4], [1, 3], [1, 3, 4], [1, 4], [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]
[[][]]
,为了解决这个问题,需要分别处理长度检查的情况:
if len(seq) == 0: yield [] else if len(seq) == 1: yield seq yield []
- Ayush使用powerset()
函数,来自more_itertools
包。
生成可迭代对象的所有可能子集
>>> list(powerset([1, 2, 3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
如果您想使用集合,请使用:
list(map(set, powerset(iterable)))
from functools import reduce
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
我发现以下算法非常清晰简单:
def get_powerset(some_list):
"""Returns all subsets of size 0 - len(some_list) for some_list"""
if len(some_list) == 0:
return [[]]
subsets = []
first_element = some_list[0]
remaining_list = some_list[1:]
# Strategy: get all the subsets of remaining_list. For each
# of those subsets, a full subset list will contain both
# the original subset as well as a version of the subset
# that contains first_element
for partial_subset in get_powerset(remaining_list):
subsets.append(partial_subset)
subsets.append(partial_subset[:] + [first_element])
return subsets
生成幂集的另一种方法是生成所有具有 n
位的二进制数。作为幂集,具有 n
位数字的数量是 2 ^ n
。该算法的原则是一个元素可以存在于子集中,也可以不存在;正如二进制数字可以是 1 或 0,但不能同时为两者。
def power_set(items):
N = len(items)
# enumerate the 2 ** N possible combinations
for i in range(2 ** N):
combo = []
for j in range(N):
# test bit jth of integer i
if (i >> j) % 2 == 1:
combo.append(items[j])
yield combo
我在学习MITx: 6.00.2x计算思维和数据科学导论时,了解了这两种算法。我认为这是我见过最容易理解的算法之一。
我之前添加了一个答案,但我真的很喜欢我的新实现。我将一个集合作为输入,但实际上它可以是任何可迭代的对象,并返回一个包含输入的所有子集的集合。我喜欢这种方法,因为它更符合幂集(所有子集的集合)的数学定义。
def power_set(A):
"""A is an iterable (list, tuple, set, str, etc)
returns a set which is the power set of A."""
length = len(A)
l = [a for a in A]
ps = set()
for i in range(2 ** length):
selector = f'{i:0{length}b}'
subset = {l[j] for j, bit in enumerate(selector) if bit == '1'}
ps.add(frozenset(subset))
return ps
如果您想要与您在答案中发布的输出完全相同,请使用以下内容:
>>> [set(s) for s in power_set({1, 2, 3, 4})]
[{3, 4},
{2},
{1, 4},
{2, 3, 4},
{2, 3},
{1, 2, 4},
{1, 2},
{1, 2, 3},
{3},
{2, 4},
{1},
{1, 2, 3, 4},
set(),
{1, 3},
{1, 3, 4},
{4}]
众所周知,幂集的元素数为2 ** len(A)
,这在for
循环中可以清楚地看到。
我需要将输入(理想情况下是一个集合)转换为列表,因为集合是一种包含唯一无序元素的数据结构,而顺序对于生成子集至关重要。
selector
在此算法中非常关键。请注意,selector
与输入集合具有相同的长度,并且为了实现这一点,它使用了带填充的f字符串。基本上,这使我能够选择在每次迭代期间将添加到每个子集中的元素。假设输入集合有3个元素{0, 1, 2}
,则选择器将取值介于0和7之间(包括0和7),其二进制表示为:
000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7
1
表示应该添加索引j
处的元素,0
表示不应该添加这个元素。frozenset
,以便我可以将其添加到ps
(幂集)中。否则,我将无法添加它,因为Python中的集合仅由不可变对象组成。zip
来避免使用j
索引,代码最终将如下所示:def power_set(A):
length = len(A)
return {
frozenset({e for e, b in zip(A, f'{i:{length}b}') if b == '1'})
for i in range(2 ** length)
}
就是这样。我喜欢这个算法的原因是它比其他算法更清晰、更直观,因为它看起来像是依靠 itertools
这个神奇的工具,即使它按预期工作。
有一种幂集的改进:
def powerset(seq):
"""
Returns all the subsets of this set. This is a generator.
"""
if len(seq) <= 0:
yield []
else:
for item in powerset(seq[1:]):
yield [seq[0]]+item
yield item
itertools.product
自然地完成:import itertools
def powerset(l):
for sl in itertools.product(*[[[], [i]] for i in l]):
yield {j for i in sl for j in i}
def get_power_set(s):
power_set=[[]]
for elem in s:
# iterate over the sub sets so far
for sub_set in power_set:
# add a new subset consisting of the subset at hand added elem to it
# effectively doubling the sets resulting in the 2^n sets in the powerset of s.
power_set=power_set+[list(sub_set)+[elem]]
return power_set
例如:
get_power_set([1,2,3])
yield
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
power_set
)中修改它是一种非常可疑的做法。例如,假设您编写了以下代码而不是建议的变量修改代码:power_set += [list(sub_set)+[elem]]
。那么循环将无法终止。 - hughdbrown