是否存在只有递归解决方案的问题?

时间:2009-07-07 20:29:17

标签: algorithm recursion

  

可能重复:
  Is there a problem that has only a recursive solution?
  Can every recursion be converted into iteration?
  “Necessary” Uses of Recursion in Imperative Languages

是否存在仅具有递归解决方案的问题,即具有递归解决方案的问题,但尚未发现或更好的迭代解决方案已被证明不存在(显然,这不是尾递归?)

8 个答案:

答案 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风格是伪装的循环。