所有复杂列表的组合

时间:2014-09-04 12:05:05

标签: python combinatorics

我想找到以下列表的所有可能组合:

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

4 个答案:

答案 0 :(得分: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)]

答案 1 :(得分:3)

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

答案 2 :(得分:3)

这是单行,但很难遵循:

from itertools import product

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

首先将data映射到结果中可以成为的列表​​。然后拿product*来获得所有可能性。最后,将每个组合展平为sum

答案 3 :(得分:0)

我的解决方案与John Zwinck's answer基本相同。生成所有组合的列表后

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

comb的每个元素生成所有可能的正/负组合。我这样做是通过迭代组合的总数2**(N-1),并将其视为二进制数,其中每个二进制数字代表一个元素的符号。 (例如,一个双元素列表将有4种可能的组合,0到3,由0b00 => (+,+)0b01 => (-,+)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))