所有的迭代算法都能用递归表达吗?
如果不能,是否存在一个好的反例来展示存在某些迭代算法没有对应的递归算法?
如果所有的迭代算法都能用递归表达,那么是否存在某些情况下更难做到这一点?
编程语言在其中扮演了什么角色?我可以想象Scheme程序员对于迭代(即尾递归)和堆栈使用可能与仅限于Java的程序员有所不同。
所有的迭代算法都能用递归表达吗?
如果不能,是否存在一个好的反例来展示存在某些迭代算法没有对应的递归算法?
如果所有的迭代算法都能用递归表达,那么是否存在某些情况下更难做到这一点?
编程语言在其中扮演了什么角色?我可以想象Scheme程序员对于迭代(即尾递归)和堆栈使用可能与仅限于Java的程序员有所不同。
这个结论有一个简单的即兴证明。因为你可以使用严格迭代结构来构建图灵完备语言,也可以使用仅递归结构的图灵完备语言,所以两者等价。
就像你说的一样,每个迭代方法都可以变成“递归”方法,并且通过尾调用,堆栈不会爆炸。 :-) 实际上,这正是Scheme实现所有常见循环形式的方式。以下是Scheme的示例:
(define (fib n)
(do ((x 0 y)
(y 1 (+ x y))
(i 1 (+ i 1)))
((> i n) x)))
这里,虽然该函数看起来是迭代的,但实际上它在内部使用一个带有三个参数 x
、y
和 i
的 lambda 递归,并在每次迭代时使用新值调用自身。
下面是该函数可能被宏展开的一种方式:
(define (fib n)
(letrec ((inner (lambda (x y i)
(if (> i n) x
(inner y (+ x y) (+ i 1))))))
(inner 0 1 1)))
这样,递归的本质更加直观可见。
将迭代定义为:
function q(vars):
while X:
do Y
可以翻译为:
function q(vars):
if X:
do Y
call q(vars)
在大多数情况下,Y 包括递增由X测试的计数器。当采用递归路径时,必须以某种方式将此变量通过 'vars' 传递。
正如plinth在他们的回答中所指出的那样,我们可以构造证明来展示递归和迭代是等价的,并且都可以用来解决同一个问题;然而,尽管我们知道这两者是等价的,但使用其中一种方法可能会有缺点。
在不针对递归进行优化的语言中,您可能会发现使用迭代的算法比递归的算法更快,同样,在经过优化的语言中,您可能会发现使用另一种语言编写的迭代算法比递归算法运行得更快。此外,可能没有明显的方法来使用递归或迭代编写给定的算法,反之亦然。这可能导致代码难以阅读,从而导致可维护性问题。
Prolog是一种仅支持递归的编程语言,你可以在其中做几乎所有的事情(虽然我不建议你这样做,但你可以尝试:))
递归解决方案通常与迭代解决方案相比,相对低效。 然而,需要注意的是,有些问题只能通过递归来解决,而等效的迭代解决方案可能不存在或者极其复杂难以编写(例如,Ackermann函数无法在没有递归的情况下表达)。虽然递归很优雅,易于编写和理解。