可能重复:
Is there a problem that has only a recursive solution?
Can every recursion be converted into iteration?
“Necessary” Uses of Recursion in Imperative Languages
是否存在仅具有递归解决方案的问题,即具有递归解决方案的问题,但尚未发现或更好的迭代解决方案已被证明不存在(显然,这不是尾递归?)
答案 0 :(得分:18)
通过将参数推送到堆栈来替换函数调用,并从弹出堆栈返回,并且您已经消除了递归。
编辑:以回应“使用堆栈不会降低空间成本”
如果递归算法可以在常量空间中运行,则可以以尾递归方式编写。如果它是以尾递归格式编写的,那么任何体面的编译器都可以崩溃堆栈。但是,这意味着“转换函数调用显式堆栈推送”方法也会占用恒定空间。举个例子,让我们考虑因子。
阶乘:
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长度堆栈,它将成为您的标准迭代因子算法。
当然,有些递归函数无法以尾递归格式编写。您仍然可以使用某种堆栈转换为迭代格式,但如果对空间复杂性有任何保证,我会感到惊讶。
答案 1 :(得分:9)
没有递归就无法表达Ackermann function
答案 2 :(得分:9)
在响应Ackermann function answer时,这是一个非常简单的转换 - 调用堆栈 - 实际堆栈问题。这也显示了迭代版本的一个好处。
在我的平台上(Python 3.1rc2 / Vista32),迭代版本计算ack(3,7)= 1021罚款,而递归版本stackoverflows。注意:它在另一台机器上的python 2.6.2 / Vista64上没有stackoverflow,所以它似乎与平台有关,
(社区维基,因为这实际上是对另一个答案的评论[如果只有评论支持代码格式化....])
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
答案 3 :(得分:8)
你可以定义一个没有递归的Turing Machine(对吗?)因此语言不需要递归Turing-complete。
答案 4 :(得分:6)
在编程中,递归实际上是迭代的一种特殊情况 - 您可以使用调用堆栈作为存储状态的特殊方法。您可以将任何递归方法重写为迭代方法。它可能更复杂或更不优雅,但它是等价的。
在数学中,有些问题需要递归技术来得出答案 - 一些例子是找到根(Newton's Method),计算素数,图优化等。但是,即使在这里,也只是如何区分术语“迭代”和“递归”。
编辑:正如其他人所指出的,存在许多定义为递归的函数 - 例如。 Ackermann function。但是,这并不意味着它们无法使用迭代结构进行计算 - 只要您拥有图灵完整操作集和无限内存。答案 5 :(得分:4)
所有非np完全问题都可以通过序列,决策和迭代来解决。不应该要求递归,尽管它通常会大大简化问题。
答案 6 :(得分:3)
归结为解决问题需要多少行代码......
使用递归然后不使用递归列出C:\上的所有文件。当然,你可以两种方式做到 - 但是一种方法可以更容易理解和调试。
答案 7 :(得分:0)
没有。递归只不过是一个堆栈,您可以通过显式实现堆栈来实现相同的结果。
这可能不是一个特别令人满意的答案,但你必须提出一个更具体的问题才能得到更好的答案。例如,理论规定,在计算水平上,如果你有一个while循环而只有(传统的)for循环,那么可以解决的问题范围就会有很大差异。
我把“传统”放在那里,因为它们的意思是循环迭代了一定次数,而(......; ...; ...)循环的C风格是伪装的循环。