使用itertools 为每个重复键构建排列的笛卡尔积:
代码
from itertools import chain, permutations, groupby, product
from operator import itemgetter
def all_sorts(numbers, letters):
return [list(map(itemgetter(1), chain.from_iterable(p))) for p in product(*(permutations(g) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))))]
print( all_sorts([1,2,3,1,2,1], 'abcdef') )
# [['a', 'd', 'f', 'b', 'e', 'c'], ['a', 'd', 'f', 'e', 'b', 'c'], ['a', 'f', 'd', 'b', 'e', 'c'], ['a', 'f', 'd', 'e', 'b', 'c'], ['d', 'a', 'f', 'b', 'e', 'c'], ['d', 'a', 'f', 'e', 'b', 'c'], ['d', 'f', 'a', 'b', 'e', 'c'], ['d', 'f', 'a', 'e', 'b', 'c'], ['f', 'a', 'd', 'b', 'e', 'c'], ['f', 'a', 'd', 'e', 'b', 'c'], ['f', 'd', 'a', 'b', 'e', 'c'], ['f', 'd', 'a', 'e', 'b', 'c']]
这种方法是最佳的,因为它直接生成解决方案,而不是从庞大的候选列表中过滤它们。对于给定的大小为 6 的示例列表,它仅生成 12 个解决方案,而不是过滤大小为 6 的列表的所有 720 个排列。
工作原理:
首先,我们使用 sorted 和 itertools.groupby 按键进行排序和分组。注意operator.itemgetter(0) 与lambda t: t[0] 相同。
>>> [list(g) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))]
[[(1, 'a'), (1, 'd'), (1, 'f')],
[(2, 'b'), (2, 'e')],
[(3, 'c')]]
然后我们生成每个键的可能排列,在每个组上使用itertools.permutation。
>>> [list(permutations(g)) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))]
[[((1, 'a'), (1, 'd'), (1, 'f')), ((1, 'a'), (1, 'f'), (1, 'd')), ((1, 'd'), (1, 'a'), (1, 'f')), ((1, 'd'), (1, 'f'), (1, 'a')), ((1, 'f'), (1, 'a'), (1, 'd')), ((1, 'f'), (1, 'd'), (1, 'a'))],
[((2, 'b'), (2, 'e')), ((2, 'e'), (2, 'b'))],
[((3, 'c'),)]]
然后我们使用itertools.product 构建这些排列列表的笛卡尔积;我们从笛卡尔积中的每个元组重建一个列表,使用itertools.chain 连接。最后我们“取消装饰”,丢弃键并只保留字母,我用map(itemgetter(1), ...) 做的,但也可以用列表理解[t[1] for t in ...] 做同样的事情。
>>> [list(map(itemgetter(1), chain.from_iterable(p))) for p in product(*(permutations(g) for _,g in groupby(sorted(zip(numbers, letters)), key=itemgetter(0))))]
[['a', 'd', 'f', 'b', 'e', 'c'], ['a', 'd', 'f', 'e', 'b', 'c'], ['a', 'f', 'd', 'b', 'e', 'c'], ['a', 'f', 'd', 'e', 'b', 'c'], ['d', 'a', 'f', 'b', 'e', 'c'], ['d', 'a', 'f', 'e', 'b', 'c'], ['d', 'f', 'a', 'b', 'e', 'c'], ['d', 'f', 'a', 'e', 'b', 'c'], ['f', 'a', 'd', 'b', 'e', 'c'], ['f', 'a', 'd', 'e', 'b', 'c'], ['f', 'd', 'a', 'b', 'e', 'c'], ['f', 'd', 'a', 'e', 'b', 'c']]