Program Structure 笔记8 (执行效率介绍2)
(作者:colinboy Email:cybbh@163.com) 2008.5.14
(内容难免出现错误或一些专业词汇使用不当,只是个人笔记,能理解总体内容就好)
Order of Growth:
(define (linear x)
(if (empty? x)
``````````
(combine (constant (first x))
(linear (bf x)))))
以上函数的增长率位Θ(n),每次调用产生n次递归调用,每个递归调用花费常量时间。
(define (quad x)
(if (empty? x)
`````````
(combine (linear (first x))
(quad (bf x)))))
以上函数的增长率为Θ(n^2),每次调用产生n次递归调用,每个递归调用开销为Θ(n)。
(define (mystery x)
(cond ((empty? x) `````)
((test (first x)) (constant (mystery (bf x))))
(else (mystery (bf x)))))
以上函数的增长率为Θ(n),每次调用产生n次递归调用,每个递归调用花费常量时间。
空间需求:
我们已经分析过一些函数的时间需求,函数运行的时候还需要运行空间。
(define (count sent)
(if (empty? sent)
0
(+ 1 (count (bf sent)))))
(count '(she loves you))
以上程序的调用可以分成以下步骤:
1、 (count '(she loves you))
2、 (+ 1 (count '(loves you)))
3、 (+ 1 (+ 1 (count '(you))))
4、 (+ 1 (+ 1 (+ 1 (count '()))))
5、 (+ 1 (+ 1 (+ 1 0)))
6、 (+ 1 (+ 1 1))
7、 (+ 1 2)
8、 3
以上程序的执行过程在所有递归调用结束后,程序并没执行完毕,而是反过来计算表达式的值,每次递归调用都需要一些内存空间来存储那次调用所需的一些临时变量。
以上程序的空间需求为Θ(n),每次调用产生n次递归调用,每次递归调用都需要存储一些临时变量。
(define (count sent)
(define (iter wds result)
(if (empty? wds)
result
(iter (bf wds) (+ result 1))))
(iter sent 0))
以上程序的空间需求为Θ(1),每次调用产生n次迭代调用,迭代调用只需要保存当前调用的一些临时变量,而不需要记录以前调用所需要的临时变量。
(count '(she loves you))
以上调用的执行过程为:
1、(count '(she loves you))
2、(iter '(loves you) 1)
3、(iter '(you) 2)
4、(iter '() 3)
5、3
上面过程是一个递归过程,它产生了迭代调用过程,count函数看似也许会产生递归调用,但是却不是,对于一些高级语言(如c、c++)在函数中调用自身一定会产生递归调用,这些语言借助一些辅助语句如for while等来产生迭代过程,在scheme中,在一个函数中调用自身并不一定会产生递归调用,这种实现特性成为尾递归(tail call/tail recursion)。
以上定义的2个版本的count的时间需求均为Θ(n)。
树形递归:
(define (pascal row col)
(cond ((= col 0) 1)
((= col row) 1)
(else (+ (pascal (- row 1) (- col 1))
(pascal (- row 1) col)))))
上面定义的pascal过程作用为产生pascal triangle的某个元素,row代表行,col代表列。
(pascal 20 10) --> 184756
此调用花费很长时间才计算出结果(1.7G/Debian Linux花费了15秒),为什么如此慢? 此过程中含有2次递归调用,会产生一个树形递归过程,在此递归过程中,输入参数的很小变动,都会使递归调用次数发生十分大的变化,而且此实现会重复计算很多很多pascal triangle的元素。
更好的pascal实现:
(define (new-pascal row col)
(nth col (pascal-row row)))
(define (pascal-row row-num)
(define (iter in out)
(if (empty? (bf in))
out
(iter (bf in) (se (+ (first in) (first (bf in))) out))))
(define (next-row old-row num)
(if (= num 0)
old-row
(next-row (se '1 (iter old-row '(1))) (- num 1))))
(next-row '(1) row-num))
(pascal 20 10) --> 184756
相比上面的实现,这种实现对于上面的调用仅花费不到1秒时间,采用逐行计算的方法,避免了对元素的重复计算。
此实现的时间需求为Θ(n^2)