[英]Generating permutations with n consecutive repeated elements
我需要生成p
不同字符的所有可能排列,長度為r
,字符串中包含重復字符。 我有一個額外的限制——在每個排列中的任何一點我只能有n
個連續的重復字符。
約束條件: (1 ≤ p, r, n ≤ 12)
例如:對於字母“AB”,r 值為 4,n 值為 2,我想要以下輸出:
AABA
AABB
ABAA
ABAB
ABBA
BAAB
BABA
BABB
BBAA
BBAB
相鄰相同字符的最大數量 n 為 2。
我通過查找字符串的笛卡爾積,然后遍歷結果並消除任何不適合 n 值的內容,獲得了所需的輸出。 不過,我的方法非常慢! 如果需要,這是我的代碼:
s = "AB"
q = 2
r = 4
pools = [s] * r
result = [[]]
final = []
for pool in pools:
result = [x+[y] for x in result for y in pool]
check = [''.join([l]*(q+1))for l in s]
for r in result:
add = True
z = ''.join(r)
for c in check:
if c in z:
add = False
break
if add:
final.append(z)
print(final)
# ['AABA', 'AABB', 'ABAA', 'ABAB', 'ABBA', 'BAAB', 'BABA', 'BABB', 'BBAA', 'BBAB']
我的解決方案適用於上面的輸入,但對於更大的數字/字符串,它需要幾分鍾。 例如,使用我的解決方案,以下輸入需要很長時間:
s = "ABCDEF"
q = 3
r = 10
我想要一個最好不到一秒鍾的解決方案。 我寫了一個更快的遞歸解決方案,但是我的機器因較大數字的內存錯誤而崩潰-所以請不要遞歸解決方案:)謝謝!
把它想象成一個樹遍歷問題。 節點是排列的初始段,每個段都可以以p
種方式或p-1
種方式擴展(取決於最后n
字符是否相同)。
一些未優化的代碼:
from string import ascii_uppercase
def children(s,p,r,n):
chars = ascii_uppercase[:p]
if len(s)==r:
return []
elif len(s) >= n and s[-n:] == s[-1]*n:
return [s+c for c in chars if c != s[-1]]
else:
return [s+c for c in chars]
def extensions(s,p,r,n):
kids = children(s,p,r,n)
if kids == []:
return [s]
else:
perms = []
for kid in kids:
perms.extend(extensions(kid,p,r,n))
return perms
def perms(p,r,n):
return extensions('',p,r,n)
例如,
>>> perms(2,4,2)
['AABA', 'AABB', 'ABAA', 'ABAB', 'ABBA', 'BAAB', 'BABA', 'BABB', 'BBAA', 'BBAB']
perms(6,10,3)
大約需要一分鍾才能生成長度為 10 的ABCDEF
的58792500
個排列,並且沒有超過 3 的重復塊。它是遞歸的,但遞歸的深度以 `r 為界,因此遞歸不應該崩潰(除非列表本身太大而無法保存在內存中)。 您可以研究非遞歸列表遍歷算法並將其編寫為生成器而不是返回列表的東西。
在編輯下面仍然是遞歸的,但它使用生成器,因此內存效率更高:
def yield_leaves(s,chars,r,n):
if len(s) == r:
yield s
else:
avoid = s[-1] if len(s) >= n and s[-n:] == s[-1]*n else ''
for c in chars:
if c != avoid:
yield from yield_leaves(s+c,chars,r,n)
def yield_perms(chars,r,n):
yield from yield_leaves('',chars,r,n)
我更改了界面以使其更接近您的代碼。 例如,
p = list(yield_perms('ABCDEF',10,3))
相當於p = perms(6,10,3)
。 它實際上似乎稍微快了一點。
對於此類問題,您不應排除遞歸(盡管迭代方法通常更快)。 內存問題是實現之一。 遞歸與迭代並不是固有的。
這是一個使用遞歸定義生成器函數的示例:
def permute(letters,size,maxConsec):
if size == 1: yield from letters; return
for s in permute(letters,size-1,maxConsec):
for c in letters:
if size<=maxConsec or s[-1]!=c or not s.endswith(c*maxConsec):
yield s+c
for p in permute("AB",4,2): print(p)
AABA
AABB
ABAA
ABAB
ABBA
BAAB
BABA
BABB
BBAA
BBAB
考慮到要產生的排列的絕對數量,較大的字符串總是需要很長時間。 使用生成器函數的優點之一是不需要將所有中間結果存儲在列表中。 因此,您可以將其用作迭代器來計算值,或者在最后有效地制作一個列表:
print( sum(1 for _ in permute("ABCDEF",10,3)) ) # 15.0 seconds
print( len(list(permute("ABCDEF",10,3))) ) # 16.2 seconds
58,792,500 permutations (out of 60,466,176 possible without constraint)
需要存儲中間結果的迭代解決方案(無遞歸)將增加內存注意事項,但可以提供一些性能增益:
def permute(letters,size,maxConsec):
letterMax = [(c,c*maxConsec) for c in letters]
result = [""]
for sz in range(size):
canRepeat = sz < maxConsec
result = [ s+c for s in result for c,m in letterMax
if canRepeat or s[-1]!=c or not s.endswith(m) ]
return result
print(len(permute("ABCDEF",10,3))) # 12.9 seconds
在任何情況下,生成一個包含 5800 萬個值的列表都不會在一秒鍾內起作用。
list(range(58792500)) # 1.9 seconds
[編輯] 這里是迭代函數的進一步優化版本,如評論和其他改進中所述
from itertools import product
def permute2(letters,size,maxConsec):
result = map("".join,product(*[letters]*maxConsec))
if size == maxConsec: return list(result)
xLetters = { x:[c for c in letters if c != x] for x in letters }
xSuffix = { c*maxConsec for c in letters }
for _ in range(size-maxConsec):
result = [ r+c for r in result
for c in (xLetters[r[-1]] if r[-maxConsec:] in xSuffix else letters) ]
return result
print(len(permute2("ABCDEF",10,3))) # 8.9 seconds
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.