1.2.4 指数

1.2.4 指数
考虑一下计算一个数的指数的问题,我们要有一个程序,
参数是一个底数B,和一个正整数的指数N,并且计算B的N次幂.
根据递归的定义,实现的一种方式如下:

b^n=b*b^(n-1)
b^0=1

它能被翻译成程序如下:

(define (expt b n)
   (if (= n 0)
        1
       (* b (expt b (- n 1)))
   )
)

这是一个线性的递归程序,它要求有 得塔N 步骤和 得塔N的空间.
正如非波那数列一样,我们能够形式化生成一个等价的线性迭代.

(define (expt b n)
    (expt-iter b n 1))

(define (expt-iter b counter product)
      (if (= counter 0)
            product
            (expt-iter b (- counter 1) (* b product))
      )
)

这个版本的程序要求 得塔N 步骤和 得塔1的空间.

利用连续的乘法,我们能够用更少的步骤计算指数.例如,B的8次方,
不用算8次,

b*(b*(b*(b*(b*(b*(b*b))))))

我们能够用3次乘法.

b^2=b*b
b^4=b^2*b^2
b^8=b^4*b^4

对于指数是2的幂的情况,这种方法就很好. 总之在有如下的规则的情况下,
在计算指数时,我们也能够利用连续乘方的方法.
如果N是偶数时:  b^n=(b^(b/2))^2
如果N是奇数时: b^n=b*b^(n-1)

我们能够把这个方法写成程序:

(define (fast-expt b n)
     (cond ((= n 0) 1)
           ((even? n)  (square (fast-expt b (/ n 2))))
           (else (* b (fast-expt b (- n 1))))
     )
)
检测一个整数是不是偶数的判断式使用了原生程序remainder.

(define (even? n)
     (= (remainder n 2) 0))

在空间需求和执行步骤数量的要求方面,fast-expt的执行过程都是以对数级进行增长。
为了了解这个情况,注意的是使用fast-expt计算 b^(2*N),仅要求比b^N的计算多了一次乘法而矣。
我们能计算的指数级规模,因此我们能被允许双倍新的乘法。因此对于n的指数的增长要求的乘法次数
关于与以2为底的N的对象一样快。这个执行过程有 theta(log(N))的增长。

对数增长与线性增长的区别在N很大时,才变得很明显。例如,对于N=1000,fast-expt仅需要14次乘法。
在练习1.16中计算指数,以对数级的步骤数量,以迭代的算法,使用连续平方的思想也是可能的。尽管,与常规的
迭代算法一样,这不像递归算法那样容易写。


练习1.16
设计一个迭代的程序,执行连续的乘法,使用对数级的步骤数量。正如fast-expt的做法。
(提示:注意(b^(n/2))^2=(b^2)^(n/2)保持一个N和底数b,一个额外的状态变量a,定义一个状态转换,
以a*b^n从一个状态转换到另一个状态。在执行过程开始时,a=1,在过程结束时,a是答案。总之,
定义一个不变量让它从一个状态到另一个状态时保持不变,这种技术是关于迭代算法的设计的强有力的方式。)


练习1.17
在这部分的指数算法中,是基于重复乘法的方式来执行指数计算的。
在相似的方式是,能够执行整数的乘法以重复加法的方式。
如下的乘法程序(在此假定我们的语言仅有加法没有乘法)与expt的程序是相似的。

(defne (* a b)
    (if (= b 0)
         0
         (+ a (* a (- b 1)))))

这个算法的步骤的数量是与b成线性关系的。现在我们假定,包括加法,和操作 加倍,和操作减半。使用
这些程序设计一个乘法,与fast-expt相似的程序。有对数级的操作步骤数。

练习1.18
使用练习1.16和 练习1.17的结果,把一个程序改成迭代过程的程序。
两个整数相乘,使用加法,加倍,减半等程序。

练习1.19
为了计算斐波那些数,这有一个聪明的算法,它有对数级的步骤数。
在1.2.2部分中的fib-iter执行过程中,重新调用 状态变量a和b的转换,
a<-a+b和 b<-a。这个转换称为T,注意不断地重复应用T,若干次,开始为1和0,
生成Fib(n+1)和 Fib(n)的数对。换句话说,斐波那些数是被应用T若干次而生成的。
是转换T的第若干次幂,开始于数对(1,0)。现在,可以认为T是转换Tpq的家族中的
当p=0 和q=1时的一个特例,Tpq的转换是把数对(a,b)变成 a<-bq+aq+ap和 b<-bp+aq。
显示的是如果我们应用这么个转换Tpq两次,与使用一个单独转换的Tp'q'的效果一致,那么可以计算
p'和q'用p和q表示的式子。这让我们可以以一种显式的方式来平方这些转换,因此我们能够
计算T^n,使用连续的平方,正如fast-expt程序所做的那样。
把这些结合起来,完成如下的程序,它运行时有对数级步骤数量。

(define (fib n)
   (fib-iter 1 0 0 1 n)
)

(define (fib-iter a b p q count)
     (cond ((= count 0) b)
           ((even? count)
             (fib -iter a
                        b   
                        <??>  ; compute p'  
                        <??>  ; compute q'
                        (/ count 2)))
          (else (fib-iter (+ (* b q) (* a q) (* a p))
                          (+ (* b p) (* a q)
                           p
                           q
                          (- count 1)))
     )
)

猜你喜欢

转载自blog.csdn.net/gggwfn1982/article/details/81384831
今日推荐