如何生成列表的所有排列?例如:
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]
如何生成列表的所有排列?例如:
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]
当前回答
这是初始排序后生成排列的渐近最优方式O(n*n!)。
有n个!最多进行一次置换,且具有下一次置换(..),以O(n)时间复杂度运行
在3个步骤中,
找到最大的j,使a[j]可以增加以最小可行量增加a[j]找到扩展新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}")
其他回答
递归之美:
>>> 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']
这是受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
对于性能,一个由Knuth启发的numpy解决方案(第22页):
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, :]
复制大量内存可节省时间-它比列表(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
该算法是最有效的算法,它避免了递归调用中的数组传递和操作,适用于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)
这是初始排序后生成排列的渐近最优方式O(n*n!)。
有n个!最多进行一次置换,且具有下一次置换(..),以O(n)时间复杂度运行
在3个步骤中,
找到最大的j,使a[j]可以增加以最小可行量增加a[j]找到扩展新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}")