关于python:如何获取列表元素的所有可能组合?

How to get all possible combinations of a list’s elements?

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

我发现了一些代码(通过谷歌搜索),它们显然可以满足我的需求,但是我发现代码相当不透明,并且对使用它很谨慎。另外,我觉得必须有一个更优雅的解决方案。

对我来说,唯一发生的事情就是循环遍历十进制整数1–32768,然后将其转换为二进制,并使用二进制表示作为过滤器来选择合适的数字。

有人知道更好的方法吗?可能使用map()


这个答案遗漏了一个方面:操作要求所有组合…不仅仅是长度"r"的组合。

所以你要么把所有长度的"L"都圈起来:

1
2
3
4
5
6
import itertools

stuff = [1, 2, 3]
for L in range(0, len(stuff)+1):
    for subset in itertools.combinations(stuff, L):
        print(subset)

或者——如果你想变得时髦(或者让在你之后阅读你的代码的人的大脑弯曲)——你可以生成"combinations()"生成器链,然后迭代:

1
2
3
4
5
6
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:

1
itertools.combinations(iterable, r)

Return r length subsequences of elements from
the input iterable.

Combinations are emitted in lexicographic sort order. So, if the
input iterable is sorted, the
combination tuples will be produced in
sorted order.

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


这里有一个懒惰的一行程序,也使用itertools:

1
2
3
4
5
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"对应的所有元素。

1
2
3
4
5
6
7
8
9
10
11
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())),如果您需要它是可哈希的)

演示

1
2
3
4
5
>>> 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高度乐观的回答下的评论中,提到了itertools文件中的powerset()配方,包括dan自己的配方。然而,到目前为止,还没有人将其作为答案发布。因为它可能是解决问题最好的方法之一,如果不是最好的方法,并且得到了另一位评论者的一点鼓励,下面显示了它。函数生成每个长度的列表元素的所有唯一组合(包括那些包含零和所有元素的组合)。

注:如果目标是仅获得唯一元素的组合,则将行s = list(iterable)更改为s = list(set(iterable)),以消除任何重复元素。不管怎样,iterable最终变成了list,这意味着它将与发电机一起工作(与其他几个答案不同)。

1
2
3
4
5
6
7
8
9
10
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))

输出:

1
2
3
4
5
6
7
8
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)


下面是一个使用递归的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> 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

1
2
3
4
5
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

1
2
3
4
5
from itertools import combinations

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

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

输出将是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[[],
 ['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


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

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

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


您可以使用这个简单的代码在python中生成列表的所有组合

1
2
3
4
5
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
5
[()]
[(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或任何其他额外库的情况下寻求答案的用户添加这个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

简单的产量发生器用途:

1
2
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.combinations函数,但是这里我们使用双列表理解(而不是for循环或sum):

1
2
def combs(x):
    return [c for i in range(len(x)+1) for c in combinations(x,i)]

演示:

1
2
3
4
5
6
>>> 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)]

它可以用itertools来完成。

对于排列

此方法将列表作为输入,并返回以列表形式包含长度l排列的元组的对象列表。

1
2
3
4
5
6
7
8
9
10
11
# A Python program to print all  
# permutations of given length
from itertools import permutations

# Get all permutations of length 2
# and length 2
perm = permutations([1, 2, 3], 2)

# Print the obtained permutations
for i in list(perm):
    print (i)

结合使用

此方法将一个列表和一个输入r作为输入,并返回一个包含列表形式中所有可能长度r组合的元组的对象列表。

1
2
3
4
5
6
7
8
9
10
11
# A Python program to print all  
# combinations of given length
from itertools import combinations

# Get all combinations of [1, 2, 3]
# and length 2
comb = combinations([1, 2, 3], 2)

# Print the obtained combinations
for i in list(comb):
    print (i)

看到这个


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

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

1
2
3
4
5
6
7
def combs(xs, i=0):
    if i==len(xs):
        yield ()
        return
    for c in combs(xs,i+1):
        yield c
        yield c+(xs[i],)

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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,没有yield,没有清单理解):

1
2
3
4
5
6
7
8
9
10
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获得所有的组合要实际得多,但是如果你希望这样做的话,如果你想编写很多代码的话,你只需要理解列表就可以实现这一点。

对于两对组合:

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

< BR>而且,对于三对组合,就这么简单:

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.combinations相同:

1
2
3
4
5
6
7
8
9
10
11
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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']))

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 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()


这个怎么样……使用了字符串而不是列表,但相同的是..字符串可以像python中的列表一样处理:

1
2
3
4
5
6
7
8
9
10
11
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的组合

1
2
3
4
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))

谢谢


如果没有Python3中的itertools,您可以这样做:

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

其中,最初的carry ="".


这是我的实现

1
2
3
4
5
6
7
8
9
10
11
12
    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.combinations的两个实现

返回列表的

1
2
3
4
5
6
7
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

一个返回一个生成器

1
2
3
4
5
6
7
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参数是静态的,不会随每个调用而更改。

1
2
3
4
5
6
7
8
9
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]]

这是一个很肤浅的情况,但最好安全些,不要后悔。


使用列表理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 )

输出为:

1
2
3
4
5
6
['A', 'A']
['A', 'B']
['A', 'C']
['B', 'B']
['B', 'C']
['C', 'C']


如果有人在找一个颠倒的名单,就像我一样:

1
2
3
4
5
6
7
8
9
10
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 = 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