如何在Python中生成列表的所有排列

Ric*_*yes 543 python algorithm permutation combinatorics python-2.5

如何在Python中生成列表的所有排列,与该列表中的元素类型无关?

例如:

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]
Run Code Online (Sandbox Code Playgroud)

Eli*_*sky 436

从Python 2.6开始(如果您使用的是Python 3),您可以使用标准库工具:itertools.permutations.

import itertools
list(itertools.permutations([1, 2, 3]))
Run Code Online (Sandbox Code Playgroud)

如果您出于某种原因使用较旧的Python(<2.6)或者只是想知道它是如何工作的,这里有一个很好的方法,取自 http://code.activestate.com/recipes/252178/:

def all_perms(elements):
    if len(elements) <=1:
        yield elements
    else:
        for perm in all_perms(elements[1:]):
            for i in range(len(elements)):
                # nb elements[0:1] works in both string and list contexts
                yield perm[:i] + elements[0:1] + perm[i:]
Run Code Online (Sandbox Code Playgroud)

文档中列出了几种替代方法itertools.permutations.这是一个:

def permutations(iterable, r=None):
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = range(n)
    cycles = range(n, n-r, -1)
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return
Run Code Online (Sandbox Code Playgroud)

另一个基于itertools.product:

def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)
Run Code Online (Sandbox Code Playgroud)

  • bgbg,dbr:它使用了一个生成器,所以函数本身不会占用内存.它留给你如何使用all_perms返回的迭代器(假设你可以将每次迭代写入磁盘而不用担心内存).我知道这篇文章已经过时但是我正在写这篇文章是为了所有现在阅读它的人的利益.现在,最好的方法是使用许多人指出的itertools.permutations(). (54认同)
  • 不只是*a*发电机.它使用嵌套的生成器,如果不清楚的话,每个生成器都会向调用堆栈中的前一个生成器生成.它使用O(n)内存,这很好. (17认同)
  • 如果排列列表足够大,这个和其他递归解决方案可能会占用所有RAM (13认同)
  • 它们还使用大型列表达到递归限制(并且死亡) (3认同)
  • PS:我修复了它,用`for i in range(len(elements))`而不是`for i in range(len(elements)+1)`。事实上,被挑出的元素`elements[0:1]`可以在`len(elements)`不同的位置,结果,不是`len(elements)+1`。 (2认同)

Bri*_*ian 330

Python 2.6以后:

import itertools
itertools.permutations([1,2,3])
Run Code Online (Sandbox Code Playgroud)

(作为生成器返回.用于list(permutations(l))作为列表返回.)

  • 也适用于Python 3 (13认同)
  • 请注意,存在一个`r`参数,例如`itertools.permutations([1,2,3],r = 2)`,它将生成所有可能的排列,选择2个元素:`[(1,2),(1 ,3),(2,1),(2,3),(3,1),(3,2)]` (7认同)

e-s*_*tis 264

以下代码仅适用于Python 2.6及更高版本

一,进口itertools:

import itertools
Run Code Online (Sandbox Code Playgroud)

排列(订单事宜):

print list(itertools.permutations([1,2,3,4], 2))
[(1, 2), (1, 3), (1, 4),
(2, 1), (2, 3), (2, 4),
(3, 1), (3, 2), (3, 4),
(4, 1), (4, 2), (4, 3)]
Run Code Online (Sandbox Code Playgroud)

组合(顺序无关紧要):

print list(itertools.combinations('123', 2))
[('1', '2'), ('1', '3'), ('2', '3')]
Run Code Online (Sandbox Code Playgroud)

笛卡尔积(有几个迭代):

print list(itertools.product([1,2,3], [4,5,6]))
[(1, 4), (1, 5), (1, 6),
(2, 4), (2, 5), (2, 6),
(3, 4), (3, 5), (3, 6)]
Run Code Online (Sandbox Code Playgroud)

笛卡儿积(一个可迭代的本身):

print list(itertools.product([1,2], repeat=3))
[(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2),
(2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)]
Run Code Online (Sandbox Code Playgroud)

  • +1!文档链接:http://docs.python.org/2/library/itertools.html#itertools.permutations (2认同)
  • @gus print 缺少括号: print(list(itertools.permutations([1,2,3,4], 2))) 我猜这是因为 Python 版本不同。 (2认同)

小智 37

def permutations(head, tail=''):
    if len(head) == 0: print tail
    else:
        for i in range(len(head)):
            permutations(head[0:i] + head[i+1:], tail+head[i])
Run Code Online (Sandbox Code Playgroud)

称为:

permutations('abc')
Run Code Online (Sandbox Code Playgroud)

  • 为什么打印tail然后返回None?为什么不返回 tail 呢?为什么不返回任何东西呢? (3认同)

小智 25

#!/usr/bin/env python

def perm(a, k=0):
   if k == len(a):
      print a
   else:
      for i in xrange(k, len(a)):
         a[k], a[i] = a[i] ,a[k]
         perm(a, k+1)
         a[k], a[i] = a[i], a[k]

perm([1,2,3])
Run Code Online (Sandbox Code Playgroud)

输出:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 2, 1]
[3, 1, 2]
Run Code Online (Sandbox Code Playgroud)

当我交换列表的内容时,需要一个可变序列类型作为输入.例如perm(list("ball")),perm("ball")因为你不能改变字符串,所以不会工作.

这个Python实现的灵感来自Horowitz,Sahni和Rajasekeran的计算机算法一书中提出的算法.


Ric*_*yes 21

此解决方案实现了一个生成器,以避免在内存中保留所有排列:

def permutations (orig_list):
    if not isinstance(orig_list, list):
        orig_list = list(orig_list)

    yield orig_list

    if len(orig_list) == 1:
        return

    for n in sorted(orig_list):
        new_list = orig_list[:]
        pos = new_list.index(n)
        del(new_list[pos])
        new_list.insert(0, n)
        for resto in permutations(new_list[1:]):
            if new_list[:1] + resto <> orig_list:
                yield new_list[:1] + resto
Run Code Online (Sandbox Code Playgroud)


Ber*_*Ber 15

以下代码是给定列表的就地排列,实现为生成器.由于它只返回对列表的引用,因此不应在生成器外修改列表.解决方案是非递归的,因此使用低内存.在输入列表中也可以使用多个元素副本.

def permute_in_place(a):
    a.sort()
    yield list(a)

    if len(a) <= 1:
        return

    first = 0
    last = len(a)
    while 1:
        i = last - 1

        while 1:
            i = i - 1
            if a[i] < a[i+1]:
                j = last - 1
                while not (a[i] < a[j]):
                    j = j - 1
                a[i], a[j] = a[j], a[i] # swap the values
                r = a[i+1:last]
                r.reverse()
                a[i+1:last] = r
                yield list(a)
                break
            if i == first:
                a.reverse()
                return

if __name__ == '__main__':
    for n in range(5):
        for a in permute_in_place(range(1, n+1)):
            print a
        print

    for a in permute_in_place([0, 0, 1, 1, 1]):
        print a
    print
Run Code Online (Sandbox Code Playgroud)


小智 13

在我看来,一个非常明显的方式可能是:

def permutList(l):
    if not l:
            return [[]]
    res = []
    for e in l:
            temp = l[:]
            temp.remove(e)
            res.extend([[e] + r for r in permutList(temp)])

    return res
Run Code Online (Sandbox Code Playgroud)


小智 12

在功能风格

def addperm(x,l):
    return [ l[0:i] + [x] + l[i:]  for i in range(len(l)+1) ]

def perm(l):
    if len(l) == 0:
        return [[]]
    return [x for y in perm(l[1:]) for x in addperm(l[0],y) ]

print perm([ i for i in range(3)])
Run Code Online (Sandbox Code Playgroud)

结果:

[[0, 1, 2], [1, 0, 2], [1, 2, 0], [0, 2, 1], [2, 0, 1], [2, 1, 0]]
Run Code Online (Sandbox Code Playgroud)


zmk*_*zmk 10

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
Run Code Online (Sandbox Code Playgroud)

输出:

[
    [1, 2.0, 'three'], 
    [1, 'three', 2.0], 
    [2.0, 1, 'three'], 
    [2.0, 'three', 1], 
    ['three', 1, 2.0], 
    ['three', 2.0, 1]
]
Run Code Online (Sandbox Code Playgroud)

  • @James:我对你给出的O(n log n)感到有点困惑:排列的数量是n !,它已经远大于O(n log n); 所以,我看不出解决方案是如何O(n log n).然而,这个解决方案确实在O(n ^ n)中,它比n!大得多,从斯特林的近似可以清楚地看出. (3认同)
  • 虽然它在技术上产生了所需的输出,但你正在解决O(n ^ n)中可能为O(n lg n)的问题 - 对于大型集合而言,"略微"低效. (2认同)

Dav*_*eli 10

常规实现(无产量 - 将在内存中执行所有操作):

def getPermutations(array):
    if len(array) == 1:
        return [array]
    permutations = []
    for i in range(len(array)): 
        # get all perm's of subarray w/o current item
        perms = getPermutations(array[:i] + array[i+1:])  
        for p in perms:
            permutations.append([array[i], *p])
    return permutations
Run Code Online (Sandbox Code Playgroud)

产量实施:

def getPermutations(array):
    if len(array) == 1:
        yield array
    else:
        for i in range(len(array)):
            perms = getPermutations(array[:i] + array[i+1:])
            for p in perms:
                yield [array[i], *p]
Run Code Online (Sandbox Code Playgroud)

基本思想是遍历数组中第 1 个位置的所有元素,然后在第 2 个位置遍历所有其余元素,而没有为第一个选择的元素,依此类推。您可以使用recursion来做到这一点,其中停止条件正在获取一个包含 1 个元素的数组 - 在这种情况下,您将返回该数组。

在此处输入图片说明


小智 8

我使用了基于阶乘数系统的算法- 对于长度为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))
Run Code Online (Sandbox Code Playgroud)

输出:

[[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
Run Code Online (Sandbox Code Playgroud)

这个方法是非递归的,但它在我的计算机上稍慢,xrange在n时引发错误!太大而无法转换为C长整数(对我来说n = 13).当我需要它时它已经足够了,但它不是一个很长的镜头的itertools.permutations.

  • 我实际上正在寻找一种蛮力的非库方法,所以谢谢! (4认同)
  • 嗨,欢迎来到Stack Overflow.虽然发布暴力方法有其优点,但如果你认为你的解决方案不比公认的解决方案更好,你可能不应该发布它(特别是在已经有这么多答案的旧问题上). (3认同)
  • 我发现它也很有用! (2认同)

Eri*_*got 7

人们确实可以迭代每个排列的第一个元素,如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
Run Code Online (Sandbox Code Playgroud)

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


Che*_*Xie 7

请注意,此算法具有n factorial时间复杂度,其中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()    
Run Code Online (Sandbox Code Playgroud)

例:

permutation([1,2,3])
Run Code Online (Sandbox Code Playgroud)

输出:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
Run Code Online (Sandbox Code Playgroud)


B. *_* M. 6

对于性能,一个受Knuth启发的 numpy 解决方案,(p22):

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, :]
Run Code Online (Sandbox Code Playgroud)

复制大块内存可以节省时间 - 它比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
Run Code Online (Sandbox Code Playgroud)


pig*_*box 5

这是受使用列表理解的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
Run Code Online (Sandbox Code Playgroud)


Ric*_*ler 5

免责声明:包作者的无形插头。:)

猪手包是从大多数实现不同,它产生不实际包含的排列,而是描述的排列和各位置之间的映射关系的顺序,使其能够工作,排列非常大名单“,如图所示伪名单在这个演示中,它在一个“包含”字母表中所有字母排列的伪列表中执行非常即时的操作和查找,而不使用比典型网页更多的内存或处理。

无论如何,要生成排列列表,我们可以执行以下操作。

import trotter

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

print(my_permutations)

for p in my_permutations:
    print(p)
Run Code Online (Sandbox Code Playgroud)

输出:

包含 [1, 2, 3] 的 6 个 3 排列的伪列表。
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]


Alo*_*rad 5

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

import itertools
list(itertools.permutations([1, 2, 3]))
Run Code Online (Sandbox Code Playgroud)

你可以自己实现排列函数

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'}
Run Code Online (Sandbox Code Playgroud)