Мой вопрос исходит из варианта Ханоя, который имеет четыре башни.
Я знаю эту статью, в которой говорится, что в c++ вы можете преобразовать любую рекурсивную функцию в цикл, но я знаком с Python. Я попытался прочесть десять правил, но каковы ключевые слова struct
и stack
для python?
Таким образом, любая статья или обсуждение для python, которая аналогична приведенной выше c++, также очень оценена. Благодарю.
Необработанная рекурсивная функция 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 в этом вопросе я знаю, как конвертировать рекурсивные функции, такие как tmove
- return tmove
(...) + CONS или другой вызов +recur (...), но когда ситуации становятся более сложными, как fmove
, Я не знаю, как создать структуру, - i
имеет отношение к n
который отличается в другом стеке, и вы, наконец, должны использовать min
чтобы получить минимальный размер кортежа как правильный вывод для текущего стека.
Это моя попытка (best(n)
алгоритм ядра 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!")
fmove
выполняет min
над всеми значениями своих рекурсивных вызовов и вызовом tmove
поэтому в этом случае нет потоковой передачи результатов. Вам нужно 100% вызовов завершить, чтобы получить результат min
.
Что касается подхода к стеку, он создает минимальный интерпретатор с двумя опкодами True и False. :)
Посмотрите, как tmove может передавать результаты без повторения архаичных методов, необходимых на языках без генераторов.
from itertools import chain
def xtmove(n, a=0, b=1, c=2):
"int n -> a tuple of pairs"
if n==1:
yield (a,c)
else:
for i in chain(xtmove(n-1,a,c,b), [(a,c)], xtmove(n-1,b,a,c)):
yield i
После нескольких дней обучения, с помощью статьи c++, я, наконец, получил версию с чистым циклом. И я думаю, что @Javier прав - невозможно уступить.
def best(n):
"""
n -> best_cut_number
four-towers Hanoi best cut number for n disks.
"""
stacks = [(0,n,[],None,)] #(stg,n,possible,choice)
cache={1:(1,0)}
while stacks:
stg,n,possible,choice=stacks.pop()
if n in cache:
res = cache[n]
elif stg==0:
stacks.append((1,n,possible,n-1))
stacks.append((0,1,[],None))
else:
value = 2*res[0] + 2**choice-1
possible.append((value,choice))
if choice > 1:
stacks.append((1,n,possible,choice-1))
stacks.append((0,n-choice+1,[],None))
else:
res = min(possible,key=lambda x:x[0])
cache[n] = res
best_cut_number = res[1]
return best_cut_number