我的問題來自河內的一個變種,它有四座塔樓。Python 3.3如何將此遞歸函數轉換爲純循環版本?
我知道this article其中說,在C++中,你可以將任何遞歸函數轉換爲循環,但我只熟悉Python。我試圖讀取十條規則,但關鍵字struct
和stack
意味着什麼?
因此,任何與上面的C++類似的文章或討論python也非常值得讚賞。謝謝。
原始遞歸函數是fmove
(保持另一遞歸函數tmove
),接收的整數,返回對的元組。這是優雅的,但沒用(嘗試tmove(100)
並小心你的記憶)。
我想將它轉換爲純循環版本,所以即使n變得像100或1000一樣大,我仍然可以知道元組的前10對或100對是什麼。
def memory(function):
"""
This is a decorator to help raw recursion
functions to avoid repetitive calculation.
"""
cache = {}
def memofunc(*nkw,**kw):
key=str(nkw)+str(kw)
if key not in cache:
cache[key] = function(*nkw,**kw)
return cache[key]
return memofunc
@memory
def tmove(n, a=0, b=1, c=2):
"int n -> a tuple of pairs"
if n==1:
return ((a,c),)
return tmove(n-1,a,c,b)+\
((a,c),)+\
tmove(n-1,b,a,c)
@memory
def fmove(n,a=0,b=1,c=2,d=3):
"int n -> a tuple of pairs"
if n==1:
return ((a,d),)
return min(
(
fmove(n-i,a,d,b,c) +
tmove(i,a,b,d) +
fmove(n-i,c,b,a,d)
for i in range(1,n)
),
key=len,)
隨着user2357112的this question的幫助下,我知道如何遞歸函數轉換像tmove
- 回報復發(...)+缺點或另一個呼叫+ RECUR(...),但是當情況變得像fmove
更復雜,我不知道如何設計結構, - i
與n
有關,它在不同的堆棧中是不同的,最後你必須使用min
來獲得最小尺寸的元組作爲正確的輸出。當前的堆棧。
這是我嘗試(核心算法best(n)
仍然是遞歸函數):
@memory
def _best(n):
if n==1:
return 1,1
return min(
(
(i, 2*(_best(n-i)[1])+2**i-1)
for i in range(1,n)
),
key=lambda x:x[1],
)
def best(n):
return _best(n)[0]
def xtmove(n,a=0,b=1,c=2):
stack = [(True,n,a,b,c)]
while stack:
tag,n,a,b,c = stack.pop()
if n==1:
yield a,c
elif tag:
stack.append((False,n,a,b,c))
stack.append((True,n-1,a,c,b))
else:
yield a,c
stack.append((True,n-1,b,a,c))
def xfmove(n,a=0,b=1,c=2,d=3):
stack = [(True,n,a,b,c,d)]
while stack:
is_four,n,a,b,c,d = stack.pop()
if n==1 and is_four:
yield a,d
elif is_four:
# here I use a none-tail-recursion function 'best'
# to get the best i, so the core is still not explicit stack.
i = best(n)
stack.append((True,n-i,c,b,a,d))
stack.append((False,i,a,b,d,None))
stack.append((True,n-i,a,d,b,c))
else:
for t in xtmove(n,a,b,c):
yield t
這是測試代碼。確保你可以通過它。
if __name__=='__main__':
MAX_TEST_NUM = 20
is_passed = all((
fmove(test_num) == tuple(xfmove(test_num))
for test_num in range(1,MAX_TEST_NUM)
))
assert is_passed, "Doesn't pass the test."
print("Pass the test!")
除非你正在轉換某些使用尾遞歸的東西,否則你最終只會用堆棧和循環來模擬遞歸。你很少需要這樣做,所以這只是一個練習,看看你是否可以做到這一點?如果是這樣,我會用你知道的語言來做,首先,或者將遞歸版本移植到C++。 –
IMO您需要的是一種動態編程方法,您可以在其中從下向上構建DP矩陣。這可能會或可能不會顯着降低您的內存使用量,具體取決於是否可以丟棄不再需要的某些DP矩陣條目。你可以谷歌的遞歸與DP方法來解決問題。至於你關於C++ struct的問題,它類似於Python類(我認爲,我沒有太多熟悉Python) –
@David Ehrmann如果你製作一個Python版本,這將會很棒:)但這不是一個練習,但是,這是來自練習。我對目前的解決方案並不滿意。我對它感興趣。我也覺得,如果我知道一些通用技能來隱藏遞歸函數來循環或像Python那樣在C++文章中產生循環,那麼我解決複雜問題的能力就會上升。 – tcpiper