在之前的学习中,我们学习了关于求幂的一系列算法;
比如求幂的递归算法,这个算法的体验和之前学习的阶乘的算法是差不多的;
一、求幂的递归算法实现
利用(expt b n)中n不断减小达到n=0时返回值为1为终止,
(if (= n 0)
1
(* b (expt b (- n 1))))
这样一个过程得到的代换模型如下:
(* b (* b (* b (...(* b (- n 1)))))) 此时的n为1,
这里需要理解递归的过程。
二、求幂的迭代算法实现
利用一个计数器counter和一个乘积product,
计数器的初始值为n,不断减小到0,
其间乘积product的值为b和product相乘得到的值,
当计数器的值为0时,返回此时product的值,
(define (expt b n)
(define (expt-iter b counter product)
(if (= counter 0)
product
(expt-iter b
(- counter 1)
(* b product)))))
三、快速求幂的算法实现
利用了一个公式,
当n为偶数的时候,b^n=(b^(n/2))^2,
b的n次方等于b的二分之n次方的平方,
于是有以下表达示:
(if (even? n)
(square (fast-expt b (/ n 2)))
但是在遇到n是奇数的时候,
调用一个b乘以fast-expt,再在此时的n参数处减去一个1来实现,
(define (fast-expt b n)
(if (even? n)
(square (fast-expt b (/ n 2)))
(else (* b (fast-expt b (- n 1))))))
四、用迭代的方法来实现一个有快速求幂的算法实现
这就是本练习题了,
做这道题的时候读了好几遍没有读懂题,
其实一开始他就告诉我们要求,
“请定义一个过程,它能产生出一个按照迭代方式的求幂过程”
“其中使用一系列的求平方,就像fast-expt一样只用对数个步骤”
在看中文的时候有一个地方翻译有问题,
书上“一样”两个字放在了“fast-expt”的前面,照成了阅读困难,
英文为:
“Design a procedure that evolves an iterative exponentiation
process that uses successive squaring and uses a logarithmic
number of steps, as does fast-expt.”
其中提示了b^n=(b^(n/2))^2=(b^2)^(n/2)
还有一个提示是说在指数n和基数b以外,
还要有一个维持附加状态的变量a,并定义好状态的转变,
从一个状态到另一状态的转换时乘积ab^n不变。
第二个提示不好理解,应该说的时当a成为一个形参时,
在指数为单数和双数的过程中两个过程的实参不能互相影响,
要做到这一点,a也只能是一个形参(formal parameters),
而a在两种状态下都要传递实参(argument)进去,
初始的a为形参(formal parameters),
在这里也有另一个叫法“不变量(invariant quantity)”,
最后一个提示也最为关键,
“定义一个不变量,
要求它在状态之间保持不变,
这一技术是思考迭代算法设计问题时的一种非常强有力的方法。”
这句话值得细细品味,慢慢理解。
回到正题,
在fast-expt算法中有在n为双数的时候
(square (fast-expt b (/ n 2)))
在改变成迭代算法的时候可以利用(b^(n/2))^2=(b^2)^(n/2)
(fast-expt (square b) (/ n 2))
其理念为把b的“n/2”次方的平方,
变成了b的平方的“n/2”次方,
如此就完成了指数为双数时递归向迭代的转换;
要完成单数指数的计算,这时就要用到不变量a了,
在网上看到的思路为,
在迭代算法(fast-expt-iter)中引入如下形参(formal parameters):
index(指数)、base(底数)、a(不变量)
当指数为0时,返回值为0,
当指数为1时,返回值为a * base,
当指数为双数时,返回值为(fast-expt-iter (square base) (/ n 2) a)
当指数为单数时,且每次出现指数为单数时,
在过程中当时a这个形参的值就会是a * base,
(else (fast-expt-iter base (- index 1) (* a base)))
最后应用过程并用实参进行取代形参如下
(fast-expt-itxer b n 1)
尝试写代码2021年3月20日17:58
(define fast-expt
(lambda (b n)
(define fast-expt-iter
(lambda (base index a)
(cond ((= index 0) 1)
((= index 1) (* a base))
((even? index)
(fast-expt-iter (square base)
(/ index 2)
a))
(else (fast-expt-iter base
(- index 1)
(* a base))))))
(define even?
(lambda (x)
(= (remainder x 2) 0)))
(define square
(lambda (x)
(* x x)))
(fast-expt-iter b n 1)))