线性递归与线性迭代
对于阶乘运算n!,采用递归形式编写是非常容易理解的:
(define (factorial n)
(if (= n 1) 1
(* n (factorial (- n 1))
)
)
计算6!,其执行过程是这样的:
(factorial 6)
(* 6 (factorial 5))
(* 6 (* 5 (factorial 4)))
(* 6 (* 5 (* 4 (factorial 3))))
(* 6 (* 5 (* 4 (* 3 (factorial 2)))))
(* 6 (* 5 (* 4 (* 3 (* 2 (factorial 1))))))
(* 6 (* 5 (* 4 (* 3 (* 2 1)))))
(* 6 (* 5 (* 4 (* 3 2))))
(* 6 (* 5 (* 4 6))
(* 6 (* 5 24)
(* 6 120)
720
可以看出,在计算n!过程中,推迟执行的乘法链条的长度,也就是为保存其轨迹需要保存的数据量,这个长度随着n值而线性增长。
下面我们采用迭代形式来重构这个过程:
(define (newFac n)
(define (iterFac result counter)
(if (= counter n) (* n result)
(iterFac (* result counter) (+ counter 1))
)
)
(iterFac 1 1)
)
这里我们定义了两个过程,iterFac是定义在newFac内部,只能被newFac调用。同样,我们用来计算6!时,它的计算过程是这样的:
(newFac 6)
(iterFac 1 1)
(iterFac 1 2)
(iterFac 2 3)
(iterFac 6 4)
(iterFac 24 5)
(iterFac 120 6)
720
迭代过程中,没有任何保存数据量的增长,对于任何一个n,在计算过程中的每一步,我们需要保存的就是result和counter。用一个变量(eg. result)传递迭代过程中状态的变化,这是迭代过程的核心!
下面给出一个O(logn)复杂度的幂运算的迭代过程:
(define (newExp b n)
(define (even? n)
(= (remainder n 2) 0)
)
(define (iterExp a k result)
(cond ((= k 1) result)
((even? k) (iterExp (* a a) (/ k 2) (* result result)))
(else (* (iterExp a (- k 1) result) a))
)
)
(iterExp b n b)
)
(even? n)用来判断n是基数还是偶数,计算b^n时,我们采用的是这样一种方法:
b^n = (b^2)^(n/2) (n是偶数时),b^n = b^(n-1)*b(n为奇数),result用来保存计算结果,a用来保存基数的变化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。