是否存在只有递归解决方案的问题?
是否可以将每个递归转换为迭代?
“必要的”递归在命令式语言中的用途
是否存在一种只有递归解决方案的问题,即存在一个递归解决方案,但尚未找到迭代解决方案,或者更好的是,已被证明不存在(显然,这不是尾递归)?
是否存在一种只有递归解决方案的问题,即存在一个递归解决方案,但尚未找到迭代解决方案,或者更好的是,已被证明不存在(显然,这不是尾递归)?
用将函数调用替换为将参数推入堆栈,用返回弹出堆栈的方式来代替递归调用,就可以消除递归。
编辑:回应“使用堆栈不会减少空间成本”
如果一个递归算法可以在常量空间内运行,那么它可以以尾递归的方式编写。如果它以尾递归格式编写,那么任何合理的编译器都可以折叠堆栈。然而,这意味着“将函数调用转换为显式堆栈推送”方法也需要常量空间。以阶乘为例。
阶乘:
def fact_rec(n):
' Textbook Factorial function '
if n < 2: return 1
else: return n * f(n-1)
def f(n, product=1):
' Tail-recursive factorial function '
if n < 2: return product
else: return f(n-1, product*n)
def f(n):
' explicit stack -- otherwise same as tail-recursive function '
stack, product = [n], 1
while len(stack):
n = stack.pop()
if n < 2: pass
else:
stack.append(n-1)
product *= n
return product
由于在循环中,stack.pop() 接着 stack.append() 进行,因此堆栈中从未有超过一个项目,因此它满足常量空间要求。如果您想象一下使用临时变量而不是1长度堆栈,它将成为您的标准迭代阶乘算法。
当然,存在无法以尾递归格式编写的递归函数。您仍然可以使用某种类型的堆栈转换为迭代格式,但如果没有关于空间复杂度的保证,我会感到惊讶。
回应阿克曼函数答案, 这是一个很简单的将调用栈转换成真正的堆栈问题。这也展示了迭代版本的一个好处。
在我的平台上(Python 3.1rc2/Vista32),迭代版本可以正确计算ack(3,7)=1021,而递归版本则导致栈溢出。注意:在另一台不同机器上的python 2.6.2 /Vista64上没有发生栈溢出,所以似乎非常依赖于平台。
(社区wiki,因为这实际上是对另一个答案的评论[如果只有注释支持代码格式...])
def ack(m,n):
s = [m]
while len(s):
m = s.pop()
if m == 0:
n += 1
elif n == 0:
s.append(m-1)
n = 1
else:
s.append(m-1)
s.append(m)
n -= 1
return n
所有非NP完全问题都可以仅使用顺序、决策和迭代来解决。虽然递归通常会极大地简化问题,但不应该需要它。
问题的解决取决于需要编写多少行代码...
使用递归和不使用递归列出C:\上的所有文件。当然,两种方法都可以实现,但一种方法会更简单易懂且易于调试。
递归本质上只是一个堆栈,通过显式实现一个堆栈,你可以实现相同的结果。
这可能并不是特别令人满意的答案,但你需要提出更加具体的问题才能获得更好的答案。例如,理论指出,在计算机的级别中,如果你只有 while 循环而没有传统的 for 循环,那么可以解决的问题范围会有很大的差异。
我在其中加入了“传统”这个词,因为它们真正意味着迭代一定次数的循环,而 C 风格的 for (...;...;...) 循环则是 while 循环的伪装。