如何获得列表元素的所有可能组合?

我有一个包含15个数字的列表,我需要编写一些代码来生成这些数字的所有32,768种组合。

我发现一些代码(通过谷歌搜索)显然做了我正在寻找的,但我发现代码相当不透明,并谨慎使用它。而且我觉得肯定有更优雅的解决方案。

我唯一想到的就是循环十进制整数1-32768并将其转换为二进制,并使用二进制表示作为筛选器来挑选适当的数字。

有人知道更好的办法吗?使用map(),也许?

1165910 次浏览

看看itertools.combinations:

itertools.combinations(iterable, r)

返回from元素的r长度子序列 输入可迭代对象。

组合以字典排序顺序发出。那么,如果 Input iterable已排序,则 组合元组将在 排序顺序。< / p >

从2.6开始,电池包括在内!

这个答案漏掉了一个方面:OP要求所有的组合…而不仅仅是长度的组合。

所以你要么必须循环遍历所有长度"L":

import itertools


stuff = [1, 2, 3]
for L in range(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:

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

需要考虑的事情:

  • 这要求你可以在items上调用len(...)(解决方法:如果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'}]

使用列表推导式:

def selfCombine( list2Combine, length ):
listCombined = str( ['list2Combine[i' + str( i ) + ']' for i in range( length )] ).replace( "'", '' ) \
+ 'for i0 in range(len( list2Combine ) )'
if length > 1:
listCombined += str( [' for i' + str( i ) + ' in range( i' + str( i - 1 ) + ', len( list2Combine ) )' for i in range( 1, length )] )\
.replace( "', '", ' ' )\
.replace( "['", '' )\
.replace( "']", '' )


listCombined = '[' + listCombined + ']'
listCombined = eval( listCombined )


return listCombined


list2Combine = ['A', 'B', 'C']
listCombined = selfCombine( list2Combine, 2 )

输出将是:

['A', 'A']
['A', 'B']
['A', 'C']
['B', 'B']
['B', 'C']
['C', 'C']

我同意Dan H的观点,Ben确实要求所有组合。itertools.combinations()没有给出所有的组合。

另一个问题是,如果输入iterable很大,返回一个生成器而不是列表中的所有内容可能会更好:

iterable = range(10)
for s in xrange(len(iterable)+1):
for comb in itertools.combinations(iterable, s):
yield comb

下面是一个使用递归的例子:

>>> 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']

这一行代码给出了所有的组合(如果原始列表/集包含n个不同的元素,则在0n项之间),并使用本机方法itertools.combinations:

Python 2

from itertools import combinations


input = ['a', 'b', 'c', 'd']


output = sum([map(list, combinations(input, i)) for i in range(len(input) + 1)], [])

Python 3

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

你可以使用以下简单的代码在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)]

这段代码采用了一个简单的嵌套列表算法…

# FUNCTION getCombos: To generate all combos of an input list, consider the following sets of nested lists...
#
#           [ [ [] ] ]
#           [ [ [] ], [ [A] ] ]
#           [ [ [] ], [ [A],[B] ],         [ [A,B] ] ]
#           [ [ [] ], [ [A],[B],[C] ],     [ [A,B],[A,C],[B,C] ],                   [ [A,B,C] ] ]
#           [ [ [] ], [ [A],[B],[C],[D] ], [ [A,B],[A,C],[B,C],[A,D],[B,D],[C,D] ], [ [A,B,C],[A,B,D],[A,C,D],[B,C,D] ], [ [A,B,C,D] ] ]
#
#  There is a set of lists for each number of items that will occur in a combo (including an empty set).
#  For each additional item, begin at the back of the list by adding an empty list, then taking the set of
#  lists in the previous column (e.g., in the last list, for sets of 3 items you take the existing set of
#  3-item lists and append to it additional lists created by appending the item (4) to the lists in the
#  next smallest item count set. In this case, for the three sets of 2-items in the previous list. Repeat
#  for each set of lists back to the initial list containing just the empty list.
#


def getCombos(listIn = ['A','B','C','D','E','F'] ):
listCombos = [ [ [] ] ]     # list of lists of combos, seeded with a list containing only the empty list
listSimple = []             # list to contain the final returned list of items (e.g., characters)


for item in listIn:
listCombos.append([])   # append an emtpy list to the end for each new item added
for index in xrange(len(listCombos)-1, 0, -1):  # set the index range to work through the list
for listPrev in listCombos[index-1]:        # retrieve the lists from the previous column
listCur = listPrev[:]                   # create a new temporary list object to update
listCur.append(item)                    # add the item to the previous list to make it current
listCombos[index].append(listCur)       # list length and append it to the current list


itemCombo = ''                          # Create a str to concatenate list items into a str
for item in listCur:                    # concatenate the members of the lists to create
itemCombo += item                   # create a string of items
listSimple.append(itemCombo)            # add to the final output list


return [listSimple, listCombos]
# END getCombos()

下面是一个“标准递归答案”,类似于另一个类似的答案https://stackoverflow.com/a/23743696/711085。(实际上,我们不必担心耗尽堆栈空间,因为我们没有办法处理所有N!排列)。

它依次访问每个元素,要么取它,要么离开它(从这个算法中我们可以直接看到2^N的基数)。

def combs(xs, i=0):
if i==len(xs):
yield ()
return
for c in combs(xs,i+1):
yield c
yield c+(xs[i],)

演示:

>>> list( combs(range(5)) )
[(), (0,), (1,), (1, 0), (2,), (2, 0), (2, 1), (2, 1, 0), (3,), (3, 0), (3, 1), (3, 1, 0), (3, 2), (3, 2, 0), (3, 2, 1), (3, 2, 1, 0), (4,), (4, 0), (4, 1), (4, 1, 0), (4, 2), (4, 2, 0), (4, 2, 1), (4, 2, 1, 0), (4, 3), (4, 3, 0), (4, 3, 1), (4, 3, 1, 0), (4, 3, 2), (4, 3, 2, 0), (4, 3, 2, 1), (4, 3, 2, 1, 0)]


>>> list(sorted( combs(range(5)), key=len))
[(),
(0,), (1,), (2,), (3,), (4,),
(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3),
(2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1), (4, 3, 2),
(3, 2, 1, 0), (4, 2, 1, 0), (4, 3, 1, 0), (4, 3, 2, 0), (4, 3, 2, 1),
(4, 3, 2, 1, 0)]


>>> len(set(combs(range(5))))
32

这里是另一个解决方案(一行程序),涉及到使用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)]

的文档中所述

def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
return
indices = list(range(r))
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != i + n - r:
break
else:
return
indices[i] += 1
for j in range(i+1, r):
indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices)




x = [2, 3, 4, 5, 1, 6, 4, 7, 8, 3, 9]
for i in combinations(x, 2):
print i

在@Dan H高度好评的回答下的评论中,提到了# eyz3中的powerset()配方,其中包括丹自己然而,到目前为止还没有人把它作为答案发布出来。因为这可能是解决这个问题最好的方法之一(如果不是最好的方法的话),并且给出了另一位评论者的小的鼓励,如下所示。该函数生成长度为每一个的列表元素的所有个唯一组合(包括包含0和所有元素的列表元素)。

请注意:如果略有不同,目标是只获得唯一元素的组合,将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)

我想我应该为那些寻求答案的人添加这个函数,而不需要导入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 Generator用法:

for i in powerSet([1,2,3,4]):
print (i, ", ",  end="")

以上用法示例的输出:

< p >[],[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],

这是我的实现

def get_combinations(list_of_things):
"""gets every combination of things in a list returned as a list of lists


Should be read : add all combinations of a certain size to the end of a list for every possible size in the
the list_of_things.


"""
list_of_combinations = [list(combinations_of_a_certain_size)
for possible_size_of_combinations in range(1,  len(list_of_things))
for combinations_of_a_certain_size in itertools.combinations(list_of_things,
possible_size_of_combinations)]
return list_of_combinations

我知道使用itertools来获得所有的组合要实际得多,但是如果你碰巧想要,如果你想要编码很多,你可以只需要列表理解就可以部分实现这一点

对于两对组合:

lambda l: [(a, b) for i, a in enumerate(l) for b in l[i+1:]]

而且,对于三对组合,它是这样简单的:

lambda l: [(a, b, c) for i, a in enumerate(l) for ii, b in enumerate(l[i+1:]) for c in l[i+ii+2:]]

结果和使用itertools.combination是一样的:

import itertools
combs_3 = lambda l: [
(a, b, c) for i, a in enumerate(l)
for ii, b in enumerate(l[i+1:])
for c in l[i+ii+2:]
]
data = ((1, 2), 5, "a", None)
print("A:", list(itertools.combinations(data, 3)))
print("B:", combs_3(data))
# A: [((1, 2), 5, 'a'), ((1, 2), 5, None), ((1, 2), 'a', None), (5, 'a', None)]
# B: [((1, 2), 5, 'a'), ((1, 2), 5, None), ((1, 2), 'a', None), (5, 'a', None)]

不使用itertools:

def combine(inp):
return combine_helper(inp, [], [])




def combine_helper(inp, temp, ans):
for i in range(len(inp)):
current = inp[i]
remaining = inp[i + 1:]
temp.append(current)
ans.append(tuple(temp))
combine_helper(remaining, temp, ans)
temp.pop()
return ans




print(combine(['a', 'b', 'c', 'd']))

下面是itertools.combinations的两个实现

返回一个列表的函数

def combinations(lst, depth, start=0, items=[]):
if depth <= 0:
return [items]
out = []
for i in range(start, len(lst)):
out += combinations(lst, depth - 1, i + 1, items + [lst[i]])
return out

一个返回一个生成器

def combinations(lst, depth, start=0, prepend=[]):
if depth <= 0:
yield prepend
else:
for i in range(start, len(lst)):
for c in combinations(lst, depth - 1, i + 1, prepend + [lst[i]]):
yield c

请注意,建议为它们提供一个helper函数,因为prepend参数是静态的,不会随着每次调用而改变

print([c for c in combinations([1, 2, 3, 4], 3)])
# [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]


# get a hold of prepend
prepend = [c for c in combinations([], -1)][0]
prepend.append(None)


print([c for c in combinations([1, 2, 3, 4], 3)])
# [[None, 1, 2, 3], [None, 1, 2, 4], [None, 1, 3, 4], [None, 2, 3, 4]]

这是一个很肤浅的例子,但小心为妙

如果有人正在寻找一个反向列表,就像我一样:

stuff = [1, 2, 3, 4]


def reverse(bla, y):
for subset in itertools.combinations(bla, len(bla)-y):
print list(subset)
if y != len(bla):
y += 1
reverse(bla, y)


reverse(stuff, 1)

这个怎么样?使用字符串而不是列表,但同样的事情..string可以像Python中的列表一样处理:

def comb(s, res):
if not s: return
res.add(s)
for i in range(0, len(s)):
t = s[0:i] + s[i + 1:]
comb(t, res)


res = set()
comb('game', res)


print(res)

来自itertools的组合

import itertools
col_names = ["aa","bb", "cc", "dd"]
all_combinations = itertools.chain(*[itertools.combinations(col_names,i+1) for i,_ in enumerate(col_names)])
print(list(all_combinations))

没有 itertools在Python 3中,你可以这样做:

def combinations(arr, carry):
for i in range(len(arr)):
yield carry + arr[i]
yield from combinations(arr[i + 1:], carry + arr[i])

carry = "".

这种方法可以很容易地移植到所有支持递归的编程语言(没有itertools,没有yield,没有列表理解)中:

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]]
flag = 0
requiredCals =12
from itertools import chain, combinations


def powerset(iterable):
s = list(iterable)  # allows duplicate elements
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))


stuff = [2,9,5,1,6]
for i, combo in enumerate(powerset(stuff), 1):
if(len(combo)>0):
#print(combo , sum(combo))
if(sum(combo)== requiredCals):
flag = 1
break
if(flag==1):
print('True')
else:
print('else')


from itertools import combinations




features = ['A', 'B', 'C']
tmp = []
for i in range(len(features)):
oc = combinations(features, i + 1)
for c in oc:
tmp.append(list(c))

输出

[
['A'],
['B'],
['C'],
['A', 'B'],
['A', 'C'],
['B', 'C'],
['A', 'B', 'C']
]

3个功能:

  1. 列出n个元素的所有组合
  2. 列出n个元素的所有组合,其中顺序不明确
  3. 所有的排列
import sys


def permutations(a):
return combinations(a, len(a))


def combinations(a, n):
if n == 1:
for x in a:
yield [x]
else:
for i in range(len(a)):
for x in combinations(a[:i] + a[i+1:], n-1):
yield [a[i]] + x


def combinationsNoOrder(a, n):
if n == 1:
for x in a:
yield [x]
else:
for i in range(len(a)):
for x in combinationsNoOrder(a[:i], n-1):
yield [a[i]] + x
    

if __name__ == "__main__":
for s in combinations(list(map(int, sys.argv[2:])), int(sys.argv[1])):
print(s)

您还可以使用优秀的more_itertools包中的powerset函数。

from more_itertools import powerset


l = [1,2,3]
list(powerset(l))


# [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

我们也可以验证,它满足OP的要求

from more_itertools import ilen


assert ilen(powerset(range(15))) == 32_768
我来派对晚了,但我想分享我对同样问题的解决方案: 具体地说,我想做顺序组合,所以对于“STAR"我想要的是“star”、“ta”、“ar”,而不是“srr”。

lst = [S, T, A, R]
lstCombos = []
for Length in range(0,len(lst)+1):
for i in lst:
lstCombos.append(lst[lst.index(i):lst.index(i)+Length])

可以通过在最后一行之前添加额外的if来过滤重复:

lst = [S, T, A, R]
lstCombos = []
for Length in range(0,len(lst)+1):
for i in lst:
if not lst[lst.index(i):lst.index(i)+Length]) in lstCombos:
lstCombos.append(lst[lst.index(i):lst.index(i)+Length])

如果由于某种原因,这将在输出中返回空白列表,这发生在我身上,我添加:

for subList in lstCombos:
if subList = '':
lstCombos.remove(subList)

如果你不想使用组合库,这里是解决方案:

nums = [1,2,3]
p = [[]]
fnl = [[],nums]


for i in range(len(nums)):
for j in range(i+1,len(nums)):
p[-1].append([i,j])


for i in range(len(nums)-3):
p.append([])
for m in p[-2]:
p[-1].append(m+[m[-1]+1])


for i in p:
for j in i:
n = []
for m in j:
if m < len(nums):
n.append(nums[m])
if n not in fnl:
fnl.append(n)


for i in nums:
if [i] not in fnl:
fnl.append([i])


print(fnl)

输出:

[[], [1, 2, 3], [1, 2], [1, 3], [2, 3], [1], [2], [3]]

我在这个话题上有点晚了,但我想我可以帮助别人。

你可以使用productitertools:

from itertools import product


n = [1, 2, 3]


result = product(n, repeat=3) # You can change the repeat more then n length


print(list(result))

输出:

[(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 1), (1, 2, 2), (1, 2, 3), (1, 3, 1),
(1, 3, 2), (1, 3, 3), (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 2, 1), (2, 2, 2),
(2, 2, 3), (2, 3, 1), (2, 3, 2), (2, 3, 3), (3, 1, 1), (3, 1, 2), (3, 1, 3),
(3, 2, 1), (3, 2, 2), (3, 2, 3), (3, 3, 1), (3, 3, 2), (3, 3, 3)]

另一个例子,但是改变了repeat参数:

from itertools import product


n = [1, 2, 3]


result = product(n, repeat=4) # Changing repeat to 4
print(list(result))

输出:

(1, 1, 2, 3), (1, 1, 3, 1), (1, 1, 3, 2), (1, 1, 3, 3), (1, 2, 1, 1),
(1, 2, 1, 2), (1, 2, 1, 3), (1, 2, 2, 1), (1, 2, 2, 2), (1, 2, 2, 3),
(1, 2, 3, 1), (1, 2, 3, 2), (1, 2, 3, 3), (1, 3, 1, 1), (1, 3, 1, 2),
(1, 3, 1, 3), (1, 3, 2, 1), (1, 3, 2, 2), (1, 3, 2, 3), (1, 3, 3, 1),
(1, 3, 3, 2), (1, 3, 3, 3), (2, 1, 1, 1), (2, 1, 1, 2), (2, 1, 1, 3),
(2, 1, 2, 1), (2, 1, 2, 2), (2, 1, 2, 3), (2, 1, 3, 1), (2, 1, 3, 2),
(2, 1, 3, 3), (2, 2, 1, 1), (2, 2, 1, 2), (2, 2, 1, 3), (2, 2, 2, 1),
(2, 2, 2, 2), (2, 2, 2, 3), (2, 2, 3, 1), (2, 2, 3, 2), (2, 2, 3, 3),
(2, 3, 1, 1), (2, 3, 1, 2), (2, 3, 1, 3), (2, 3, 2, 1), (2, 3, 2, 2),
(2, 3, 2, 3), (2, 3, 3, 1), (2, 3, 3, 2), (2, 3, 3, 3), (3, 1, 1, 1),
(3, 1, 1, 2), (3, 1, 1, 3), (3, 1, 2, 1), (3, 1, 2, 2), (3, 1, 2, 3),
(3, 1, 3, 1), (3, 1, 3, 2), (3, 1, 3, 3), (3, 2, 1, 1), (3, 2, 1, 2),
(3, 2, 1, 3), (3, 2, 2, 1), (3, 2, 2, 2), (3, 2, 2, 3), (3, 2, 3, 1),
(3, 2, 3, 2), (3, 2, 3, 3), (3, 3, 1, 1), (3, 3, 1, 2), (3, 3, 1, 3),
(3, 3, 2, 1), (3, 3, 2, 2), (3, 3, 2, 3), (3, 3, 3, 1), (3, 3, 3, 2),
(3, 3, 3, 3)]```

正如James Brady所提到的,itertools.combinations就是关键。但这并不是一个完整的解决方案。

解决方案1

import itertools
def all(lst):
# ci is a bitmask which denotes particular combination,
# see explanation below
for ci in range(1, 2**len(lst)):
yield tuple(itertools.compress(
lst,
[ci & (1<<k) for k in  range(0, len(lst))]
))

解决方案2

import itertools
def all_combs(lst):
for r in range(1, len(lst)+1):
for comb in itertools.combinations(lst, r):
yield comb

例子

>>> list(all_combs([1,2,3]))
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
>>> len(list(all_combs([1,2,3])))
7
>>> len(list(all_combs(range(0, 15))))
32767
>>> list(all([1,2,3]))
[(1,), (2,), (1, 2), (3,), (1, 3), (2, 3), (1, 2, 3)]
>>> len(list(all(range(15))))
32767

解释

假设数组一个的长度为N。让一个长度为N的位掩码B表示一个特定的组合C。如果B[我]是1,那么(我) 属于C的组合。

方案1说明

所以我们可以遍历所有位掩码,用这个位掩码过滤源数组一个,这可能是由itertools.compress完成的。

方案2说明

...或者,我们可以将它表示为组合

现在我们需要考虑一些情况,当B中只有一个,然后只有两个,等等。每个案例都属于特定的结合。 因此,一旦我们组合所有的组合集,我们将得到所有的子序列

此外,很明显,在这种情况下,所有可能的组合的数量是2^ n - 1。当所有B[我]都是0时,我们省略大小写,因为我们假设空集不是一个组合。否则,就不要减去1。

我喜欢这个问题,因为有很多方法来实现它。我决定为未来创造一个参考答案。

在生产中使用什么?

intertools的文档有一个独立的例子,为什么不在你的代码中使用它呢?有些人建议使用more_itertools.powerset,但它有完全相同的实现!如果我是你,我不会为一个小东西安装整个软件包。也许这是最好的方法:

import itertools


def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return itertools.chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

其他可能的方法

方法0:使用组合

import itertools


def subsets(nums):
result = []
for i in range(len(nums) + 1):
result += itertools.combinations(nums, i)
return result

方法1:简单的递归

def subsets(nums):
result = []


def powerset(alist, index, curr):
if index == len(alist):
result.append(curr)
return


powerset(alist, index + 1, curr + [alist[index]])
powerset(alist, index + 1, curr)


powerset(nums, 0, [])
return result

方法2:回溯

def subsets(nums):
result = []


def backtrack(index, curr, k):
if len(curr) == k:
result.append(list(curr))
return
for i in range(index, len(nums)):
curr.append(nums[i])
backtrack(i + 1, curr, k)
curr.pop()


for k in range(len(nums) + 1):
backtrack(0, [], k)
return result

def subsets(nums):
result = []


def dfs(nums, index, path, result):
result.append(path)
for i in range(index, len(nums)):
dfs(nums, i + 1, path + [nums[i]], result)


dfs(nums, 0, [], result)
return result

方法3:位掩码

def subsets(nums):
res = []
n = len(nums)
for i in range(1 << n):
aset = []
for j in range(n):
value = (1 << j) & i  # value = (i >> j) & 1
if value:
aset.append(nums[j])
res.append(aset)
return res

或者(不是位掩码,直觉上是2^n个子集)

def subsets(nums):
subsets = []
expected_subsets = 2 ** len(nums)


def generate_subset(subset, nums):
if len(subsets) >= expected_subsets:
return
if len(subsets) < expected_subsets:
subsets.append(subset)
for i in range(len(nums)):
generate_subset(subset + [nums[i]], nums[i + 1:])


generate_subset([], nums)
return subsets

方法4:级联

def subsets(nums):
result = [[]]
for i in range(len(nums)):
for j in range(len(result)):
subset = list(result[j])
subset.append(nums[i])
result.append(subset)
return result