1

线性递归与线性迭代

对于阶乘运算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用来保存基数的变化。


Dr_Noooo
50 声望5 粉丝

引用和评论

0 条评论