效率:递归与循环

时间:2012-02-21 22:33:07

标签: performance recursion common-lisp

这只是我的好奇心,但更有效,递归还是循环呢?

给定两个函数(使用常见的lisp):

(defun factorial_recursion (x)
    (if (> x 0)
        (* x (factorial_recursion (decf x)))
        1))

(defun factorial_loop (x)
    (loop for i from 1 to x for result = 1 then
        (* result i) finally
        (return result)))

哪个更有效?

4 个答案:

答案 0 :(得分:34)

我甚至不需要阅读你的代码。

循环对于阶乘更有效。当您进行递归时,您最多可以在堆栈上进行 x 函数调用。

出于性能原因,您几乎从不使用递归。您使用递归来使问题更简单。

答案 1 :(得分:11)

亩。

说真的,现在没关系。不是这个尺寸的例子。它们都具有相同的复杂性。如果您的代码不够快,这可能是您最后看到的地方之一。

现在,如果您真的想知道哪个更快,请测量它们。在SBCL上,您可以循环调用每个函数并测量时间。由于您有两个简单的函数,time就足够了。如果您的程序更复杂,profiler会更有用。提示:如果您不需要用于测量的分析器,您可能不需要担心性能。

在我的机器上(SBCL 64位),我运行了你的功能并得到了这个:

CL-USER> (time (loop repeat 1000 do (factorial_recursion 1000)))
Evaluation took:
  0.540 seconds of real time
  0.536034 seconds of total run time (0.496031 user, 0.040003 system)
  [ Run times consist of 0.096 seconds GC time, and 0.441 seconds non-GC time. ]
  99.26% CPU
  1,006,632,438 processor cycles
  511,315,904 bytes consed

NIL
CL-USER> (time (loop repeat 1000 do (factorial_loop 1000)))
Evaluation took:
  0.485 seconds of real time
  0.488030 seconds of total run time (0.488030 user, 0.000000 system)
  [ Run times consist of 0.072 seconds GC time, and 0.417 seconds non-GC time. ]
  100.62% CPU
  902,043,247 processor cycles
  511,322,400 bytes consed

NIL

将函数放在顶部为(declaim (optimize speed))的文件中后,递归时间降至504毫秒,循环时间降至475毫秒。

如果你真的想知道发生了什么,请在你的功能上试试dissasemble并查看其中的内容。

同样,这对我来说似乎不是问题。就个人而言,我尝试使用Common Lisp作为脚本语言进行原型设计,然后分析和优化缓慢的部分。从500毫秒到475毫秒是没有。例如,在一些个人代码中,我通过简单地向数组添加元素类型来获得几个数量级的加速(因此在我的情况下使数组存储小64倍)。当然,理论上重用该数组(在使其变小之后)并且不会反复分配它会更快。但是简单地将:element-type bit添加到它就足以满足我的需求 - 更多的更改需要更多时间才能获得额外的好处。也许我很草率,但“快”和“慢”对我来说并不重要。我更喜欢'足够快'和'太慢'。在大多数情况下,你的两个函数都“足够快”(或者在某些情况下两者都“太慢”),因此它们之间没有真正的区别。

答案 2 :(得分:8)

如果你能以这样的方式编写递归函数,使得递归调用完成的最后一件事(并且函数因此尾递归 您使用的语言和编译器/解释器支持尾递归,然后递归函数可以(通常)优化为真正迭代的代码,并且与同一函数的迭代版本一样快。

但是,Sam I Am是正确的,迭代函数通常比它们的递归函数更快。如果递归函数与执行相同操作的迭代函数一样快,则必须依赖优化器。

原因是函数调用 比跳转更昂贵,而且你消耗了堆栈空间,一个(非常)有限的资源。

您提供的函数不是尾递归,因为您调用factorial_recursion然后将其乘以x。尾递归版本的一个例子是

(defun factorial-recursion-assist (x cur)
    (if (> x 1)
        (factorial-recursion-assist (- x 1) (+ cur (* (- x 1) x)))
        cur))

(defun factorial-recursion (x)
    (factorial-recursion-assist x 1))

(print (factorial-recursion 4))

答案 3 :(得分:-1)

这是一个尾递归因子(我认为):

(defun fact (x)
  (funcall (alambda (i ret)
             (cond ((> i 1)
                    (self (1- i) (* ret i)))
                   (t
                    ret)))
           x 1))
相关问题