SICP読書録 #16 練習問題 1.43 - 練習問題 1.46

[hiroshi-manabe様の日本語訳](https://github.com/hiroshi-manabe/sicp-pdf)を使わせてもらいます。
練習問題等は[Gauche](http://practical-scheme.net/gauche/index-j.html)で実行。

[前回はこちら](https://qiita.com/shuetsu@github/items/7e94c38ef70638a10053)

### 練習問題 1.43

手続きを指定した回数だけ適用する手続き`repeated`を実装する。
前回作成した、合成関数を生成する手続き`compose`が活用できる。

```_.scm
(define (compose f g) (lambda (x) (f (g x))))

(define (repeated proc n)
  (if (= n 1)
    proc
    (compose proc (repeated proc (- n 1)))))
```

実行結果。

```_.scm
gosh> ((repeated square 2) 5)
625
```

### 練習問題 1.44

平滑化とは、$f$が関数で$dx$が小さな値であるとき、$x$での値を$f(x-dx)$, $f(x)$, $f(x+dx)$の平均とすることをいう。これは、信号処理においてノイズを除去するために利用される手法だ。

手続きを受け取り、その平滑化関数を返す手続き`smooth`を実装する。

```_.scm
(define dx 0.0001)

(define (smooth proc)
  (lambda (x)
    (/ (+ (proc (+ x dx)) (proc x) (proc (- x dx))) 3)))
```

平滑化を繰り返し適用するn重平滑化関数というのが役に立つこともある。先の`repeated`を使えば簡単に実装できる。

```_.scm
(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の値が変わったときに必要な平均緩和法の回数を調べるため、以下の手続きを用意する。

```_.scm
(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の最小値を調べていくと。。。

```_.scm
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$を小数点以下切り捨てした回数だけ、平均緩和法を適用すればよい、らしい。

というわけで、以下のように一般化できた。

```_.scm
(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`手続きを実装しよう。

```_.scm
(define (iterative-improve enough? improve)
  (define (iter guess)
    (if (enough? guess)
      guess
      (iter (improve guess))))
  (lambda (guess) (iter guess)))
```

これを使って、1.1.7節で実装した`sqrt`手続きを作り直してみる。

```_.scm
(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`も、同様に作り直してみる。

```_.scm
(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))
```

これにて第１章おわり！