![](/img/trans.png)
[英]Generate unique permutations from a multiset using backtracking and in lexicographic order
[英]Unique permutations using backtracking
我試圖在尋找唯一排列的問題中使用回溯。 我寫過這個:
def f(A, start, end):
if start == end - 1:
print(A)
else:
for idx in range(start, end):
if idx != start and A[idx] == A[start]:
continue
A[idx], A[start] = A[start], A[idx]
f(A, start + 1, end)
這個例子有效
A = [2, 3, 2]
f(A, 0, len(A))
[2, 3, 2]
[2, 2, 3]
[3, 2, 2]
這個不行
A = [2, 2, 1, 1]
f(A, 0, len(A))
[2, 2, 1, 1]
[2, 1, 2, 1]
[2, 1, 1, 2]
[2, 2, 1, 1] #unwanted duplicate!
[1, 2, 2, 1]
[1, 2, 1, 2]
[1, 1, 2, 2]
[1, 2, 2, 1]
[1, 2, 1, 2]
[2, 2, 1, 1]
[2, 1, 2, 1]
[2, 1, 1, 2]
[2, 2, 1, 1]
為什么我的結果中仍然有重復項?
在過濾中,您使用一對一檢查。 因此,作為一個結果,但不會從目前的工作有三個以上的元素。
這是因為,您可以在多次(真實)交換后獲得相同的排列。 例如:
[1 ,2(1),2(2),3 ] -> swap 1 with 3
[1 ,3, 2(2),2(1)] -> swap 1 with 2
[1 ,2(2),3 ,2(1)] -> swap 2 with 3
[1 ,2(2),2(1),3 ]
如您所見,排列是相同的(但兩者的起源不同)。 所以我們間接地交換了兩者。
然而,沒有必要讓它變得那么復雜。 有兩種方法可能在這里起作用:
Counter
,然后確保根據計數器發出)。后者將運行得更快,因為它不會生成它必須省略的排列。
一個示例實現可能是:
from collections import Counter
def f(lst):
def g(l,c,n):
if n <= 0:
yield tuple(l)
else:
for k,v in c.items():
if v > 0:
c[k] -= 1
l.append(k)
for cfg in g(l,c,n-1):
yield cfg
l.pop()
c[k] += 1
for cfg in g([],Counter(lst),len(lst)):
yield cfg
這給出:
>>> list(f([1,1,2,2]))
[(1, 1, 2, 2), (1, 2, 1, 2), (1, 2, 2, 1), (2, 1, 1, 2), (2, 1, 2, 1), (2, 2, 1, 1)]
>>> list(f([1,1,2,2,3]))
[(1, 1, 2, 2, 3), (1, 1, 2, 3, 2), (1, 1, 3, 2, 2), (1, 2, 1, 2, 3), (1, 2, 1, 3, 2), (1, 2, 2, 1, 3), (1, 2, 2, 3, 1), (1, 2, 3, 1, 2), (1, 2, 3, 2, 1), (1, 3, 1, 2, 2), (1, 3, 2, 1, 2), (1, 3, 2, 2, 1), (2, 1, 1, 2, 3), (2, 1, 1, 3, 2), (2, 1, 2, 1, 3), (2, 1, 2, 3, 1), (2, 1, 3, 1, 2), (2, 1, 3, 2, 1), (2, 2, 1, 1, 3), (2, 2, 1, 3, 1), (2, 2, 3, 1, 1), (2, 3, 1, 1, 2), (2, 3, 1, 2, 1), (2, 3, 2, 1, 1), (3, 1, 1, 2, 2), (3, 1, 2, 1, 2), (3, 1, 2, 2, 1), (3, 2, 1, 1, 2), (3, 2, 1, 2, 1), (3, 2, 2, 1, 1)]
那么你的輸入數組中有重復的元素。 這可能會導致解決方案中的元素冗余或冗余排列,但如果您在數組中使用了諸如唯一元素之類的輸入,例如...
A = [1,2,3,4...] 等等然后下面的代碼可能會有所幫助
def f(A, start, end):
if start == end - 1:
print(A)
else:
for idx in range(start, end):
if idx != start and A[idx] == A[start]:
continue
A[idx], A[start] = A[start], A[idx]
f(A, start + 1, end)
A[idx], A[start] = A[start], A[idx] #This is added
而這個例子......
A = [1, 2, 3, 4]
f(A, 0, len(A))
輸出是...
[1, 2, 3, 4]
[1, 2, 4, 3]
[1, 3, 2, 4]
[1, 3, 4, 2]
[1, 4, 3, 2]
[1, 4, 2, 3]
[2, 1, 3, 4]
[2, 1, 4, 3]
[2, 3, 1, 4]
[2, 3, 4, 1]
[2, 4, 3, 1]
[2, 4, 1, 3]
[3, 2, 1, 4]
[3, 2, 4, 1]
[3, 1, 2, 4]
[3, 1, 4, 2]
[3, 4, 1, 2]
[3, 4, 2, 1]
[4, 2, 3, 1]
[4, 2, 1, 3]
[4, 3, 2, 1]
[4, 3, 1, 2]
[4, 1, 3, 2]
[4, 1, 2, 3]
希望這對你有幫助:)
這是因為您正在“就地”將開關應用於您的列表。 (即:您在計算排列時正在修改列表 A。)
這是對您的代碼的快速修復:
def f(A, start, end):
if start == end - 1:
print(A)
else:
B = A.copy()
for idx in range(start, end):
if idx != start and B[idx] == B[start]:
continue
B[idx], B[start] = A[start], A[idx]
f(B, start + 1, end)
A = [2, 2, 1, 1]
f(A, 0, len(A))
# [2, 2, 1, 1]
# [2, 1, 2, 1]
# [2, 1, 1, 2]
# [1, 2, 2, 1]
# [1, 2, 1, 2]
# [1, 1, 2, 2]
如果您想避免由於重復數字而導致的重復,您可以先對您的數據進行排序,然后添加一個條件進行交換(僅當元素較大時):
def f_s(A, start, end):
f(sorted(A), start, end)
def f(A, start, end):
if start == end - 1:
print(A)
else:
for idx in range(start, end):
if idx != start and A[idx] == A[start]:
continue
if A[idx] >= A[start]:
A[idx], A[start] = A[start], A[idx]
f(A, start + 1, end)
A = [2, 3, 2]
f_s(A, 0, len(A))
A = [2, 2, 1, 1]
f_s(A, 0, len(A))
輸出:
[2, 2, 3]
[2, 3, 2]
[3, 2, 2]
[1, 1, 2, 2]
[1, 2, 1, 2]
[1, 2, 2, 1]
[2, 1, 2, 1]
[2, 2, 1, 1]
Willem 的答案的輕微變化,利用了收益和對正在發生的事情的解釋。 我們避免枚舉重復元素,但我們仍然對數據進行排序以按字典順序發出排列。
def multiset_permutation(A):
def solve_permutation(depth, counter, permutation):
# base case/goal
if depth == 0:
yield permutation
return
# choices
for key, value in counter.items():
# constraint
if value > 0:
# make a choice
counter[key] -= 1
permutation.append(key)
# explore
yield from [
list(i) for i in solve_permutation(depth - 1, counter, permutation)
]
# backtrack - undo our choices
permutation.pop()
counter[key] += 1
"""
Lexicographical order requires that we sort the list first so that we
incrementally emit the next larger permutation based on the counters
"""
A = sorted(A)
counter = collections.Counter(A)
return list(solve_permutation(len(A), counter, []))
輸出:
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]
調用堆棧來解決[1, 1, 2]
將如下所示:
depth counter permutation
0, {1:0, 2:0}, [1,1,2]
1, {1:0, 2:1}, [1,1]
2, {1:1, 2:1}, [1]
3, {1:2, 2:1}, []
0, {1:0, 2:0}, [1,2,1]
1, {1:0, 2:1}, [1,2]
2, {1:1, 2:1}, [1]
0, {1:0, 2:0}, [2,1,1]
1, {1:0, 2:1}, [2,1]
2, {1:1, 2:1}, [2]
3, {1:2, 2:1}, []
遞歸樹:
[]
/ \
[1] [2]
/ \ |
[1,1] [1,2] [2,1]
/ \ |
[1, 1, 2] [1, 2, 1] [2, 1, 1]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.