Algorithm 递归程序优化

Algorithm 递归程序优化,algorithm,recursion,Algorithm,Recursion,如何编写一个函数countTo(n),该函数从1计数到n,并在不使用显式循环(仅递归)的情况下打印每个数字 给定任意大的n,即使没有尾部调用优化,解决方案在空间和时间上也必须是渐近最优的 注:最佳时间复杂度为O(1),而最佳空间复杂度为O(logn)–即使在迭代情况下也是如此,因为需要打印(任意大的)数字 问题来源于这里的讨论,相关细节取自那里的讨论(否则问题就无法回答,因为他们的原始陈述做出了误导性的假设)。使用迭代而不是递归 def countTo(n): for i in rang

如何编写一个函数
countTo(n)
,该函数从1计数到n,并在不使用显式循环(仅递归)的情况下打印每个数字

给定任意大的
n
,即使没有尾部调用优化,解决方案在空间和时间上也必须是渐近最优的

注:最佳时间复杂度为O(1),而最佳空间复杂度为O(logn)–即使在迭代情况下也是如此,因为需要打印(任意大的)数字


问题来源于这里的讨论,相关细节取自那里的讨论(否则问题就无法回答,因为他们的原始陈述做出了误导性的假设)。

使用迭代而不是递归

def countTo(n):
    for i in range(1, n + 1):
        print(n)

如果您希望重写的版本仍然是递归的,那么没有办法。任何函数调用都将消耗堆栈空间


在某些语言中,处于尾部位置的调用不会占用堆栈空间。在这种语言中,您可以将函数重写为尾部递归,这样它就可以在O(1)空间中运行。但是Python不是这些语言之一。

如果Python支持尾部递归,您可以这样做:

def foo(n):
    print n
    if n > 1:
        foo(n-1)
任何现代gcc版本的相应c程序都将在O(1)中运行。虽然我不知道有哪种python解释器支持尾部递归,但我看不出该语言本身有任何限制禁止它

def foo(n):
    if n > 1:
        foo(n-1)
    print n
(将其视为算法,而不是特定于语言的代码。)


这里的时间复杂度是O(n*log(n)),如果我们认为每个数字的打印都需要相同的时间。

你能写一个“从1到n计数”的程序吗?:)我是认真的。你说的@yi_H?@S.Lott是什么意思?正如我在回答中所说,Python不执行尾部调用消除,所以这对他没有帮助。在Python的上下文中,这个问题的答案是“不”。来自LessWrong的家伙有时……嗯……很难。我只想指出问题的复杂性。简言之,最初的面试问题陈述错误。只有当问题被更仔细地陈述时,它才允许解决。我已经完全重述了这个问题,以反映最初打算提出的问题,而不是实际提出的问题。不幸的是,这意味着现在所有的答案都无关紧要,但除此之外,这个问题的答案微不足道,相当无聊@Genesis,我希望没问题。顺便说一句,这不是你的错,LessWrong的原始公式是错误的。程序必须保持递归。我在问题中补充了这一点。为什么不能使用基于CPS的Python实现呢?@apc In thoery,你可以。但实际上,所有基于CPS的Python实现都是默默无闻的。如果希望Python代码能够远程移植,就不要依赖尾部递归优化。@delnan,我可能错了,但我相信PyPy通过RPython支持它。可移植性问题似乎是一个突出的问题,但已知会发生哪些具体的可移植性问题?@apc要么你表达得不清楚,要么你对PyPy有一些误解。PyPy的“默认”版本(JIT和非JIT)没有任何远程无堆栈特性。有一个可选的stackless构建,但是(1)很少有人同时安装它,(2)不久前的一个SO问题表明它没有进行尾部调用优化,但只提供了一些可以用于此效果的原语,(3)我记得至少有一个讨厌的stackless独占性bug。但即使所有PyPy二进制文件都有TCO,PyPy也不是非常普遍。@apc忽略任何现有Python实现是否消除了尾部调用,Guido van Rossum说。因此,任何消除尾部调用的实现都不会是Python的严格实现,但会以错误的顺序打印数字。要创建尾部递归版本,需要两个参数。@sepp2k我不知怎的想到我们应该从n到1计数。。不知道为什么。但另一个解决方案也应该非常明显,我认为这是留给读者的练习;)