hiroshi-manabe様の日本語訳を使わせてもらいます。
練習問題等はGaucheで実行。
練習問題 1.43
手続きを指定した回数だけ適用する手続きrepeated
を実装する。
前回作成した、合成関数を生成する手続きcompose
が活用できる。
(define (compose f g) (lambda (x) (f (g x))))
(define (repeated proc n)
(if (= n 1)
proc
(compose proc (repeated proc (- n 1)))))
実行結果。
gosh> ((repeated square 2) 5)
625
練習問題 1.44
平滑化とは、$f$が関数で$dx$が小さな値であるとき、$x$での値を$f(x-dx)$, $f(x)$, $f(x+dx)$の平均とすることをいう。これは、信号処理においてノイズを除去するために利用される手法だ。
手続きを受け取り、その平滑化関数を返す手続きsmooth
を実装する。
(define dx 0.0001)
(define (smooth proc)
(lambda (x)
(/ (+ (proc (+ x dx)) (proc x) (proc (- x dx))) 3)))
平滑化を繰り返し適用するn重平滑化関数というのが役に立つこともある。先のrepeated
を使えば簡単に実装できる。
(define (n-smooth proc n)
((repeated smooth n) proc))
練習問題 1.45
1.3.3節で、$y\mapsto x/y$の不動点を探すことによって平方根を計算してみた。この際、平均緩和法によって解を収束できることも学んだ。
これは、n乗根の計算として一般化できる。$y\mapsto x/y^{n-1}$の不動点を探せばいいのだ。しかし例えば、$y\mapsto x/y^{3}$だと平均緩和法を1回行うだけでは収束しない。この場合は2回行えば収束する。
nの値が変わったときに必要な平均緩和法の回数を調べるため、以下の手続きを用意する。
(define tolerance 0.00001)
(define (average x y) (/ (+ x y) 2))
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2))
tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(define (average-damp f)
(lambda (x) (average x (f x))))
(define (n-root-test x n c)
(fixed-point ((repeated average-damp c)
(lambda (y) (/ x (expt y (- n 1)))))
1.0))
nを増やしながら、それぞれ収束するcの最小値を調べていくと。。。
gosh> (n-root-test (expt 2 2) 2 1)
2.000000000000002
gosh> (n-root-test (expt 2 3) 3 1)
1.9999981824788517
gosh> (n-root-test (expt 2 4) 4 2)
...
gosh> (n-root-test (expt 2 16) 16 4)
2.000000000076957
...
gosh> (n-root-test (expt 2 32) 32 5)
2.000000000000006
こんな法則が見つかった。理屈は不明だけど、$log_2 n$を小数点以下切り捨てした回数だけ、平均緩和法を適用すればよい、らしい。
というわけで、以下のように一般化できた。
(define (n-root x n)
(fixed-point ((repeated average-damp (floor (/ (log n) (log 2))))
(lambda (y) (/ x (expt y (- n 1)))))
1.0))
練習問題 1.46
今までやってきたことのいくつかは、反復改良法というものの例だ。つまり、推測値が十分よくなるまで改良を続ける、というプロセスである。これを抽象化しよう。
推測値が十分によいかを判断する手続きと、推測値を改良する手続きが与えられれば、反復改良法により近似値を探し出す手続き(これは初期推測値を引数として受け取る)を返す、という内容のiterative-improve
手続きを実装しよう。
(define (iterative-improve enough? improve)
(define (iter guess)
(if (enough? guess)
guess
(iter (improve guess))))
(lambda (guess) (iter guess)))
これを使って、1.1.7節で実装したsqrt
手続きを作り直してみる。
(define tolerance 0.00001)
(define (average x y)
(/ (+ x y) 2))
(define (sqrt x)
(define (enough? guess)
(< (abs (- (square guess) x)) tolerance))
(define (improve guess)
(average guess (/ x guess)))
((iterative-improve enough? improve) 1.0))
不動点を求める手続きfixed-point
も、同様に作り直してみる。
(define (fixed-point f first-guess)
(define (enough? guess)
(< (abs (- guess (f guess))) tolerance))
(define (improve guess)
(f guess))
((iterative-improve enough? improve) 1.0))
これにて第1章おわり!