You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何分析给定两个Lisp函数的时间与空间复杂度?

分析两个Lisp阶乘函数的时间与空间复杂度

咱们先从最清晰的fatt函数开始拆解,再聊那个带累加器的fact——先提一句,你给出的fact代码里有两个明显的笔误:一是递归调用里的(- 1 x)应该是(- x 1)(不然x>1时会得到负数,递归永远停不下来);二是调用的fatt应该是fact自己(因为fatt只接受一个参数,传两个参数会直接报错)。我会先基于修正后的正确代码分析,再提原错误代码的问题。


1. fatt:普通递归版阶乘

先看代码:

(defun fatt (x)
  (if (zerop x)
      1
      (* x (fatt (- x 1)))))

时间复杂度

这个函数的逻辑是,每次递归把输入x减1,直到x等于0才触发终止条件。对于输入n,总共会执行n+1次函数调用(从x=n一直到x=0)。每次调用里的操作——判断zerop、乘法运算——都是常数时间(O(1))。所以总的时间复杂度是O(n),和输入的大小线性相关。

空间复杂度

普通递归会依赖调用栈来保存每一层的上下文:每调用一次fatt,都会在栈上保存当前的x值和返回地址,直到递归到base case(x=0)才开始回溯。栈的深度等于递归的次数,也就是n+1层。所以空间复杂度是O(n),和输入大小线性相关。


2. fact:带累加器的尾递归版本(修正后)

修正后的正确代码应该是这样的:

(defun fact (x &optional (acc 1))
  (if (zerop x)
      acc
      (fact (- x 1) (* x acc))))

时间复杂度

和普通递归版一样,这个函数也需要执行n+1次调用,每次调用的操作都是常数时间。所以时间复杂度还是O(n)——尾递归并没有优化时间,它的优势在空间上。

空间复杂度

这里的关键是尾递归优化(TCO):

  • 如果你的Lisp实现(比如SBCL、CLISP等)支持TCO,那么编译器/解释器会把尾递归转换成循环结构,不需要为每一次递归调用创建新的栈帧——只需要更新x和acc两个变量的值就行。这种情况下,空间复杂度是O(1)(常数空间)。
  • 如果你的Lisp实现不支持TCO,那么这个函数的行为和普通递归一样,每一次调用都会占用栈帧,空间复杂度还是O(n)。

关于你原代码的说明

你给出的fact函数里,递归调用(fatt (- 1 x) (* x acc))是无法运行的:一方面(- 1 x)会产生负数导致递归无法终止,另一方面fatt只接受一个参数,传两个参数会直接抛出错误。所以这应该是输入时的笔误,上面的分析基于合理修正后的代码。


内容的提问来源于stack exchange,提问作者Giuseppe Giubaldo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:41:18