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

Lir*_*evi 14 algorithm recursion

可能重复:
是否存在仅具有递归解决方案的问题?
每次递归都可以转换成迭代吗?
命令式语言中"递归"的"必要"用法

是否有一个只有一个递归解决方案,那就是,有一个递归解决问题的问题,而是一个迭代的解决方案还没有被发现,或者更好的是,已被证明是不存在的(显然,这不是一个尾递归)?

Jim*_*mmy 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
Run Code Online (Sandbox Code Playgroud)

因为stack.pop()跟在循环中的stack.append()之后,堆栈中永远不会有多个项目,因此它满足了常量空间要求.如果您想象使用临时变量而不是1长度堆栈,它将成为您的标准迭代因子算法.

当然,有递归函数不能以尾递归格式编写.您仍然可以使用某种堆栈转换为迭代格式,但如果对空间复杂性有任何保证,我会感到惊讶.

  • "保证使用有限数量的内存"不是非递归函数的定义.它可能是一个理想的属性,但它不是定义要求.具有内存泄漏的迭代算法将使用可能无限量的内存,但没有人会认为单独使算法递归. (3认同)
  • 这不是递归消除.当用非递归函数替换递归函数时,应该使用有限的内存而不是理论上无限的堆栈. (2认同)
  • @ jia3ep:这是递归消除.事实上,它已经在某些情况下通过将负载从操作系统的调用堆栈(在许多架构中具有有限的空间)移动到堆中并避免函数调用的开销而具有实际的好处. (2认同)

Jim*_*mmy 10

在响应Ackermann函数答案时,这是一个非常直接的转换 - 调用堆栈 - 实际堆栈问题.这也显示了迭代版本的一个好处.

在我的平台上(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
Run Code Online (Sandbox Code Playgroud)


LB4*_*B40 9

阿克曼函数不能没有递归表达

  • 计数器:"Ackermann函数也可以使用Conway链式箭头符号表示非递归"(参见维基百科) (13认同)
  • 我认为区分"数学"定义涉及递归形式的问题是很重要的......其计算只能递归地实现.您可以使用非递归编程形式计算Ackermann函数 - 但是您将_will_为每次迭代存储状态. (5认同)
  • 有趣.Ackermann函数不是原始的递归函数:http://en.wikipedia.org/wiki/Primitive_recursive_function.根据这个页面,具有基本的arithemetic,比较和有界for循环的语言不能表达这种类型的功能.但是,如果你添加WHILE或GOTO,你将再次完成Turing-complete. (3认同)
  • 请参阅我在http://stackoverflow.com/questions/1094679/is-there-a-problem-that-has-only-a-recursive-solution/1095538#1095538的回复 (2认同)

Eri*_*ika 8

你可以定义一个没有递归的图灵机(对吧?)因此语言不需要递归才能完成图灵.


LBu*_*kin 6

在编程中,递归实际上是迭代的一种特殊情况 - 您可以使用调用堆栈作为存储状态的特殊方法.您可以将任何递归方法重写为迭代方法.它可能更复杂或更不优雅,但它是等价的.

在数学中,有一些问题需要递归技术来得出答案 - 一些例子是找到根(牛顿方法),计算素数,图优化等.但是,即使在这里,也只是一个问题,你如何区分术语"迭代"和"递归".

编辑:正如其他人所指出的,存在许多函数,其定义是递归的 - 例如.在阿克曼功能.但是,这并不意味着它们无法使用迭代结构进行计算 - 只要您拥有图灵完整操作集和无限内存.