Exercise1.20~1.39

Exercise1.20

;; 正規順序
正規順序による (gcd 206 40) の評価は次のように進行する。

if:(= 40 0) => #f
else:(gcd 40 (remainder 206 40))

if:(= (remainder 206 40) 0) => #f
else:(gcd (remainder 206 40)
          (remainder 40 (remainder 206 40)))
 
if:(= (remainder 40 (remainder 206 40)) 0) => #f
else:(gcd (remainder 40 (remainder 206 40))
          (remainder (remainder 206 40) (remainder 40 (remainder 206 40))))

if:(= (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) 0) => #f
else:(gcd (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
          (remainder (remainder 40 (remainder 206 40))
                     (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))

if:(= (remainder (remainder 40 (remainder 206 40))
                 (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))) 0) => #t
then:(remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
=>2

となる。結局 remainder 演算が実行されるのは、if文の中で14回、最後に簡
約されるとき4回であり、計18回実行される。

;; 作用的順序
(gcd 206 40)
作用的順序による評価は次のように進行する。
(gcd 40 6)                              ;6: (remainder 206 40)
(gcd 6 4)                               ;4: (remainder 40 6)
(gcd 4 2)                               ;2: (remainder 6 4)
(gcd 2 0)                               ;0: (remainder 4 2)
2
 引数が評価されてから作用させるので、remainder は 4回実行される。
by kacchi (Alyssaさんの指摘を受けて修正)

Exercise1.21

199, 1999, 19999 の最小除数

(smallest-divisor 199)
;Value: 199
(smallest-divisor 1999)
;Value: 1999
(smallest-divisor 19999)
;Value: 7
by kacchi

Exercise1.22

runtime 関数がある MIT Scheme を利用しました(他の処理系にはない?)。
指定範囲の連続する奇整数について素数性を調べる手続き search-for-prime を書け

;; 素数でない整数は出力しないようにした。
(define (search-for-prime start limit)
  (if (prime? start)
      (times-prime-test start))
  (if (<= (+ start 2) limit)
      (search-for-prime (+ start 2) limit)))

;; 速すぎて計測できないので大きな数で試した。
(search-for-prime 1000001 1000100)
1000003 *** 1.0000000000000009e-2
1000033 *** 1.0000000000000009e-2
1000037 *** 1.0000000000000009e-2

(search-for-prime 10000001 10000200)
10000019 *** .01999999999999602
10000079 *** .03999999999999915
10000103 *** 2.0000000000003126e-2

(search-for-prime 100000001 100000100)
100000007 *** .11999999999999744
100000037 *** 6.0000000000002274e-2
100000039 *** 6.0000000000002274e-2

ひと桁増えると計算時間がほぼ√10倍になると思うので、
・支持する。
・合っている。
by kacchi

Exercise1.23

(define (next n)
  (if (= n 2)
      3
      (+ n 2)))

(define (find-divisor n test-divisor)
  (cond ((> (square test-divisor) n) n)
        ((divides? test-divisor n) test-divisor)
        (else (find-divisor n (next test-divisor)))))

(times-prime-test 1000003)
1000003 *** .00999999999999801
(times-prime-test 10000019)
10000019 *** 3.0000000000001137e-2
(times-prime-test 100000007)
100000007 *** .10999999999999943

速度に変化はないと思う。しかし理由が解らない。
by kacchi

Exercise1.24

解答募集中

Exercise1.25

理論上は正しい。しかし,高速素数テストには使えない。

Hackerのプログラムでは(fast-exp base exp)を計算する。
expが大きな数になると,この計算に時間が掛かりすぎる。
たとえば1001の素数判定を行うとしてaに2を選ぶと,
(fast-exp 2 1001),すなわち2の1001乗を先に計算する
ことになる。
by chrono

Exercise1.26

Reasonerのプログラムでのexpmod関数の呼び出し回数は,次のように求まる。
nを二進数で表現し,「各ビットに対してビット0なら1,ビット1なら2に
そのビットが表す10進数を掛けた値」の和となる。
ただし,最上位ビットは1にビットが表す10進数を掛ける。
たとえば,nが10の時,
10 = (1010)_2 より 8*1 + 4*1 + 2*2 + 1*1 = 17回となる。

expmod関数の呼び出し関係と照らし合わせると次となることより分かる。
(詳細は割愛します…)

       1         :      0         :                 1                 :              0
                                  :                                   :
expmod(base 1 m)-+                                                    :
expmod(base 1 m)--expmod(base 2 m)+                                   :
                                  |                                   :
expmod(base 1 m)-+                |
expmod(base 1 m)--expmod(base 2 m)--expmod(base 4 m)--expmod(base 5 m)+
                                                                      |
expmod(base 1 m)-+                                                    |
expmod(base 1 m)--expmod(base 2 m)+                                   |
                                  |                                   |
expmod(base 1 m)-+                |                                   |
expmod(base 1 m)--expmod(base 2 m)--expmod(base 4 m)--expmod(base 5 m)--expmod(base 10 m)
 
ここで,2のべき乗のうちn以下の最大の数を2^kとすると,expmod関数の
呼び出し回数の関係は,明らかに
  2^k <= n < 2^(k+1)
となる (ここも割愛します…)。

2^kの呼び出し回数は上述より2^(k+1) - 1となり,2^(k+1)の呼び出し回数は
上述より2^(k+2) - 1となる。
よって,Reasonerのプログラムはθ(n)である。

by chrono

Exercise1.27

問題文
脚注47におけるCarmichael数を実際にfool the Fermat testを行って証明せよ。つまり、整数nを引数に取り∀a < n(a^n≡a (mod n))を満たすかテストする手続きを作れ、そしてそれで与えられたCarmichael数をテストしてみよ。

(define (fool-the-fermat-test n)
  (define (iter-fermat a)
    (cond ((< a 0) #f)
          ((= a 0) #t)
          ((= (expmod a n n) a) (iter-fermat (- a 1)))
          (else #f)))
  (iter-fermat (- n 1)))


gosh> (fool-the-fermat-test 561)
#t
gosh> (fool-the-fermat-test 1105)
#t
gosh> (fool-the-fermat-test 1729)
#t
gosh> (fool-the-fermat-test 2465)
#t
gosh> (fool-the-fermat-test 2821)
#t
gosh> (fool-the-fermat-test 6601)
#t
by iwk

Exercise1.28

解答募集中

Exercise1.29

;; 合成シンプソン公式を参考にした。
http://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%B3%E3%83%97%E3%82%BD%E3%83%B3%E3%81%AE%E5%85%AC%E5%BC%8F

(define (cube x) (* x x x))

(define (integral f a b n)
  (define h (/ (- b a) n))
  (define (y k) (f (+ a (* k h))))
  (define (next n) (+ n 2))
  (/ (* h
        (+ (y 0)
           (* (sum y 1 next (- n 1)) 4)
           (* (sum y 2 next (- n 2)) 2)
           (y n)))
     3))

(integral cube 0 1 100)
=>1/4
(integral cube 0 1 1000)
=>1/4
by kacchi

Exercise1.30

(define (sum term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (+ (term a) result))))
  (iter a 0))
by kacchi

Exercise1.31

(a)
(1)与えられた範囲の点での関数値の積を返す手続き product を定義せよ。
(2)product を使って factorial を定義せよ。
(3)式 π/4 = (2*4*4*6*6*8..)/(3*3*5*5*7*7...) によってπの近似値を
   productを使って定義せよ。

(1)
(define (product term a next b)
  (if (> a b)
      1
      (* (term a) (product term (next a) next b))))

(2)
(define (factorial n)
  (product identity 1 inc n))

(3)
;; John Wallis
;; http://www.pluto.ai.kyutech.ac.jp/plt/matumoto/pi_small/node5.html
(define (pi-product a b)
  (define (pi-term x)
    (/ (* (* x 2) (+ (* x 2) 2))
       (square (+ (* x 2) 1))))
  (define (pi-next x) (+ x 1))
  (product pi-term a pi-next b))

gosh> (* 4 (pi-product 1.0 1000))
=>3.1423773650938855

(b) 反復的プロセスを生成する product を書け。

(define (product term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (* (term a) result))))
  (iter a 1))
by kacchi

Exercise1.32

(a) sum や product 更に一般的な accumulate の特殊な場合であることを示せ。

;; 再帰的プロセス版
(define (accumulate combiner null-value term a next b)
  (if (> a b)
      null-value
      (combiner (term a)
                (accumulate combiner null-value term (next a) next b))))

(define (sum term a next b)
  (accumulate + 0 term a next b))

(define (product term a next b)
  (accumulate * 1 term a next b))

(b) 反復的プロセスを生成する版

(define (accumulate combiner null-value term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (combiner (term a) result))))
  (iter a null-value))
by kacchi

Exercise1.33

;; 再帰版
(define (filtered-accumulate predicate combiner null-value term a next b)
  (cond ((> a b) null-value)
        ((predicate a)
         (combiner (term a)
                   (filtered-accumulate
                    predicate combiner null-value term (next a) next b)))
        (else (filtered-accumulate
               predicate combiner null-value term (next a) next b))))

;; 反復版
(define (filtered-accumulate predicate combiner null-value term a next b)
  (define (iter a result)
    (cond ((> a b) result)
          ((predicate a) (iter (next a) (combiner (term a) result)))
          (else (iter (next a) result))))
  (iter a null-value))

(a) 素数の2乗の和
(define (sum-squared-prime-numbers a b)
  (filtered-accumulate prime? + 0 square a inc b))

(sum-squared-prime-numbers 2 10)
;Value: 87
  
(b) i < n で gcd(i,n) = 1 となる全整数の積

 (define (product-gcd n)
  (define (p i)
    (= (gcd i n) 1))
  (filtered-accumulate p * 1 identity 1 inc n))

(product-gcd 10)
;Value: 189

(product-gcd 100)
;Value: 426252881942771063138176712755660145456313428952105524817872601
by kacchi

Exercise1.34

(define (f g)
  (g 2))

解釈系に (f f) を評価させるとどうなるか。

f の本体を取り出し仮引数 g を f で置き換える。
(f 2)
f の本体を取り出し仮引数 g を 2 で置き換える。
(2 2)
に帰着する。左端の演算子を評価すると値は 2 となる。値は手続きでなければならないが、
数値なのでエラーになる。

MIT scheme での結果。
;The object 2 is not applicable.
Gauche での結果。
*** ERROR: invalid application: (2 2)
by kacchi

Exercise1.35

1.2.2節の黄金比は、x^2 = x + 1 を満たす x である。
これを等価な、x = 1 + 1/x と書けば、平方根の計算と同様、x |-> 1 + 1/x の不動点を
探すことと同じである。

gosh> (fixed-point (lambda (x) (+ 1 (/ 1 x))) 1.0)
=>1.6180327868852458
by kacchi

Exercise1.36

(1) fixed-point を修正して、生成する近似値を順に印字できるようにせよ。
(define (average x y) (/ (+ x y) 2))

(define tolerance 0.00001)

(define (fixed-point f first-guess)
  (define (close-enough? v1 v2)
    (< (abs (- v1 v2)) tolerance))
  (define (try guess step)
    (let ((next (f guess)))
      (print-step next step)
      (if (close-enough? guess next)
          next
          (try next (+ step 1)))))
  (try first-guess 1))

(define (print-step n step)
  (display step)
  (display ": ")
  (display n)
  (newline))
(2) x^x = 1000 の解を求めよ。
(expt 4 4) < 1000 < (expt 5 5) なので、4.0 を fixed-point の予測値とした。

gosh> (fixed-point (lambda (x) (/ (log 1000) (log x))) 4.0)
1: 4.9828921423310435
2: 4.301189432497896
(略)
28: 4.555530430629037
29: 4.555539183677709
=>4.555539183677709

gosh> (expt 4.555539183677709 4.555539183677709)
=>1000.0087530953886
(3) 平均緩和法を使った場合とステップ数を比較せよ。
gosh> (fixed-point (lambda (x) (average (/ (log 1000) (log x)) x)) 4.0)
1: 4.491446071165521
2: 4.544974650975552
(略)
6: 4.5555268862194875
7: 4.5555342036887705
=>4.5555342036887705

gosh> (expt 4.5555342036887705 4.5555342036887705)
=>999.9962217021748

平均緩和法を使った場合、ステップ数は 1/4 になった。
by kacchi

Exercise1.37

(a) k項有限連分数を計算する手続きを定義し、kの順次の値で1/φの近似をとり手続きを調べよ。4桁の精度を得るにはkをどのくらいの大きさにしなければならないか。
(1) cont-frac
;; 再帰的プロセス版
(define (cont-frac n d k)
  (define (rec i)
    (if (= i k)
        (/ (n i) (d i))
        (/ (n i) (+ (d i) (rec (+ i 1))))))
  (rec 1))

(2) kの大きさ
1/φ の計算
gosh> (/ 1 (fixed-point (lambda (x) (+ 1 (/ 1 x))) 1.0))
=>0.6180344478216819

手続きを調べるため cont-frac-test を定義し、kの値が1から15まで1/φの近似を調べた。
kの増加に伴って精度が上がることを確認した。4桁の精度を得るには、kの値は11程度でよい。

(define (cont-frac-test a b)
  (if (<= a b)
      (let ((r (cont-frac (lambda (i) 1.0)
                          (lambda (i) 1.0)
                          a)))
        (display a)
        (display ": ")
        (display r)
        (newline)
        (cont-frac-test (+ a 1) b))))

gosh> (cont-frac-test 1 15)
1: 1.0
2: 0.5
3: 0.6666666666666666
4: 0.6000000000000001
5: 0.625
6: 0.6153846153846154
7: 0.6190476190476191
8: 0.6176470588235294
9: 0.6181818181818182
10: 0.6179775280898876
11: 0.6180555555555556
12: 0.6180257510729613
13: 0.6180371352785146
14: 0.6180327868852459
15: 0.6180344478216819
(b) cont-frac の反復的プロセス版
(define (cont-frac n d k)
  (define (iter a i)
    (if (= i 1)
        a
        (iter (/ (n (- i 1)) (+ (d (- i 1)) a)) (- i 1))))
  (iter (/ (n k) (d k)) k))
by kacchi

Exercise1.38

オイラーの展開による自然対数の底を近似するプログラムを書け。
;; e-2-cfの計算でkが20程度で15桁の精度が得られたので、
;; kを20としてeを近似するプログラムを書いた。
 (define (e)
  (define (e-2-cf k)
    (cont-frac (lambda (i) 1.0)
               (lambda (i)
                 (if (= (remainder i 3) 2)
                     (* 2.0 (inc (quotient i 3)))
                     1.0))
               k))
  (+ (e-2-cf 20) 2))

gosh> (e)
=>2.718281828459045
by kacchi (iwkさんの指摘を受けて修正)

Exercise1.39

正接関数の近似値を計算する手続き(tan-cf x k)を定義せよ。
(define (tan-cf x k)
  (cont-frac (lambda (i) (if (= i 1) x (- (* x x))))
             (lambda (i) (- (* i 2) 1))
             k))
by kacchi

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2008年01月08日 22:25
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。