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

How do I generate all permutations of a list?

提问人:Ricardo Reyes 提问时间:9/20/2008 最后编辑:Mateen UlhaqRicardo Reyes 更新时间:9/10/2023 访问量:1065604

问:

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

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]
Python 算法 排列 组合学

评论


答:

24赞 Ricardo Reyes 9/20/2008 #1

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

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
748赞 Eli Bendersky 9/20/2008 #2

使用标准库中的 itertools.permutations

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

这里改编下来的是如何实现的演示:itertools.permutations

def permutations(elements):
    if len(elements) <= 1:
        yield elements
        return
    for perm in permutations(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:]

的文档中列出了几种替代方法。这是其中之一: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

另一个,基于: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)

评论

27赞 Boris Gorelik 5/27/2009
如果置换列表足够大,则此解决方案和其他递归解决方案存在占用所有 RAM 的潜在危险
6赞 dbr 6/9/2009
它们也达到了递归极限(并死了),列表很大
74赞 Jagtesh Chadha 5/2/2011
bgbg, dbr:它使用生成器,所以函数本身不会占用内存。如何使用 all_perms 返回的迭代器(假设您可以将每次迭代写入磁盘,而不用担心内存)。我知道这篇文章很旧,但我写这篇文章是为了现在阅读它的每个人的利益。同样现在,最好的方法是使用许多人指出的 itertools.permutations()。
23赞 cdunn2001 7/20/2011
不仅仅是发电机。它使用嵌套生成器,每个生成器都会在调用堆栈中向上返回前一个生成器,以防不清楚。它使用 O(n) 内存,这很好。
2赞 Eric O. Lebigot 5/29/2012
PS:我修复了它,而不是.事实上,单挑的元素可以处于不同的位置,在结果中,而不是 .for i in range(len(elements))for i in range(len(elements)+1)elements[0:1]len(elements)len(elements)+1
372赞 Brian 9/20/2008 #3

对于 Python 2.6 及更高版本:

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

这将作为生成器返回。用于作为列表返回。list(permutations(xs))

评论

30赞 toto_tico 8/24/2017
请注意,存在一个参数,例如 ,它将生成所有可能的排列,选择 2 个元素:ritertools.permutations([1,2,3], r=2)[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
17赞 Ber 9/21/2008 #4

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

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
352赞 Bite code 10/4/2008 #5

一、导入:itertools

import itertools

排列(顺序很重要):

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

组合(顺序无关紧要):

print(list(itertools.combinations('123', 2)))

[('1', '2'), ('1', '3'), ('2', '3')]

笛卡尔积(具有多个可迭代对象):

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

笛卡尔积(具有一个可迭代和自身):

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

评论

5赞 Pramod 1/31/2013
+1!文档链接:docs.python.org/2/library/itertools.html#itertools.permutations
17赞 tzwenn 3/31/2011 #6

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

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
13赞 zmk 8/22/2011 #7
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赞 James 8/22/2011
虽然它在技术上产生了所需的输出,但你正在解决 O(n^n) 中可能是 O(n lg n) 的东西 - 对于大型集合来说“略微”低效。
6赞 Eric O. Lebigot 5/29/2012
@James:我对你给出的 O(n log n) 有点困惑:排列的数量是 n!,它已经比 O(n log n) 大得多;所以,我看不出解决方案怎么可能是 O(n log n)。然而,这个解确实在 O(n^n) 中,它比 n! 大得多,这从斯特林的近似中可以清楚地看出。
65赞 kx2k 10/12/2011 #8
def permutations(head, tail=''):
    if len(head) == 0:
        print(tail)
    else:
        for i in range(len(head)):
            permutations(head[:i] + head[i+1:], tail + head[i])

称为:

permutations('abc')

评论

4赞 bugmenot123 11/27/2017
为什么要打印 tail 然后返回 None?为什么不返回尾巴呢?为什么不退货呢?
0赞 Alex Moore-Niemi 1/3/2021
@bugmenot123您可能想要所有最后的尾巴,而不仅仅是尾巴,这可以通过向函数添加一个参数,在每个函数上附加到它并有一个最终来轻松完成perms=[]printreturn perms
8赞 Eric O. Lebigot 5/29/2012 #9

人们确实可以遍历每个排列的第一个元素,就像 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%,这显然要归功于以 而不是 结尾的递归。 它的内存效率也更高,因为它使用生成器函数(通过),就像在 Riccardo Reyes 的解决方案中一样。len(elements) <= 10yield

40赞 Silveira Neto 8/15/2012 #10
#!/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])

输出:

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

当我交换列表的内容时,它需要一个可变的序列类型作为输入。例如 会起作用,但不会起作用,因为您无法更改字符串。perm(list("ball"))perm("ball")

这个 Python 实现的灵感来自于 Horowitz、Sahni 和 Rajasekeran 在《计算机算法》一书中介绍的算法。

评论

0赞 Konstantinos Monachopoulos 2/25/2019
我假设 k 是长度或排列。当 k = 2 个输出 [1, 2, 3] 时。不应该是(1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3, 2) ??
0赞 sf8193 5/10/2020
k 是要交换的元素的索引
0赞 Pathros 4/5/2021
NameError:未定义名称“xrange”
0赞 mLstudent33 4/24/2021
7 年后,我将如何返回所有置换列表的列表列表?另外,这可以迭代完成吗?
0赞 Aditya 1/27/2022
这可以通过向函数添加 perms=[] 参数、在每次打印时附加到该参数并具有最终返回 perms 来轻松完成
10赞 Chen Xie 1/23/2013 #11

请注意,此算法具有时间复杂度,其中 是输入列表的长度n factorialn

在运行中打印结果:

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]
3赞 Adrian Statescu 5/9/2013 #12
from __future__ import print_function

def perm(n):
    p = []
    for i in range(0,n+1):
        p.append(i)
    while True:
        for i in range(1,n+1):
            print(p[i], end=' ')
        print("")
        i = n - 1
        found = 0
        while (not found and i>0):
            if p[i]<p[i+1]:
                found = 1
            else:
                i = i - 1
        k = n
        while p[i]>p[k]:
            k = k - 1
        aux = p[i]
        p[i] = p[k]
        p[k] = aux
        for j in range(1,(n-i)/2+1):
            aux = p[i+j]
            p[i+j] = p[n-j+1]
            p[n-j+1] = aux
        if not found:
            break

perm(5)
20赞 Paolo 6/30/2013 #13

在功能风格中

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

结果:

[[0, 1, 2], [1, 0, 2], [1, 2, 0], [0, 2, 1], [2, 0, 1], [2, 1, 0]]
3赞 cdiggins 7/6/2013 #14

这是一种算法,它可以在列表上工作,而无需创建新的中间列表,类似于 https://stackoverflow.com/a/108651/184528 处 Ber 的解决方案。

def permute(xs, low=0):
    if low + 1 >= len(xs):
        yield xs
    else:
        for p in permute(xs, low + 1):
            yield p        
        for i in range(low + 1, len(xs)):        
            xs[low], xs[i] = xs[i], xs[low]
            for p in permute(xs, low + 1):
                yield p        
            xs[low], xs[i] = xs[i], xs[low]

for p in permute([1, 2, 3, 4]):
    print p

您可以在此处亲自尝试代码:http://repl.it/J9v

12赞 timeeeee 8/9/2013 #15

我使用了一种基于阶乘数系统的算法 - 对于长度为 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]]

这种方法是非递归的,但它在我的计算机上速度稍慢,并且 xrange 在 n!太大了,无法转换为 C 长整数(对我来说 n=13)。当我需要它时,它就足够了,但它没有 itertools.permutations 的远景。

评论

5赞 Hannele 8/9/2013
您好,欢迎来到 Stack Overflow。尽管发布蛮力方法有其优点,但如果您认为您的解决方案不比公认的解决方案更好,您可能不应该发布它(尤其是在已经有这么多答案的旧问题上)。
7赞 Jay Taylor 7/2/2016
我实际上是在寻找一种暴力非库方法,所以谢谢!
2赞 user3347814 10/11/2020
我发现它也很有用!
7赞 piggybox 11/16/2013 #16

这是受使用列表推导式的 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
4赞 darxtrix 5/19/2014 #17

递归之美:

>>> 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']
3赞 Cmyker 2/1/2015 #18

该算法是最有效的算法,它避免了递归调用中的数组传递和操作,适用于 Python 2、3:

def permute(items):
    length = len(items)
    def inner(ix=[]):
        do_yield = len(ix) == length - 1
        for i in range(0, length):
            if i in ix: #avoid duplicates
                continue
            if do_yield:
                yield tuple([items[y] for y in ix + [i]])
            else:
                for p in inner(ix + [i]):
                    yield p
    return inner()

用法:

for p in permute((1,2,3)):
    print(p)

(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
3赞 manish kumar 5/8/2015 #19
def pzip(c, seq):
    result = []
    for item in seq:
        for i in range(len(item)+1):
            result.append(item[i:]+c+item[:i])
    return result


def perm(line):
    seq = [c for c in line]
    if len(seq) <=1 :
        return seq
    else:
        return pzip(seq[0], perm(seq[1:]))
6赞 B. M. 5/25/2015 #20

为了提高性能,一个受 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, :]

复制大块内存可以节省时间 - 它比以下设备快 20 倍:list(itertools.permutations(range(n))

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
-3赞 Bharatwaja 9/8/2015 #21

对于 Python,我们可以使用 itertools 并导入排列和组合来解决您的问题

from itertools import product, permutations
A = ([1,2,3])
print (list(permutations(sorted(A),2)))
3赞 Miled Louis Rizk 3/19/2016 #22

生成所有可能的排列

我正在使用python3.4:

def calcperm(arr, size):
    result = set([()])
    for dummy_idx in range(size):
        temp = set()
        for dummy_lst in result:
            for dummy_outcome in arr:
                if dummy_outcome not in dummy_lst:
                    new_seq = list(dummy_lst)
                    new_seq.append(dummy_outcome)
                    temp.add(tuple(new_seq))
        result = temp
    return result

测试用例:

lst = [1, 2, 3, 4]
#lst = ["yellow", "magenta", "white", "blue"]
seq = 2
final = calcperm(lst, seq)
print(len(final))
print(final)

评论

0赞 SeaDude 11/10/2021
这(到目前为止)是我(非数学头脑)最容易理解的解决方案。我可以列出我想排列的字符,这很有效!将重复字符添加到排列中的逻辑是什么?示例:其中允许每个排列的重复项数。def calcperm(arr, size, dupes):dupes
2赞 Karo Castro-Wunsch 8/5/2016 #23

我看到这些递归函数内部发生了很多迭代,不完全是纯粹的递归......

因此,对于那些甚至不能遵守单个循环的人来说,这里有一个粗略的、完全不必要的全递归解决方案

def all_insert(x, e, i=0):
    return [x[0:i]+[e]+x[i:]] + all_insert(x,e,i+1) if i<len(x)+1 else []

def for_each(X, e):
    return all_insert(X[0], e) + for_each(X[1:],e) if X else []

def permute(x):
    return [x] if len(x) < 2 else for_each( permute(x[1:]) , x[0])


perms = permute([1,2,3])
1赞 anhldbk 3/25/2017 #24

另一种解决方案:

def permutation(flag, k =1 ):
    N = len(flag)
    for i in xrange(0, N):
        if flag[i] != 0:
            continue
        flag[i] = k 
        if k == N:
            print flag
        permutation(flag, k+1)
        flag[i] = 0

permutation([0, 0, 0])

评论

0赞 Pathros 4/5/2021
NameError:未定义名称“xrange”
0赞 anhldbk 4/6/2021
@Pathros,上面的代码适用于 Python 2。对于 Python 3,请使用 .查看 stackoverflow.com/questions/17192158/...range()
0赞 abelenky 3/2/2018 #25

我的 Python 解决方案:

def permutes(input,offset):
    if( len(input) == offset ):
        return [''.join(input)]

    result=[]        
    for i in range( offset, len(input) ):
         input[offset], input[i] = input[i], input[offset]
         result = result + permutes(input,offset+1)
         input[offset], input[i] = input[i], input[offset]
    return result

# input is a "string"
# return value is a list of strings
def permutations(input):
    return permutes( list(input), 0 )

# Main Program
print( permutations("wxyz") )
0赞 Ilgorbek Kuchkarov 5/13/2018 #26
def permutation(word, first_char=None):
    if word == None or len(word) == 0: return []
    if len(word) == 1: return [word]

    result = []
    first_char = word[0]
    for sub_word in permutation(word[1:], first_char):
        result += insert(first_char, sub_word)
    return sorted(result)

def insert(ch, sub_word):
    arr = [ch + sub_word]
    for i in range(len(sub_word)):
        arr.append(sub_word[i:] + ch + sub_word[:i])
    return arr


assert permutation(None) == []
assert permutation('') == []
assert permutation('1')  == ['1']
assert permutation('12') == ['12', '21']

print permutation('abc')

输出: ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

2赞 Anatoly Alekseev 12/15/2018 #27

为了节省人们可能花费数小时的搜索和实验时间,以下是 Python 中的非递归置换解决方案,它也适用于 Numba(截至 v. 0.41):

@numba.njit()
def permutations(A, k):
    r = [[i for i in range(0)]]
    for i in range(k):
        r = [[a] + b for a in A for b in r if (a in b)==False]
    return r
permutations([1,2,3],3)
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

要给人留下有关性能的印象:

%timeit permutations(np.arange(5),5)

243 µs ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
time: 406 ms

%timeit list(itertools.permutations(np.arange(5),5))
15.9 µs ± 8.61 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
time: 12.9 s

因此,仅当您必须从 njitted 函数调用它时才使用此版本,否则更喜欢 itertools 实现。

0赞 Hello.World 3/9/2019 #28

Counter

from collections import Counter

def permutations(nums):
    ans = [[]]
    cache = Counter(nums)

    for idx, x in enumerate(nums):
        result = []
        for items in ans:
            cache1 = Counter(items)
            for id, n in enumerate(nums):
                if cache[n] != cache1[n] and items + [n] not in result:
                    result.append(items + [n])

        ans = result
    return ans
permutations([1, 2, 2])
> [[1, 2, 2], [2, 1, 2], [2, 2, 1]]

4赞 Tatsu 3/29/2019 #29

另一种方法(没有库)

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

评论

0赞 RK1 2/6/2020
这不适用于带有整数的列表,例如。 返回[1, 2, 3][6, 6, 6, 6, 6, 6]
0赞 Tatsu 2/7/2020
@RK1,你可以试试这个print(permutation(['1','2','3']))
5赞 Richard Ambler 12/21/2019 #30

免责声明:包作者的无耻插件。:)

trotter 包与大多数实现的不同之处在于,它生成的伪列表实际上并不包含排列,而是按顺序描述排列和相应位置之间的映射,从而可以使用非常大的排列“列表”,如本演示所示,该演示在伪列表中执行非常即时的操作和查找,该伪列表“包含”字母表中字母的所有排列, 不使用比典型网页更多的内存或处理。

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

import trotter

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

print(my_permutations)

for p in my_permutations:
    print(p)

输出:

A pseudo-list containing 6 3-permutations of [1, 2, 3].
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[3, 2, 1]
[2, 3, 1]
[2, 1, 3]

评论

0赞 ecdani 6/20/2022
您的包限制在 400 - 500 个元素之间。
1赞 Richard Ambler 6/25/2022
@ecdani 感谢您对这个图书馆的关注!如果银河系中的每个原子都自发地变成了一个新的银河系,并且每个新原子都再次这样做,那么我们仍然不会有那么多的原子,因为有500个元素的排列。话虽如此,如果你稍微提高系统的最大递归级别,该库可以很容易地表示 1,000 个或更多元素的排列,并且搜索和查找仍然非常即时。如果您愿意,请在 trotter 存储库页面上发布问题。
20赞 Maverick Meerkat 1/5/2020 #31

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

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

良率实现:

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]

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

enter image description here

评论

0赞 RK1 2/6/2020
这对我不起作用_> ValueError:操作数无法与形状 (0,) (2,) 一起广播,对于这一行:perms = getPermutations(array[:i] + array[i+1:])
0赞 Maverick Meerkat 2/7/2020
@RK1输入是什么?
0赞 RK1 2/7/2020
我正在传入一个数组_>,我看到它适用于列表,只是因为func参数:)而感到困惑numpygetPermutations(np.array([1, 2, 3]))array
0赞 Maverick Meerkat 2/7/2020
@RK1很高兴它起作用了:-)list 是 Python 中的一个关键字,因此将参数称为关键字通常不是一个好主意,因为它会“隐藏”它。所以我使用数组这个词,因为这是我正在使用的列表的实际功能——它们的数组方式。我想如果我写文档,我会澄清它。此外,我认为基本的“面试”问题应该在没有外部软件包的情况下解决,比如 numpy。
0赞 RK1 2/7/2020
哈哈,这是真的,是的,它试图使用它并且对速度很贪婪,所以试图将它专门用于数组numbanumpy
0赞 Dritte Saskaita 6/4/2020 #32
def permuteArray (arr):

    arraySize = len(arr)

    permutedList = []

    if arraySize == 1:
        return [arr]

    i = 0

    for item in arr:

        for elem in permuteArray(arr[:i] + arr[i + 1:]):
            permutedList.append([item] + elem)

        i = i + 1    

    return permutedList

我打算在新系列中不穷尽所有可能性,以使其具有某种独特性。

2赞 user13415013 7/7/2020 #33

无论如何,我们可以使用sympy库,也支持多集排列

import sympy
from sympy.utilities.iterables import multiset_permutations
t = [1,2,3]
p = list(multiset_permutations(t))
print(p)

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

答案的灵感来自获取 numpy 数组的所有排列

0赞 Harvey Mao 10/22/2020 #34
from typing import List
import time, random

def measure_time(func):
    def wrapper_time(*args, **kwargs):
        start_time = time.perf_counter()
        res = func(*args, **kwargs)
        end_time = time.perf_counter()
        return res, end_time - start_time

    return wrapper_time


class Solution:
    def permute(self, nums: List[int], method: int = 1) -> List[List[int]]:
        perms = []
        perm = []
        if method == 1:
            _, time_perm = self._permute_recur(nums, 0, len(nums) - 1, perms)
        elif method == 2:
            _, time_perm = self._permute_recur_agian(nums, perm, perms)
            print(perm)
        return perms, time_perm

    @measure_time
    def _permute_recur(self, nums: List[int], l: int, r: int, perms: List[List[int]]):
        # base case
        if l == r:
            perms.append(nums.copy())

        for i in range(l, r + 1):
            nums[l], nums[i] = nums[i], nums[l]
            self._permute_recur(nums, l + 1, r , perms)
            nums[l], nums[i] = nums[i], nums[l]

    @measure_time
    def _permute_recur_agian(self, nums: List[int], perm: List[int], perms_list: List[List[int]]):
        """
        The idea is similar to nestedForLoops visualized as a recursion tree.
        """
        if nums:
            for i in range(len(nums)):
                # perm.append(nums[i])  mistake, perm will be filled with all nums's elements.
                # Method1 perm_copy = copy.deepcopy(perm)
                # Method2 add in the parameter list using + (not in place)
                # caveat: list.append is in-place , which is useful for operating on global element perms_list
                # Note that:
                # perms_list pass by reference. shallow copy
                # perm + [nums[i]] pass by value instead of reference.
                self._permute_recur_agian(nums[:i] + nums[i+1:], perm + [nums[i]], perms_list)
        else:
            # Arrive at the last loop, i.e. leaf of the recursion tree.
            perms_list.append(perm)



if __name__ == "__main__":
    array = [random.randint(-10, 10) for _ in range(3)]
    sol = Solution()
    # perms, time_perm = sol.permute(array, 1)
    perms2, time_perm2 = sol.permute(array, 2)
    print(perms2)
    # print(perms, perms2)
    # print(time_perm, time_perm2)
```

评论

0赞 chux - Reinstate Monica 10/22/2020
一些解释会改善这个答案。
0赞 Michael Hodel 12/11/2020 #35

如果有人喜欢这个丑陋的单行字(虽然仅适用于字符串):

def p(a):
    return a if len(a) == 1 else [[a[i], *j] for i in range(len(a)) for j in p(a[:i] + a[i + 1:])]
1赞 Bhaskar13 1/3/2021 #36

这是初始排序后生成排列的渐近最优方式 O(n*n!)。

有 n!最多排列,并且 hasNextPermutation(..) 以 O(n) 时间复杂度运行

分 3 个步骤,

  1. 求最大 j,使得 a[j] 可以增加
  2. 增加最小可行量 a[j]
  3. 找到扩展新 a[0..j] 的词典最少方法
'''
Lexicographic permutation generation

consider example array state of [1,5,6,4,3,2] for sorted [1,2,3,4,5,6]
after 56432(treat as number) ->nothing larger than 6432(using 6,4,3,2) beginning with 5
so 6 is next larger and 2345(least using numbers other than 6)
so [1, 6,2,3,4,5]
'''
def hasNextPermutation(array, len):
    ' Base Condition '
    if(len ==1):
        return False
    '''
    Set j = last-2 and find first j such that a[j] < a[j+1]
    If no such j(j==-1) then we have visited all permutations
    after this step a[j+1]>=..>=a[len-1] and a[j]<a[j+1]

    a[j]=5 or j=1, 6>5>4>3>2
    '''
    j = len -2
    while (j >= 0 and array[j] >= array[j + 1]):
        j= j-1
    if(j==-1):
        return False
    # print(f"After step 2 for j {j}  {array}")
    '''
    decrease l (from n-1 to j) repeatedly until a[j]<a[l]
    Then swap a[j], a[l]
    a[l] is the smallest element > a[j] that can follow a[l]...a[j-1] in permutation
    before swap we have a[j+1]>=..>=a[l-1]>=a[l]>a[j]>=a[l+1]>=..>=a[len-1]
    after swap -> a[j+1]>=..>=a[l-1]>=a[j]>a[l]>=a[l+1]>=..>=a[len-1]

    a[l]=6 or l=2, j=1 just before swap [1, 5, 6, 4, 3, 2] 
    after swap [1, 6, 5, 4, 3, 2] a[l]=5, a[j]=6
    '''
    l = len -1
    while(array[j] >= array[l]):
        l = l-1
    # print(f"After step 3 for l={l}, j={j} before swap {array}")
    array[j], array[l] = array[l], array[j]
    # print(f"After step 3 for l={l} j={j} after swap {array}")
    '''
    Reverse a[j+1...len-1](both inclusive)

    after reversing [1, 6, 2, 3, 4, 5]
    '''
    array[j+1:len] = reversed(array[j+1:len])
    # print(f"After step 4 reversing {array}")
    return True

array = [1,2,4,4,5]
array.sort()
len = len(array)
count =1
print(array)
'''
The algorithm visits every permutation in lexicographic order
generating one by one
'''
while(hasNextPermutation(array, len)):
    print(array)
    count = count +1
# The number of permutations will be n! if no duplicates are present, else less than that
# [1,4,3,3,2] -> 5!/2!=60
print(f"Number of permutations: {count}")


评论

0赞 Chris 4/16/2021
欢迎使用 Stack Overflow。没有任何解释的代码转储很少有用。Stack Overflow 是关于学习的,而不是提供盲目复制和粘贴的片段。请编辑您的问题并解释它如何回答所提出的具体问题。请参阅如何回答。当用现有答案(这个有 40 个)回答旧问题(这个问题超过 12 个)时,这一点尤其重要。这个答案如何改进这里已有的内容?另请注意,这个问题是关于 Python 的。Java 中的答案有什么帮助?
5赞 Alon Barad 8/9/2021 #37

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

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

您可以自己实现 permute 函数

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'}
0赞 0script0 10/1/2021 #38
def permutate(l):
    for i, x in enumerate(l):
        for y in l[i + 1:]:
            yield x, y


if __name__ == '__main__':
    print(list(permutate(list('abcd'))))
    print(list(permutate([1, 2, 3, 4])))

#[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
#[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

评论

0赞 Dominic Zukiewicz 1/6/2022
美丽而简单!
1赞 Jason S 5/14/2022
.....但错了。
0赞 Yilmaz 2/21/2022 #39

用递归求解,遍历元素,取第 i 个元素,然后问自己:“其余项目的排列是什么”,直到没有更多的元素留下。

我在这里解释了解决方案:https://www.youtube.com/watch?v=_7GE7psS2b4

class Solution:
    def permute(self,nums:List[int])->List[List[int]]:
        res=[]
        def dfs(nums,path):
            if len(nums)==0:
                res.append(path)
            for i in range(len(nums)):
                dfs(nums[:i]+nums[i+1:],path+[nums[i]])
        dfs(nums,[])
        return res
0赞 Gorkem Polat 4/3/2022 #40

如果用户希望将所有排列保留在列表中,可以使用以下代码:

def get_permutations(nums, p_list=[], temp_items=[]):
    if not nums:
        return
    elif len(nums) == 1:
        new_items = temp_items+[nums[0]]
        p_list.append(new_items)
        return
    else:
        for i in range(len(nums)):
            temp_nums = nums[:i]+nums[i+1:]
            new_temp_items = temp_items + [nums[i]]
            get_permutations(temp_nums, p_list, new_temp_items)

nums = [1,2,3]
p_list = []

get_permutations(nums, p_list)

1赞 Rohit Sharma 9/10/2023 #41

或者,您也可以旋转


def perm_rotate(elements):
    if len(elements) <= 1:
        yield elements
        return

    for _ in range(len(elements)):
        for perm in perm_rotate(elements[1:]):
            yield [elements[0]] + perm
        elements = rotate(elements)

def rotate(numbers):
    return [numbers[-1]] + numbers[: len(numbers)-1]