多个列表的Python组合

5
有没有一种Pythonic的方法可以在多个列表之间生成组合?(类似于笛卡尔积但更复杂)
例如:
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
# ...
# there are more than 3 lists

预期输出:

1. [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
2. [(1, 4, 8), (2, 5, 7), (3, 6, 9)]
3. [(1, 4, 9), (2, 5, 7), (3, 6, 8)]
4. [(1, 5, 7), (2, 4, 8), (3, 6, 9)]
5. ...

更新:

谢谢您的快速回复~!!

为了澄清问题:

结果是列表a、b、c的笛卡尔积的所有非重复组合。

还可以用另一种不太好看的方法完成:

1)生成笛卡尔积的整个列表

from itertools import product, combinations, chain
t = list(product(a, b, c))

2) 使用组合生成所有可能的结果

p = list(combinations(t, 3))

3) 过滤重复的条件

cnt = len(list(chain(a, b, c)))
f = [x for x in p if len(set(chain(*x))) == cnt]

更新2:

通过不太优美的方法生成的预期结果:

((1, 4, 7), (2, 5, 8), (3, 6, 9))
((1, 4, 7), (2, 5, 9), (3, 6, 8))
((1, 4, 7), (2, 6, 8), (3, 5, 9))
((1, 4, 7), (2, 6, 9), (3, 5, 8))
((1, 4, 8), (2, 5, 7), (3, 6, 9))
((1, 4, 8), (2, 5, 9), (3, 6, 7))
((1, 4, 8), (2, 6, 7), (3, 5, 9))
((1, 4, 8), (2, 6, 9), (3, 5, 7))
((1, 4, 9), (2, 5, 7), (3, 6, 8))
((1, 4, 9), (2, 5, 8), (3, 6, 7))
((1, 4, 9), (2, 6, 7), (3, 5, 8))
((1, 4, 9), (2, 6, 8), (3, 5, 7))
((1, 5, 7), (2, 4, 8), (3, 6, 9))
((1, 5, 7), (2, 4, 9), (3, 6, 8))
((1, 5, 7), (2, 6, 8), (3, 4, 9))
((1, 5, 7), (2, 6, 9), (3, 4, 8))
((1, 5, 8), (2, 4, 7), (3, 6, 9))
((1, 5, 8), (2, 4, 9), (3, 6, 7))
((1, 5, 8), (2, 6, 7), (3, 4, 9))
((1, 5, 8), (2, 6, 9), (3, 4, 7))
((1, 5, 9), (2, 4, 7), (3, 6, 8))
((1, 5, 9), (2, 4, 8), (3, 6, 7))
((1, 5, 9), (2, 6, 7), (3, 4, 8))
((1, 5, 9), (2, 6, 8), (3, 4, 7))
((1, 6, 7), (2, 4, 8), (3, 5, 9))
((1, 6, 7), (2, 4, 9), (3, 5, 8))
((1, 6, 7), (2, 5, 8), (3, 4, 9))
((1, 6, 7), (2, 5, 9), (3, 4, 8))
((1, 6, 8), (2, 4, 7), (3, 5, 9))
((1, 6, 8), (2, 4, 9), (3, 5, 7))
((1, 6, 8), (2, 5, 7), (3, 4, 9))
((1, 6, 8), (2, 5, 9), (3, 4, 7))
((1, 6, 9), (2, 4, 7), (3, 5, 8))
((1, 6, 9), (2, 4, 8), (3, 5, 7))
((1, 6, 9), (2, 5, 7), (3, 4, 8))
((1, 6, 9), (2, 5, 8), (3, 4, 7))

1
为什么没有[(1, 4, 8), (2, 5, 9), (3, 6, 7)] - Moses Koledoye
1
你的问题不是很清楚。对于那个输入数据,你期望有多少行输出?如果你想要a、b和c的每个排列的笛卡尔积,那将会产生216行结果。 - PM 2Ring
1
我已经根据@PM2Ring的建议编写了代码。如果是这种情况,请重新措辞问题。 - Antti Haapala -- Слава Україні
36?因此,实质上您没有对第一个进行排列组合。 - Antti Haapala -- Слава Україні
@PM2Ring,感谢您的建议。它确实减少了RAM的消耗。我正在寻找一种不会生成2925个组合的算法。Antti的算法不会创建大量的组合集,这非常好。但输出结果并不是预期的结果。 - Vincent Li
显示剩余6条评论
2个回答

2
你可以使用itertools中的产品来获取所有组合。
>>> from itertools import product
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = [7, 8, 9]
>>> A = [a,b,c]
>>> prod = list(product(*A))
>>> print(prod)

预期输出:

[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 6, 7), (1, 6, 8), (1, 6, 9), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 6, 7), (2, 6, 8), (2, 6, 9), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 6, 7), (3, 6, 8), (3, 6, 9)]

1
这不像 OP 期望的输出。 - Akavall
如果你在最终结果中添加 list(itertools.permutations(prod, 3)),我认为你就得到了它。 - pylang

2
您需要的不是组合,而是排列。3个元素有6种排列,2个排列集的笛卡尔积有36种。PM 2Ring最初怀疑您想要这三个元素的所有排列,因为您的问题没有说明。如果您的问题中的代码产生了所需的输出,则意味着您想要对b和c进行排列,但不包括a。最初我编写了计算a、b和c所有排列的代码。然而,由于a不需要被排列,我们只需将其包装在列表中即可。这让我们非常接近所需的输出:
import itertools as it

a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]

for i in it.product([tuple(a)], it.permutations(b), it.permutations(c)):
    print(i)

输出结果为36行,起始标记为
((1, 2, 3), (4, 5, 6), (7, 8, 9))
((1, 2, 3), (4, 5, 6), (7, 9, 8))
((1, 2, 3), (4, 5, 6), (8, 7, 9))

这个格式已经接近你想要的格式,但索引被转置了,所以o[x][y]会匹配你想要的输出中的o[y][x]。我们使用一些zip技巧来进行转置。另外,这个函数现在可以处理任意数量的参数:

import itertools as it

a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]

def funnyperms(first, *rest):
    for i in it.product([first], *(it.permutations(j) for j in rest)):
        yield tuple(zip(*i))

for i in funnyperms(a, b, c):
    print(i)

输出结果为:
((1, 4, 7), (2, 5, 8), (3, 6, 9))
((1, 4, 7), (2, 5, 9), (3, 6, 8))
((1, 4, 8), (2, 5, 7), (3, 6, 9))
((1, 4, 8), (2, 5, 9), (3, 6, 7))
((1, 4, 9), (2, 5, 7), (3, 6, 8))
((1, 4, 9), (2, 5, 8), (3, 6, 7))
((1, 4, 7), (2, 6, 8), (3, 5, 9))
((1, 4, 7), (2, 6, 9), (3, 5, 8))
((1, 4, 8), (2, 6, 7), (3, 5, 9))
((1, 4, 8), (2, 6, 9), (3, 5, 7))
((1, 4, 9), (2, 6, 7), (3, 5, 8))
((1, 4, 9), (2, 6, 8), (3, 5, 7))
((1, 5, 7), (2, 4, 8), (3, 6, 9))
((1, 5, 7), (2, 4, 9), (3, 6, 8))
((1, 5, 8), (2, 4, 7), (3, 6, 9))
((1, 5, 8), (2, 4, 9), (3, 6, 7))
((1, 5, 9), (2, 4, 7), (3, 6, 8))
((1, 5, 9), (2, 4, 8), (3, 6, 7))
((1, 5, 7), (2, 6, 8), (3, 4, 9))
((1, 5, 7), (2, 6, 9), (3, 4, 8))
((1, 5, 8), (2, 6, 7), (3, 4, 9))
((1, 5, 8), (2, 6, 9), (3, 4, 7))
((1, 5, 9), (2, 6, 7), (3, 4, 8))
((1, 5, 9), (2, 6, 8), (3, 4, 7))
((1, 6, 7), (2, 4, 8), (3, 5, 9))
((1, 6, 7), (2, 4, 9), (3, 5, 8))
((1, 6, 8), (2, 4, 7), (3, 5, 9))
((1, 6, 8), (2, 4, 9), (3, 5, 7))
((1, 6, 9), (2, 4, 7), (3, 5, 8))
((1, 6, 9), (2, 4, 8), (3, 5, 7))
((1, 6, 7), (2, 5, 8), (3, 4, 9))
((1, 6, 7), (2, 5, 9), (3, 4, 8))
((1, 6, 8), (2, 5, 7), (3, 4, 9))
((1, 6, 8), (2, 5, 9), (3, 4, 7))
((1, 6, 9), (2, 5, 7), (3, 4, 8))
((1, 6, 9), (2, 5, 8), (3, 4, 7))

将这些内容存储到一个集合中,并与您的方法产生的值进行比较,可以证明它们具有相同的输出结果:
print(set(funnyperms(a, b, c)) == set(f))

打印True,证毕。


谢谢你的回答!这是一个相当不错的方法,但结果不同(输入的元素应该被分成不同的部分)。我已经更新了问题并提供了期望的结果。有没有算法可以产生这个结果? - Vincent Li
3
你的意思是什么?就像我之前所说的,我的第二段代码生成了一组元组,与你提供的完全相同。 - Antti Haapala -- Слава Україні
抱歉,我错过了你的第二段代码。这个结果正是我想要的。太棒了!非常感谢你的回答! - Vincent Li

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接