代码分析:递归斐波那契函数

(defun fibonacci (n)
  (if (or (= n 0) (= n 1))
      n
      (+ (fibonacci (- n 1))
         (fibonacci (- n 2)))))

函数定义

  • defun​ 用于定义一个名为 fibonacci​ 的函数,接受一个参数 n​。

条件判断

  • if​ 语句用于条件分支。
  • (or (= n 0) (= n 1))​ 检查 n​ 是否等于 0 或 1。

递归实现

  • 如果 n​ 为 0 或 1,直接返回 n​。
  • 否则,返回 (fibonacci (- n 1))​ 和 (fibonacci (- n 2))​ 的和。

递归流程图

1726982828479

难点解析

  1. 递归理解

    • 递归是一个函数调用自身的过程。在这里,fibonacci​ 函数为了计算 n​ 的斐波那契数,会调用自身来计算 n-1​ 和 n-2​ 的斐波那契数。
    • 递归需要一个基础情况(这里是 n = 0 或 1)来结束递归。
  2. 效率问题

    • 这种实现方式的时间复杂度是指数级的 O(2^n),因为每次递归都会产生两个新的函数调用。
    • 对于较大的 n,计算会变得非常慢,因为会重复计算许多相同的值。

要点

  1. 基础情况

    • 定义正确的基础情况(n = 0 或 1)是确保递归终止的关键。
  2. 递归关系

    • 斐波那契数列的定义:F(n) = F(n-1) + F(n-2),这在代码中直接体现。
  3. 函数式编程

    • 这是一个纯函数式的实现,没有使用任何可变状态。
  4. 代码简洁性

    • 递归实现使得代码非常简洁和易读,直接对应数学定义。

优化建议

  1. 尾递归优化
    Common Lisp 支持尾递归优化,可以改写函数以利用这一特性,减少栈空间使用。
  2. 记忆化(Memoization)
    可以使用哈希表存储已计算的值,避免重复计算。
  3. 迭代实现
    使用循环代替递归可以大大提高效率,特别是对于大的 n 值。

示例:优化后的迭代实现

(defun fibonacci-iter (n)
  (if (< n 2)
      n
      (let ((a 0) (b 1))
        (loop repeat (- n 1)
              do (psetq a b
                        b (+ a b))
              finally (return b)))))

这个迭代版本的时间复杂度为 O(n),空间复杂度为 O(1),对于大的 n 值会有显著的性能提升。


现在让我们继续讨论斐波那契函数的实现。除了之前提到的优化建议,我还想补充一些关于 Common Lisp 中实现这个函数的有趣细节:

  1. 多值返回
    Common Lisp 支持多值返回,我们可以利用这一特性来进一步优化斐波那契函数的计算:

    (defun fib-pair (n)
      (if (= n 0)
          (values 0 1)
          (multiple-value-bind (a b) (fib-pair (1- n))
            (values b (+ a b)))))
    
    (defun fibonacci (n)
      (nth-value 0 (fib-pair n)))
    

    这个实现使用了 multiple-value-bind​ 和 values​,可以同时计算和返回两个连续的斐波那契数,从而减少重复计算。

  2. 宏的使用
    我们可以使用宏来生成一个编译时计算的斐波那契查找表:

    (defmacro fibonacci-table (max)
      `(progn
         (defvar *fib-table* 
           (make-array ,(1+ max) :initial-contents 
             ',(loop for n from 0 to max
                     collect (if (< n 2)
                                 n
                                 (+ (fibonacci (- n 1))
                                    (fibonacci (- n 2)))))))
         (defun fibonacci (n)
           (if (< n ,(1+ max))
               (aref *fib-table* n)
               (error "Fibonacci number too large: ~D" n)))))
    

    使用 (fibonacci-table 100)​ 可以生成一个查找表和相应的 fibonacci​ 函数,它可以快速返回 0 到 100 范围内的任何斐波那契数。

  3. 惰性序列
    Common Lisp 没有内置的惰性求值,但我们可以模拟创建一个惰性的斐波那契序列:

    (defstruct fib-seq
      (a 0)
      (b 1))
    
    (defun fibonacci-stream ()
      (let ((seq (make-fib-seq)))
        #'(lambda ()
            (with-slots (a b) seq
              (prog1 a
                (psetq a b
                       b (+ a b)))))))
    

    这个实现返回一个闭包,每次调用都会生成下一个斐波那契数。

这些实现展示了 Common Lisp 的一些高级特性,如多值返回、宏、结构体和闭包。每种方法都有其特定的用途和优势。


  • Lisp
    37 引用 • 13 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...