我有一个包含15个数字的列表,我需要编写一些代码来生成这些数字的所有32,768种组合.
我发现了一些代码(通过谷歌搜索)显然正在寻找我正在寻找的东西,但我发现代码相当不透明并且对使用它很谨慎.另外我觉得必须有一个更优雅的解决方案.
我发生的唯一事情就是循环遍历十进制整数1-32768并将它们转换为二进制,并使用二进制表示作为过滤器来选择适当的数字.
有谁知道更好的方法?使用map()
,也许?
这个答案错过了一个方面:OP要求所有组合...而不仅仅是长度"r"的组合.
所以你要么必须遍历所有长度"L":
import itertools stuff = [1, 2, 3] for L in range(0, len(stuff)+1): for subset in itertools.combinations(stuff, L): print(subset)
或者 - 如果你想变得时髦(或者在你之后读取你的代码的大脑弯曲) - 你可以生成"combination()"生成器链,并迭代:
from itertools import chain, combinations def all_subsets(ss): return chain(*map(lambda x: combinations(ss, x), range(0, len(ss)+1))) for subset in all_subsets(stuff): print(subset)
看看itertools.combinations:
itertools.combinations(iterable, r)返回输入iterable中元素的r个子序列.
组合以字典排序顺序发出.因此,如果对输入iterable进行排序,则将按排序顺序生成组合元组.
从2.6开始,包括电池!
这是一个懒惰的单行,也使用itertools:
from itertools import compress, product def combinations(items): return ( set(compress(items,mask)) for mask in product(*[[0,1]]*len(items)) ) # alternative: ...in product([0,1], repeat=len(items)) )
这个答案背后的主要思想是:有2 ^ N个组合 - 与长度为N的二进制字符串的数量相同.对于每个二进制字符串,您选择对应于"1"的所有元素.
items=abc * mask=### | V 000 -> 001 -> c 010 -> b 011 -> bc 100 -> a 101 -> a c 110 -> ab 111 -> abc
需要考虑的事项:
这就需要你可以调用len(...)
的items
(解决方法:如果items
是像就像一台发电机的迭代,用第一把它变成一个列表items=list(_itemsArg)
)
这要求迭代的顺序items
不是随机的(解决方法:不要疯狂)
这就要求项目是独一无二的,要不然{2,2,1}
并{2,1,1}
都将崩溃{2,1}
(解决方法:使用collections.Counter
作为一个下拉更换set
;它基本上是一个多集...尽管你可能需要在以后使用tuple(sorted(Counter(...).elements()))
,如果你需要它是可哈希)
演示
>>> list(combinations(range(4))) [set(), {3}, {2}, {2, 3}, {1}, {1, 3}, {1, 2}, {1, 2, 3}, {0}, {0, 3}, {0, 2}, {0, 2, 3}, {0, 1}, {0, 1, 3}, {0, 1, 2}, {0, 1, 2, 3}] >>> list(combinations('abcd')) [set(), {'d'}, {'c'}, {'c', 'd'}, {'b'}, {'b', 'd'}, {'c', 'b'}, {'c', 'b', 'd'}, {'a'}, {'a', 'd'}, {'a', 'c'}, {'a', 'c', 'd'}, {'a', 'b'}, {'a', 'b', 'd'}, {'a', 'c', 'b'}, {'a', 'c', 'b', 'd'}]
在@Dan H 高度评价的回答中的评论中,提到powerset()
了itertools
文档中的配方- 包括Dan本人的一个.但是,到目前为止还没有人发布它作为答案.因为这可能是解决问题的最好方法之一 - 并且考虑到另一位评论者的一点鼓励,如下所示.该函数生成每个可能长度的列表元素的所有唯一组合(包括那些包含零和所有元素的组合).
注意:如果,微妙的不同,目标是获得独特元素的唯一组合,改线s = list(iterable)
,以s = list(set(iterable))
消除任何重复的元素.无论如何,这iterable
最终变成了一种list
手段,它将与发电机一起工作(与其他几个答案不同).
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) # allows duplicate elements return chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) stuff = [1, 2, 3] for i, combo in enumerate(powerset(stuff), 1): print('combo #{}: {}'.format(i, combo))
输出:
combo #1: ()
combo #2: (1,)
combo #3: (2,)
combo #4: (3,)
combo #5: (1, 2)
combo #6: (1, 3)
combo #7: (2, 3)
combo #8: (1, 2, 3)
这是一个使用递归:
>>> import copy >>> def combinations(target,data): ... for i in range(len(data)): ... new_target = copy.copy(target) ... new_data = copy.copy(data) ... new_target.append(data[i]) ... new_data = data[i+1:] ... print new_target ... combinations(new_target, ... new_data) ... ... >>> target = [] >>> data = ['a','b','c','d'] >>> >>> combinations(target,data) ['a'] ['a', 'b'] ['a', 'b', 'c'] ['a', 'b', 'c', 'd'] ['a', 'b', 'd'] ['a', 'c'] ['a', 'c', 'd'] ['a', 'd'] ['b'] ['b', 'c'] ['b', 'c', 'd'] ['b', 'd'] ['c'] ['c', 'd'] ['d']
这个单行程序为您提供所有组合(如果原始列表/集合包含不同的元素之间的项目0
和n
项目n
)并使用本机方法itertools.combinations
:
from itertools import combinations input = ['a', 'b', 'c', 'd'] output = sum([map(list, combinations(input, i)) for i in range(len(input) + 1)], [])
from itertools import combinations input = ['a', 'b', 'c', 'd'] output = sum([list(map(list, combinations(input, i))) for i in range(len(input) + 1)], [])
输出将是:
[[], ['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']]
在线试用:
http://ideone.com/COghfX
我同意Dan H的观点,Ben确实要求所有组合.itertools.combinations()
没有给出所有组合.
另一个问题是,如果输入可迭代很大,那么返回生成器而不是列表中的所有内容可能更好:
iterable = range(10) for s in xrange(len(iterable)+1): for comb in itertools.combinations(iterable, s): yield comb
您可以使用这个简单的代码在python中生成列表的所有组合
import itertools a = [1,2,3,4] for i in xrange(0,len(a)+1): print list(itertools.combinations(a,i))
结果将是:
[()] [(1,), (2,), (3,), (4,)] [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] [(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)] [(1, 2, 3, 4)]
我想我会为那些寻求答案的人添加这个功能,而无需导入itertools或任何其他额外的库.
def powerSet(items): """ Power set generator: get all possible combinations of a list’s elements Input: items is a list Output: returns 2**n combination lists one at a time using a generator Reference: edx.org 6.00.2x Lecture 2 - Decision Trees and dynamic programming """ 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
简单的Yield Yield用法:
for i in powerSet([1,2,3,4]): print (i, ", ", end="")
以上用法示例的输出:
[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3],[4],[1,4] ,[2,4],[1,2,4],[3,4],[1,3,4],[2,3,4],[1,2,3,4],
这种方法可以轻松地转移到所有支持递归的编程语言中(无itertools,无收益,无列表理解):
def combs(a): if len(a) == 0: return [[]] cs = [] for c in combs(a[1:]): cs += [c, c+[a[0]]] return cs >>> combs([1,2,3,4,5]) [[], [1], [2], [2, 1], [3], [3, 1], [3, 2], ..., [5, 4, 3, 2, 1]]
这是另一个解决方案(单线程),涉及使用itertools.combinations
函数,但在这里我们使用双列表理解(而不是for循环或求和):
def combs(x): return [c for i in range(len(x)+1) for c in combinations(x,i)]
演示:
>>> combs([1,2,3,4]) [(), (1,), (2,), (3,), (4,), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4), (1, 2, 3, 4)]