如何生成列表的所有排列?

841

如何生成列表的所有排列组合?例如:

permutations([])
[]

permutations([1])
[1]

permutations([1, 2])
[1, 2]
[2, 1]

permutations([1, 2, 3])
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
41个回答

13
list2Perm = [1, 2.0, 'three']
listPerm = [[a, b, c]
            for a in list2Perm
            for b in list2Perm
            for c in list2Perm
            if ( a != b and b != c and a != c )
            ]
print listPerm

输出:

[
    [1, 2.0, 'three'], 
    [1, 'three', 2.0], 
    [2.0, 1, 'three'], 
    [2.0, 'three', 1], 
    ['three', 1, 2.0], 
    ['three', 2.0, 1]
]

4
虽然技术上可以产生所需的输出,但你正在用O(n^n)的算法解决一个本应该用O(n lg n)算法解决的问题 - 对于大数据集来说效率“稍微”低下。 - James
6
@James: 我有点困惑你提到的O(n log n): 排列的数量是n!,已经比O(n log n)大得多了,所以我看不出一个解决方案如何能够是O(n log n)。然而,确实可以证明这个解法复杂度为O(n^n),这远远大于n!,这一点从Stirling近似可以清楚地看出。 - Eric O. Lebigot

12

我使用了基于阶乘数系统的算法- 对于长度为n的列表,您可以逐项组合每个排列,从每个阶段剩下的项目中进行选择。 对于第一个项目,您有n个选择,对于第二个项目,您有n-1个选择,而对于最后一个项目,只有一个选择,因此您可以使用阶乘数系统中数字作为索引。 这样,数字0到n!-1对应于按字典顺序排列的所有可能的排列。

from math import factorial
def permutations(l):
    permutations=[]
    length=len(l)
    for x in xrange(factorial(length)):
        available=list(l)
        newPermutation=[]
        for radix in xrange(length, 0, -1):
            placeValue=factorial(radix-1)
            index=x/placeValue
            newPermutation.append(available.pop(index))
            x-=index*placeValue
        permutations.append(newPermutation)
    return permutations

permutations(range(3))

输出:

[[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]

这种方法是非递归的,但在我的电脑上速度稍慢,并且当n!太大无法转换为C长整型时(n=13),xrange会引发一个错误。 当我需要时,这已经足够了,但与itertools.permutations相比还有很长的路要走。


5
你好,欢迎来到Stack Overflow。虽然粗暴的方法在某些情况下也有其优点,但如果你认为自己的解决方案不比已被接受的解决方案更好,那么最好不要发表它(尤其是在已经有很多答案的老问题上)。 - Hannele
7
实际上我寻找的是一种非库函数的暴力解决方案,感谢! - Jay Taylor
2
我也觉得很有用! - user3347814

10
请注意,此算法的时间复杂度为 n 阶乘,其中 n 是输入列表的长度。
在运行中打印结果:
global result
result = [] 

def permutation(li):
if li == [] or li == None:
    return

if len(li) == 1:
    result.append(li[0])
    print result
    result.pop()
    return

for i in range(0,len(li)):
    result.append(li[i])
    permutation(li[:i] + li[i+1:])
    result.pop()    

例子:

permutation([1,2,3])

输出:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

8

可以按照tzwenn的答案对每个排列的第一个元素进行迭代。然而,以下方法更为高效:

def all_perms(elements):
    if len(elements) <= 1:
        yield elements  # Only permutation possible = no permutation
    else:
        # Iteration over the first element in the result permutation:
        for (index, first_elmt) in enumerate(elements):
            other_elmts = elements[:index]+elements[index+1:]
            for permutation in all_perms(other_elmts): 
                yield [first_elmt] + permutation

这个解决方案快了大约30%,显然是因为递归在len(elements) <= 1处结束,而不是0。它还更加高效地使用内存,因为它使用生成器函数(通过yield),就像Riccardo Reyes的解决方案一样。


7
这是受Haskell实现使用列表推导式的启发:
def permutation(list):
    if len(list) == 0:
        return [[]]
    else:
        return [[x] + ys for x in list for ys in permutation(delete(list, x))]

def delete(list, item):
    lc = list[:]
    lc.remove(item)
    return lc

6

为了提高性能,受Knuth(p22)启发,我们可以使用numpy来解决问题:

from numpy import empty, uint8
from math import factorial

def perms(n):
    f = 1
    p = empty((2*n-1, factorial(n)), uint8)
    for i in range(n):
        p[i, :f] = i
        p[i+1:2*i+1, :f] = p[:i, :f]  # constitution de blocs
        for j in range(i):
            p[:i+1, f*(j+1):f*(j+2)] = p[j+1:j+i+2, :f]  # copie de blocs
        f = f*(i+1)
    return p[:n, :]

复制大块内存可以节省时间——比起list(itertools.permutations(range(n)),它快20倍:

In [1]: %timeit -n10 list(permutations(range(10)))
10 loops, best of 3: 815 ms per loop

In [2]: %timeit -n100 perms(10) 
100 loops, best of 3: 40 ms per loop

5

如果您不想使用内置方法,例如:

import itertools
list(itertools.permutations([1, 2, 3]))

你可以自己实现排列函数

from collections.abc import Iterable


def permute(iterable: Iterable[str]) -> set[str]:
    perms = set()

    if len(iterable) == 1:
        return {*iterable}

    for index, char in enumerate(iterable):
        perms.update([char + perm for perm in permute(iterable[:index] + iterable[index + 1:])])

    return perms


if __name__ == '__main__':
    print(permute('abc'))
    # {'bca', 'abc', 'cab', 'acb', 'cba', 'bac'}
    print(permute(['1', '2', '3']))
    # {'123', '312', '132', '321', '213', '231'}

5

免责声明:包作者不厚颜无耻地自荐。:)

trotter 包不同于大部分实现,它生成的假列表并不包含排列,而是描述了排列与在排序中相应位置之间的映射关系,使得可以处理非常大的“列表”排列。如 此演示文稿 所示,可以在一个假列表中执行快速操作和查找,该列表“包含”字母表中所有排列,而不使用比典型网页更多的内存或处理。

无论如何,要生成一个排列列表,我们可以按照以下步骤进行。

import trotter

my_permutations = trotter.Permutations(3, [1, 2, 3])

print(my_permutations)

for p in my_permutations:
    print(p)

输出:

一个包含[1,2,3]的伪列表,其中有6个3元排列。
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]

您的包裹限制在400至500个元素之间。 - ecdani
@ecdani 感谢您对这个库的关注!如果银河系中的每个原子都自发地变成一个新的银河系,并且每个新原子再次这样做,我们仍然没有像500个元素的排列那么多的原子。话虽如此,如果您将系统的最大递归级别提高一点,该库可以轻松表示1,000个或更多元素的排列,并且搜索和查找仍然非常即时。如果您愿意,请在trotter存储库页面上发布问题。 - Richard Ambler

4

另一种方法(不需要库)

def permutation(input):
    if len(input) == 1:
        return input if isinstance(input, list) else [input]

    result = []
    for i in range(len(input)):
        first = input[i]
        rest = input[:i] + input[i + 1:]
        rest_permutation = permutation(rest)
        for p in rest_permutation:
            result.append(first + p)
    return result

输入可以是字符串或列表

print(permutation('abcd'))
print(permutation(['a', 'b', 'c', 'd']))

这对于整数列表不起作用,例如 [1, 2, 3] 返回 [6, 6, 6, 6, 6, 6] - RK1
@RK1,你可以尝试这个代码 print(permutation(['1','2','3'])) - Tatsu

4

递归的美妙之处:

>>> import copy
>>> def perm(prefix,rest):
...      for e in rest:
...              new_rest=copy.copy(rest)
...              new_prefix=copy.copy(prefix)
...              new_prefix.append(e)
...              new_rest.remove(e)
...              if len(new_rest) == 0:
...                      print new_prefix + new_rest
...                      continue
...              perm(new_prefix,new_rest)
... 
>>> perm([],['a','b','c','d'])
['a', 'b', 'c', 'd']
['a', 'b', 'd', 'c']
['a', 'c', 'b', 'd']
['a', 'c', 'd', 'b']
['a', 'd', 'b', 'c']
['a', 'd', 'c', 'b']
['b', 'a', 'c', 'd']
['b', 'a', 'd', 'c']
['b', 'c', 'a', 'd']
['b', 'c', 'd', 'a']
['b', 'd', 'a', 'c']
['b', 'd', 'c', 'a']
['c', 'a', 'b', 'd']
['c', 'a', 'd', 'b']
['c', 'b', 'a', 'd']
['c', 'b', 'd', 'a']
['c', 'd', 'a', 'b']
['c', 'd', 'b', 'a']
['d', 'a', 'b', 'c']
['d', 'a', 'c', 'b']
['d', 'b', 'a', 'c']
['d', 'b', 'c', 'a']
['d', 'c', 'a', 'b']
['d', 'c', 'b', 'a']

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