2014-09-04 55 views
5

我想找到下面的列表中的所有可能的组合:复杂列表的所有组合

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

我知道这看起来很简单的任务,它可以通过类似下面的代码来实现:

comb = [c for i in range(1, len(data)+1) for c in combinations(data, i)] 

但我想要的是实际上给列表数据的每个元素提供两种可能性的方法('a''-a')。

组合的一个例子可以是['a','b']['-a','b']['a','b','-c']等 无需像当然['-a','a']的下面的情况。

回答

5

您可以编写一个生成器函数,它接受一个序列并生成每个可能的否定组合。就像这样:

import itertools 
def negations(seq): 
    for prefixes in itertools.product(["", "-"], repeat=len(seq)): 
     yield [prefix + value for prefix, value in zip(prefixes, seq)] 

print list(negations(["a", "b", "c"])) 

结果(修改为清楚起见空格):

[ 
    [ 'a', 'b', 'c'], 
    [ 'a', 'b', '-c'], 
    [ 'a', '-b', 'c'], 
    [ 'a', '-b', '-c'], 
    ['-a', 'b', 'c'], 
    ['-a', 'b', '-c'], 
    ['-a', '-b', 'c'], 
    ['-a', '-b', '-c'] 
] 

可以将此融入的东西你现有的代码一样

comb = [x for i in range(1, len(data)+1) for c in combinations(data, i) for x in negations(c)] 
+0

非常感谢,正是我想要的:) – Ophilia 2014-09-04 12:35:07

3

一旦您生成了常规组合,您可以通过第二遍来生成具有“否定”的组合。我想它就像一个二进制数字,列表中的元素数量是位数。通过0b0001,0b0010等从0b0000到0b1111进行计数,并且无论设置了哪个位,都将结果中的该元素取反。对于长度为n的每个输入组合,这将产生2^n个组合。

+0

感谢提示,这真的有帮助 – Ophilia 2014-09-04 12:31:38

0

我的解决方案主要有同样的想法如John Zwinck's answer。当你已经产生你生成的comb每个元素的所有可能的正/负组合所有组合

comb = [c for i in range(1, len(data)+1) for c in combinations(data, i)] 

名单。我通过遍历组合的总数2**(N-1)并将其视为二进制数来完成此操作,其中每个二进制数字表示一个元素的符号。 (例如,两个元素的列表将具有4个可能的组合,0〜3,通过0b00 => (+,+),​​,0b10 => (+,-)0b11 => (-,-)表示。)

def twocombinations(it): 
    sign = lambda c, i: "-" if c & 2**i else "" 
    l = list(it) 

    if len(l) < 1: 
     return 

    # for each possible combination, make a tuple with the appropriate 
    # sign before each element 
    for c in range(2**(len(l) - 1)): 
     yield tuple(sign(c, i) + el for i, el in enumerate(l)) 

现在我们应用此功能的comb每个元素和扁平所得嵌套迭代器:

l = itertools.chain.from_iterable(map(twocombinations, comb)) 
3

这里是一个班轮,但它可以是难以遵循:

from itertools import product 

comb = [sum(t, []) for t in product(*[([x], ['-' + x], []) for x in data])] 

第一张地图data列出他们可以成为结果的列表。然后以product*获得所有可能性。最后,用sum平铺每个组合。