以下是两个lisp的功能
(defun fact (x &optional (acc 1))
(if (zerop x) acc
(fatt (- 1 x) (* x acc))))
(defun fatt (x)
(if (zerop x) 1
(* x (fatt (- x 1)))))
如何找到此功能的空间和时间复杂度?
答案 0 :(得分:1)
第一个功能的(更正版本)
(defun fact (x &optional (acc 1))
(if (zerop x)
acc
(fact (- x 1) (* x acc))))
当使用(fact N)
调用时,时间复杂度为O(N),因为每个递归级别的步骤相同,并且有N个递归调用。空间复杂性取决于编译器。
每个体面的LISP编译器都进行尾递归优化,因此fact
的递归调用被“跳转”替换为fact
函数的开头,参数被新的替换。所以你只有一组变量x
和acc
,意思是O(1)。
当然,对于一个愚蠢的编译器,你最终会得到N个调用堆栈帧,每个帧都有自己的一组变量x
和acc
,意思是O(N)。
第二个功能
(defun fatt (x)
(if (zerop x)
1
(* x (fatt (- x 1)))))
没有(通常 - 可能有一些非常聪明的编译器......)允许尾递归优化,因此,当使用(fatt N)
调用时,最终时间和空间都是O( N)。
<强>吹毛求疵强>:
如果使用大N值,计算将不再适合fixnum数字范围并改为使用bignums,然后违反所有递归调用执行相同步骤的假设。实际上,乘法和减法之类的执行时间随着数字的长度而增加。对于适合计算机内存的阶乘,每一步都可能是O(log(x))而不是O(1)。因此对于大数字,我们将观察到O(NlogN)而不是O(N)。