如何生成列表的所有排列?例如:
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}")
其他回答
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)
这是初始排序后生成排列的渐近最优方式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}")
为了节省您可能的搜索和实验时间,下面是Python中的非递归置换解决方案,它也适用于Numba(从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
因此,只有在必须从njit函数调用它时才使用此版本,否则更倾向于itertools实现。
以下代码是给定列表的就地排列,作为生成器实现。由于它只返回对列表的引用,因此不应在生成器外部修改列表。该解决方案是非递归的,因此使用了低内存。还可以很好地处理输入列表中元素的多个副本。
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
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']