【发布时间】:2014-02-24 03:38:45
【问题描述】:
我的问题来自河内的一个变体,它有四个塔。
我知道this article 上面写着在 c++ 中您可以将任何递归函数转换为循环,但我只熟悉 Python。我试图阅读这十条规则,但是关键字struct 和stack 对python 意味着什么?
因此,非常感谢任何与上述 C++ 类似的有关 Python 的文章或讨论。谢谢。
原始递归函数是fmove(包含另一个递归函数tmove),接收一个整数,返回一个对的元组。它很优雅但没用(试试tmove(100) 并注意你的记忆)。
我想将其转换为纯 yield 循环版本,因此即使 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,)
在this question 中的user2357112 的帮助下,我知道如何转换像tmove 这样的递归函数——返回recur(...)+ CONS 或另一个调用+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 矩阵条目。您可以在谷歌上搜索递归 vs DP 方法来解决问题。至于你关于 C++ struct 的问题,它类似于 Python 类(我想,我对 Python 不太熟悉)
-
@David Ehrmann 如果您制作 Python 版本,那就太好了 :)。这不是练习,但是,这是来自练习。我对我目前的解决方案不满意。我对此很感兴趣。我也觉得,如果我知道一些通用技巧,可以像那篇 C++ 文章那样在 Python 中将递归函数转换为循环或产生循环,我解决复杂问题的能力就会提高。
-
@Abhishek Bansal 谢谢,我会看到的。
标签: python c++ algorithm recursion