2010-10-30

問題1.22

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "cffi"))

(cffi:defcvar "errno" :int)

(cffi:defcfun "strerror" :string
  "Map the error number to a locale-dependent error message string."
  (errnum :int))

(cffi:define-foreign-library librt
  (t (:default "librt")))

(cffi:use-foreign-library librt)

(cffi:defctype clockid-t :int)
(cffi:defctype time-t :long)

(cffi:defcstruct timespec
  (tv_sec time-t)
  (tv_nsec :long))

(cffi:defcfun (%clock-gettime "clock_gettime") :int
  (clock-id clockid-t)
  (tp (:pointer timespec)))

(defconstant +clock-realtime+ 0)
(defconstant +clock-monotonic+ 1)
(defconstant +clock-process-cputime+ 2)
(defconstant +clock-thread-cputime+ 3)

(defun clock-gettime (clock-id)
  "Return the current value for the specified clock."
  (let ((id (ccase clock-id
              ((:realtime) +clock-realtime+)
              ((:monotonic) +clock-monotonic+)
              ((:process-cputime) +clock-thread-cputime+)
              ((:thread-cputime) +clock-thread-cputime+))))
    (cffi:with-foreign-object (tp 'timespec)
      (unless (zerop (%clock-gettime id tp))
        (error "Foreign function clock-gettime faild: ~a" (strerror *errno*)))
      (values (cffi:foreign-slot-value tp 'timespec 'tv_sec)
              (cffi:foreign-slot-value tp 'timespec 'tv_nsec)))))

(defmacro runtime ()
  `(multiple-value-bind (sec nsec)
       (clock-gettime :process-cputime)
     (+ (* sec (expt 10 9)) nsec)))

(defun dividesp (a b)
  (zerop (mod b a)))

(defun find-divisor (n test-divisor)
  (cond ((> (expt test-divisor 2) n) n)
        ((dividesp test-divisor n) test-divisor)
        (t (find-divisor n (1+ test-divisor)))))

(defun smallest-divisor (n)
  (find-divisor n 2))

(defun primep (n)
  (= n (smallest-divisor n)))

(defun report-prime (elapsed-time)
  (princ " *** ")
  (princ elapsed-time))

(defun start-prime-test (n start-time)
  (when (primep n)
    (report-prime (- (runtime) start-time))))

(defun timed-prime-test (n)
  (terpri)
  (princ n)
  (start-prime-test n (runtime)))

(defun search-for-primes (start end)
  (when (oddp start)
    (timed-prime-test start))
  (when (< start end)
    (search-for-primes (1+ start) end)))

runtimeがないので、clock_gettimeを使って定義する。精度はナノ秒。また、定義が自明なため、奇数の判別には組み込みのoddpを使っている。

> (search-for-primes 1000 1050)

1001
1003
1005
1007
1009 *** 9114
1011
1013 *** 8887
1015
1017
1019 *** 9054
1021 *** 9078
1023
1025
1027
1029
1031 *** 9275
1033 *** 9431
1035
1037
1039 *** 9162
1041
1043
1045
1047
1049 *** 9066
NIL
> (search-for-primes 10000 10050)

10001
10003
10005
10007 *** 23444
10009 *** 23803
10011
10013
10015
10017
10019
10021
10023
10025
10027
10029
10031
10033
10035
10037 *** 23983
10039 *** 23486
10041
10043
10045
10047
10049
NIL
> (search-for-primes 100000 100050)

100001
100003 *** 73907
100005
100007
100009
100011
100013
100015
100017
100019 *** 73650
100021
100023
100025
100027
100029
100031
100033
100035
100037
100039
100041
100043 *** 73279
100045
100047
100049 *** 73584
NIL
> (search-for-primes 1000000 1000050)

1000001
1000003 *** 217447
1000005
1000007
1000009
1000011
1000013
1000015
1000017
1000019
1000021
1000023
1000025
1000027
1000029
1000031
1000033 *** 216698
1000035
1000037 *** 216829
1000039 *** 227579
1000041
1000043
1000045
1000047
1000049
NIL

おおむねそれらしい値になっている。純粋な処理時間以外にも、手続きを呼び出すオーバーヘッドなども含まれるので、多少の誤差は許容範囲内と思われる。

問題1.21

(defun smallest-divisor (n)
  (find-divisor n 2))

(defun find-divisor (n test-divisor)
  (cond ((> (expt test-divisor 2) n) n)
        ((dividesp test-divisor n) test-divisor)
        (t (find-divisor n (1+ test-divisor)))))

(defun dividesp (a b)
  (zerop (mod b a)))

(smallest-divisor 199)
;; => 199
(smallest-divisor 1999)
;; => 1999
(smallest-divisor 19999)
;; => 7

2010-10-27

問題1.20

Common Lispでは、

(defun gcd (a b)
  (if (zerop b)
      a
      (gcd b (remainder a b))))

実行されるremainderを#|→|#で示す。正規順序評価では、

(gcd 206 40)

(if (zerop 40)
    206
    (gcd 40 (remainder 206 40)))

(gcd 40 (remainder 206 40))

(if (zerop #|→|#(remainder 206 40))
    40
    (gcd (remainder 206 40)
         (remainder 40 (remainder 206 40))))

;; 1st remainder call

(if (zerop 6)
    40
    (gcd (remainder 206 40)
         (remainder 40 (remainder 206 40))))

(gcd (remainder 206 40)
     (remainder 40 (remainder 206 40)))

(if (zerop (remainder 40 #|→|#(remainder 206 40)))
    (remainder 206 40)
    (gcd (remainder 40 (remainder 206 40))
         (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))

;; 2nd remainder call

(if (zerop #|→|#(remainder 40 6))
    (remainder 206 40)
    (gcd (remainder 40 (remainder 206 40))
         (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))

;; 3rd remainder call

(if (zerop 4)
    (remainder 206 40)
    (gcd (remainder 40 (remainder 206 40))
         (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))

(gcd (remainder 40 (remainder 206 40))
     (remainder (remainder 206 40) (remainder 40 (remainder 206 40))))

(if (zerop (remainder (remainder 206 40)
                      (remainder 40 #|→|#(remainder 206 40))))
    (remainder 40 (remainder 206 40))
    (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))))))

;; 4th remainder call

(if (zerop (remainder #|→|#(remainder 206 40) #|→|#(remainder 40 6)))
    (remainder 40 (remainder 206 40))
    (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))))))

;; 5th and 6th remainder calls

(if (zerop #|→|#(remainder 6 4))
    (remainder 40 (remainder 206 40))
    (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))))))

;; 7th remainder call

(if (zerop 2)
    (remainder 40 (remainder 206 40))
    (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))))))

(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 (zerop (remainder (remainder 40 (remainder 206 40))
                      (remainder (remainder 206 40)
                                 (remainder 40 #|→|#(remainder 206 40)))))
    (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
    (gcd (remainder (remainder 40 (remainder 206 40))
                    (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40))))
         (remainder (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40)))
                    (remainder (remainder 40 (remainder 206 40))
                               (remainder (remainder 206 40)
                                          (remainder 40
                                                     (remainder 206 40)))))))

;; 8th remainder call

(if (zerop (remainder (remainder 40 #|→|#(remainder 206 40))
                      (remainder #|→|#(remainder 206 40)
                                 #|→|#(remainder 40 6))))
    (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
    (gcd (remainder (remainder 40 (remainder 206 40))
                    (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40))))
         (remainder (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40)))
                    (remainder (remainder 40 (remainder 206 40))
                               (remainder (remainder 206 40)
                                          (remainder 40
                                                     (remainder 206 40)))))))

;; 9th, 10th and 11th remainder calls

(if (zerop (remainder #|→|#(remainder 40 6) #|→|#(remainder 6 4)))
    (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
    (gcd (remainder (remainder 40 (remainder 206 40))
                    (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40))))
         (remainder (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40)))
                    (remainder (remainder 40 (remainder 206 40))
                               (remainder (remainder 206 40)
                                          (remainder 40
                                                     (remainder 206 40)))))))

;; 12th and 13th remainder calls

(if (zerop #|→|#(remainder 4 2))
    (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
    (gcd (remainder (remainder 40 (remainder 206 40))
                    (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40))))
         (remainder (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40)))
                    (remainder (remainder 40 (remainder 206 40))
                               (remainder (remainder 206 40)
                                          (remainder 40
                                                     (remainder 206 40)))))))

;; 14th remainder call

(if (zerop 0)
    (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
    (gcd (remainder (remainder 40 (remainder 206 40))
                    (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40))))
         (remainder (remainder (remainder 206 40)
                               (remainder 40 (remainder 206 40)))
                    (remainder (remainder 40 (remainder 206 40))
                               (remainder (remainder 206 40)
                                          (remainder 40
                                                     (remainder 206 40)))))))

(remainder (remainder 206 40) (remainder 40 #|→|#(remainder 206 40)))

;; 15th remainder call

(remainder #|→|#(remainder 206 40) #|→|#(remainder 40 6))

;; 16th and 17th remainder calls

#|→|#(remainder 6 4)

;; 18th remainder call

2

となり、18回実行される。作用的順序では、

(gcd 206 40)

(if (zerop 40)
    206
    (gcd 40 (remainder 206 40)))

(gcd 40 #|→|#(remainder 206 40))

;; 1st remainder call

(gcd 40 6)

(if (zerop 6)
    40
    (gcd 6 (remainder 40 6)))

(gcd 6 #|→|#(remainder 40 6))

;; 2nd remainder call

(gcd 6 4)

(if (zerop 4)
    6
    (gcd 4 (remainder 6 4)))

(gcd 4 #|→|#(remainder 6 4))

;; 3rd remainder call

(gcd 4 2)

(if (zerop 2)
    4
    (gcd 2 (remainder 4 2)))

(gcd 2 #|→|#(remainder 4 2))

;; 4th remainder call

(gcd 2 0)

(if (zerop 0)
    2
    (gcd 0 (remainder 2 0)))

2

となり、4回実行される。

2010-10-23

問題1.19

定義により、(Tpq)^2は

a←(bp + aq)q + (bq + aq + ap)q + (bq + aq + ap)p
b←(bp + aq)p + (bq + aq + ap)q

であり、これがTp'q'と等しいのであれば、

bq' + aq' + ap' = (bp + aq)q + (bq + aq + ap)q + (bq + aq + ap)p
bp' + aq' = (bp + aq)p + (bq + aq + ap)q

が成り立つ。この連立方程式を解くと、

aq' = (bp + aq)p + (bq + aq + ap)q - bp'
 q' = (bp^2 + bq^2 - bp') / a + q^2 + 2pq

b{(bp^2 + bq^2 - bp') / a + q^2 + 2pq} + a{(bp^2 + bq^2 - bp') / a + q^2 + 2pq} + ap'
  = (bp + aq)q + (bq + aq + ap)q + (bq + aq + ap)p
(a^2 - b^2 - ab)p' = (a^2 - b^2 - ab)p^2 + (a^2 - b^2 - ab)q^2
                p' = p^2 + q^2

b(p^2 + q^2) + aq' = (bp + aq)p + (bq + aq + ap)q
 bp^2 + bq^2 + aq' = bp^2 + apq + bq^2 + aq^2 + apq
                   = bp^2 + aq^2 + bq^2 + 2apq
               aq' = aq^2 + 2apq
                q' = q^2 + 2pq

となり、p' = p^2 + q^2、q' = q^2 + 2pqのとき、(Tpq)^2 = Tp'q'は成立する。

手続きfibの定義は以下の通り。

(5am:test exercise-1.19
  (5am:is (= 0 (fib 0)))
  (5am:is (= 1 (fib 1)))
  (5am:is (= 5 (fib 5)))
  (5am:is (= (fib 10)
             (+ (fib 8) (fib 9)))))

(defun square (x)
  (* x x))

(defun fib-iter (a b p q count)
  (cond ((zerop count) b)
        ((evenp count)
         (fib-iter a
                   b
                   (+ (square p) (square q))
                   (+ (square q) (* 2 p q))
                   (/ count 2)))
        (t (fib-iter (+ (* b q) (* a q) (* a p))
                     (+ (* b p) (* a q))
                     p
                     q
                     (1- count)))))

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

2010-10-21

問題1.18

(5am:test exercise-1.18
  (5am:is (= 0 (multi 1 0)))
  (5am:is (= 6 (multi 2 3)))
  (5am:is (= (multi 4 5)
             (multi 5 4))))

(defun double (x)
  (* x 2))

(defun halve (x)
  (/ x 2))

(defun multi-iter (a b r)
  (cond ((zerop b) r)
        ((evenp b) (multi-iter (double a) (halve b) r))
        (t (multi-iter a (1- b) (+ r a)))))

(defun multi (a b)
  (multi-iter a b 0))

問題1.17

(5am:test exercise-1.17
  (5am:is (= 0 (multi 1 0)))
  (5am:is (= 6 (multi 2 3)))
  (5am:is (= (multi 4 5)
             (multi 5 4))))

(defun double (x)
  (* x 2))

(defun halve (x)
  (/ x 2))

(defun multi (a b)
  (cond ((zerop b) 0)
        ((evenp b) (double (multi a (halve b))))
        (t (+ a (multi a (1- b))))))

2010-10-18

問題1.16

(5am:test exercise-1.16
  (5am:is (= 1 (fast-expt 2 0)))
  (5am:is (= 2 (fast-expt 2 1)))
  (5am:is (= 8 (fast-expt 2 3)))
  (5am:is (= (* 2 (fast-expt 2 9))
             (fast-expt 2 10))))

(defun square (x)
  (* x x))

(defun fast-expt-iter (b n a)
  (cond ((zerop n) a)
        ((evenp n) (fast-expt-iter (square b) (/ n 2) a))
        (t (fast-expt-iter b (1- n) (* a b)))))

(defun fast-expt (b n)
  (fast-expt-iter b n 1))

2010-10-17

問題1.15

aについて。(sine 12.15)でpが呼ばれるのは5回。以下traceの出力。

> (sine 12.15)
0> Calling (P 0.049999997) 
<0 P returned 0.1495
0> Calling (P 0.1495) 
<0 P returned 0.43513453
0> Calling (P 0.43513453) 
<0 P returned 0.9758465
0> Calling (P 0.9758465) 
<0 P returned -0.7895632
0> Calling (P -0.7895632) 
<0 P returned -0.39980328
-0.39980328

bについて。ステップ数はsineが呼ばれる回数に比例する。ここで、sineが呼ばれる回数をnとすると、

a / 3^n ≦ 0.1 < a / 3^(n - 1)

という関係が成り立つ。これは、

a / 3^n ≦ 0.1 < a / 3^(n - 1)
a / 0.1 ≦ 3^n < 3 * a / 0.1
log3(a / 0.1) ≦ n < log3(3 * a / 0.1)
log3(a) - log3(0.1) ≦ n < log3(a) - log3(0.1) + 1

よって、nは3を底とするaの対数関数であることが分かる。sineではスペースとステップ数は単純に比例するため、増加の程度は共にΘ(log a)。

問題1.14

(cc  11 5) - (cc  11 4) - (cc  11 3) - (cc  11 2) - (cc  11 1) - (cc  11 0)
    |            |            |            |            |
(cc -39 5)   (cc -14 4)       |            |        (cc  10 1) - (cc  10 0)
                              |            |            |
                              |            |        (cc   9 1) - (cc   9 0)
                              |            |            |
                              |            |        (cc   8 1) - (cc   8 0)
                              |            |            |
                              |            |        (cc   7 1) - (cc   7 0)
                              |            |            |
                              |            |        (cc   6 1) - (cc   6 0)
                              |            |            |
                              |            |        (cc   5 1) - (cc   5 0)
                              |            |            |
                              |            |        (cc   4 1) - (cc   4 0)
                              |            |            |
                              |            |        (cc   3 1) - (cc   3 0)
                              |            |            |
                              |            |        (cc   2 1) - (cc   2 0)
                              |            |            |
                              |            |        (cc   1 1) - (cc   1 0)
                              |            |            |
                              |            |        (cc   0 1)
                              |            |
                              |        (cc   6 2) - (cc   6 1) - (cc   6 0)
                              |            |            |
                              |            |        (cc   5 1) - (cc   5 0)
                              |            |            |
                              |            |        (cc   4 1) - (cc   4 0)
                              |            |            |
                              |            |        (cc   3 1) - (cc   3 0)
                              |            |            |
                              |            |        (cc   2 1) - (cc   2 0)
                              |            |            |
                              |            |        (cc   1 1) - (cc   1 0)
                              |            |            |
                              |            |        (cc   0 1)
                              |            |
                              |        (cc   1 2) - (cc   1 1) - (cc   1 0)
                              |            |            |
                              |        (cc  -4 2)   (cc   0 1)
                              |
                          (cc   1 3) - (cc   1 2) - (cc   1 1) - (cc   1 0)
                              |            |            |
                          (cc  -9 3)   (cc  -4 2)   (cc   0 1)

プロセスが使うスペースについて。木構造再帰的プロセスに必要なスペースは、木構造の最大の深さに比例する。1セントだけで両替した場合が最も木構造が深く、両替する金額に比例するため、増加の程度はΘ(n)。

プロセスのステップ数について。両替する金額をnとする。1セントだけで両替した場合、nに比例する深さのステップの枝が一本、1セントと5セントで両替した場合、1セントでの両替に分岐する枝が、nに比例する本数存在する。1セントと5セントと10セントの場合、1セントと5セントの両替に分岐する枝が同様となる。このように、ステップ数は、nを底、硬貨の種類を指数とする冪乗に比例する。つまり、五種類の硬貨での増加の程度はΘ(n^5)。

2010-10-14

問題1.13

ψ = (1 - √5) / 2
f(n) = (φ^n - ψ^n) / √5

と仮定する。

f(0) = 0        ― (1)
f(1) = 1        ― (2)

となり、

f(n - 1) + f(n - 2) = (φ^(n - 1) - ψ^(n - 1)) / √5 + (φ^(n - 2) - ψ^(n - 2)) / √5
                    = (φ^(n - 2) * (φ + 1) - ψ^(n - 2) * (ψ + 1)) / √5     ― (3)

となる。ここで、

φ^2 = ((1 + √5) / 2)^2 = (3 + √5) / 2 = (1 + √5) / 2 + 1
    = φ + 1
ψ^2 = ((1 - √5) / 2)^2 = (3 - √5) / 2 = (1 - √5) / 2 + 1
    = ψ + 1

であるので、それぞれ(3)に代入すると、

f(n - 1) + f(n - 2) = (φ^(n - 2) * φ^2 - ψ^(n - 2) * ψ^2) / √5
                    = (φ^n - ψ^n) / √5
                    = f(n)      ― (4)

Fib(n)の定義は

         0                              n = 0
Fib(n) = 1                              n = 1
         Fib(n - 1) + Fib(n - 2)        otherwise.

であるから、(1)、(2)、(4)より

Fib(n) = f(n)   ― (5)

Fib(n)がφ^n / √5に最も近い整数であるなら、

Fib(n) - 0.5 < φ^n / √5 < Fib(n) + 0.5        ― (6)

が成り立つ。(5)より、

               f(n) - 0.5 < φ^n / √5 < f(n) + 0.5
   (φ^n - ψ^n) / √5 - 0.5 < φ^n / √5 < (φ^n - ψ^n) / √5 + 0.5
φ^n / √5 - ψ^n / √5 - 0.5 < φ^n / √5 < φ^n / √5 - ψ^n / √5 + 0.5      ― (7)

となる。ここで、

ψ^n / √5 = ((1 - √5) / 2)^n / √5

だが、

2 < √5 < 3

であるため、

|ψ^n / √5| < 0.5

は自明であり、(7)および(6)が成り立つ。よって、Fib(n)はφ^n / √5に最も近い整数である。

(証明終)

2010-10-13

問題1.12

(5am:test exercise-1.12
  (5am:is (= 1 (pascals-triangle 1 1)))
  (5am:is (= 6 (pascals-triangle 3 5)))
  (5am:is (= (+ (pascals-triangle 4 9)
                (pascals-triangle 5 9))
             (pascals-triangle 5 10)))
  (5am:is-false (pascals-triangle 3 2))) ; invalid coord

(defun pascals-triangle (x y)
  (cond ((> x y) nil)
        ((or (= x 1) (= x y)) 1)
        (t (+ (pascals-triangle (1- x) (1- y))
              (pascals-triangle x (1- y))))))

問題1.11

(5am:test exercise-1.11
  (5am:is (zerop (f 0)))
  (5am:is (= 2 (f 2)))
  (5am:is (= (+ (f 2)
                (* 2 (f 1))
                (* 3 (f 0)))
             (f 3))))

;; recursive
(defun f (n)
  (if (< n 3)
      n
      (+ (f (1- n))
         (* 2 (f (- n 2)))
         (* 3 (f (- n 3))))))

;; (iter (for i below 10) (collect (f i)))
;; => (0 1 2 4 11 25 59 142 335 796)

;; f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3)
;; f(3) = f(2) + 2f(1) + 3f(0)

;; 0   1   2
;; a + b + c

;; a <- b
;; b <- c
;; c <- 3a + 2b + c

;; iterative
(defun f (n)
  (if (< n 3)
      n
      (f-iter 0 1 2 n)))

(defun f-iter (a b c n)
  (if (< n 3)
      c
      (f-iter b c (+ (* 3 a) (* 2 b) c) (1- n))))

2010-08-08

問題1.10

(A 1 10)
(A 0 (A 1 9))
(A 0 (A 0 (A 1 8)))
(A 0 (A 0 (A 0 (A 1 7))))
(A 0 (A 0 (A 0 (A 0 (A 1 6)))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 1 5))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 4)))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 3))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 2)))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1))))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 2)))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 4))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 8)))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 16))))))
(A 0 (A 0 (A 0 (A 0 (A 0 32)))))
(A 0 (A 0 (A 0 (A 0 64))))
(A 0 (A 0 (A 0 128)))
(A 0 (A 0 256))
(A 0 512)
1024

(A 2 4)
(A 1 (A 2 3))
(A 1 (A 1 (A 2 2)))
(A 1 (A 1 (A 1 (A 2 1))))
(A 1 (A 1 (A 1 2)))
(A 1 (A 1 (A 0 (A 1 1))))
(A 1 (A 1 (A 0 2)))
(A 1 (A 1 4))
(A 1 (A 0 (A 1 3)))
(A 1 (A 0 (A 0 (A 1 2))))
(A 1 (A 0 (A 0 (A 0 (A 1 1)))))
(A 1 (A 0 (A 0 (A 0 2))))
(A 1 (A 0 (A 0 4)))
(A 1 (A 0 8))
(A 1 16) ; 2^16
; 中略
65536

(A 3 3)
(A 2 (A 3 2))
(A 2 (A 2 (A 3 1)))
(A 2 (A 2 2))
(A 2 (A 1 (A 2 1)))
(A 2 (A 1 2))
(A 2 (A 0 (A 1 1)))
(A 2 (A 0 2))
(A 2 4)
65536

fは2n、gは2n、hは2↑↑n。

2010-08-07

問題1.9

(defun + (a b)
  (if (= a 0)
      b
      (inc (+ (dec a) b))))

(+ 4 5)
(inc (+ 3 5))
(inc (inc (+ 2 5)))
(inc (inc (inc (+ 1 5))))
(inc (inc (inc (inc (+ 0 5)))))
(inc (inc (inc (inc 5))))
(inc (inc (inc 6)))
(inc (inc 7))
(inc 8)
9

となり、再帰的プロセス。

(defun + (a b)
  (if (= a 0)
      b
      (+ (dec a) (inc b))))

(+ 4 5)
(+ 3 6)
(+ 2 7)
(+ 1 8)
(+ 0 9)
9

となり、反復的プロセス。

問題1.8

(defun good-enough? (guess x)
  (< (abs (- (expt guess 3) x)) 0.001))

(defun improve (guess x)
  (/ (+ (/ x (expt guess 2)) (* guess 2)) 3))

(defun cbrt-iter (guess x)
  (if (good-enough? guess x)
      guess
      (cbrt-iter (improve guess x) x)))

(defun cbrt (x)
  (cbrt-iter 1.0 x))

二乗や三乗を求める関数の定義は自明なので、exptで。

2010-03-04

問題1.7

小さい数について。

CL-USER> (mysqrt 0.0001)
0.032308448
CL-USER> (* (mysqrt 0.0001) (mysqrt 0.0001))
0.0010438358

となり、good-enough?での許容値より小さい数には上手く動作しない。

大きい数について。

CL-USER> (mysqrt 1.0e20)

; デバッガへ
FLOATING-POINT-OVERFLOW detected
   [Condition of type FLOATING-POINT-OVERFLOW]

CL-USER> (trace good-enough?)
NIL
CL-USER> (mysqrt 1.0d29)
0> Calling (GOOD-ENOUGH? 1.0 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 5.0D+28 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.5D+28 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.25D+28 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 6.25D+27 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.125D+27 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.5625D+27 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 7.8125D+26 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.90625D+26 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.953125D+26 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 9.765625D+25 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 4.8828125D+25 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.44140625D+25 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.220703125D+25 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 6.103515625D+24 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.0517578125D+24 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.52587890625D+24 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 7.62939453125D+23 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.814697265625D+23 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.9073486328125D+23 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 9.5367431640625D+22 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 4.76837158203125D+22 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.384185791015625D+22 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.1920928955078126D+22 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 5.960464477539067D+21 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.980232238769542D+21 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.4901161193847878D+21 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 7.450580596924274D+20 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.725290298462808D+20 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.8626451492327463D+20 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 9.313225746190575D+19 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 4.656612873148975D+19 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.3283064366818615D+19 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.1641532185556791D+19 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 5.820766097073363D+18 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.910383057126616D+18 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.4551915457431772D+18 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 7.275958072313265D+17 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.637979723351356D+17 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.8189912360648666D+17 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 9.094983668087334D+16 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 4.547546809403519D+16 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.2738833540922676D+16 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.137161565194354D+16 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 5.690204738559249D+15 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 2.8538893993246385D+15 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 1.444464649808892D+15 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 7.568472253152072D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 4.444871434833608D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.347327645857288D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.167392710266006D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.162281790337874D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.162277660171076D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.162277660168379D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.162277660168379D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
0> Calling (GOOD-ENOUGH? 3.162277660168379D+14 1.0D+29) 
<0 GOOD-ENOUGH? returned NIL
; 以下無限ループ

桁溢れを起こしたり、精度の問題で処理が終わらなかったりする。

もうひとつの戦略を実装するコードは、

(defun sqrt-iter (guess prev-guess x)
  (if (good-enough? guess prev-guess)
      guess
      (sqrt-iter (improve guess x) guess x)))

(defun improve (guess x)
  (average guess (/ x guess)))

(defun average (x y)
  (/ (+ x y) 2))

(defun good-enough? (guess prev-guess)
  (< (abs (- 1.0 (/ guess prev-guess))) 0.001))

(defun mysqrt (x)
  (sqrt-iter 1.0 x x))

結果は、

CL-USER> (mysqrt 0.0001)
0.01
CL-USER> (mysqrt 1.0d29)
3.162277660171076D+14

小さな数でも大きな数でも正常に動作する。good-enough?のアルゴリズムの変更によって、桁溢れも起こらない。

2010-03-03

問題1.6

new-ifは手続きなので、predicateの結果に関わらず、then-clauseとelse-clauseが評価される。そのため、new-ifによって定義されたsqrt-iterでは、else-clauseにあたるsqrt-iterの呼び出しが、good-enough?の結果に関わらずに評価されてしまい、それによって評価が終了しない。

2010-02-24

問題1.1

  • 10
  • 12
  • 8
  • 3
  • 6
  • 実装依存
  • 実装依存
  • 19
  • nil
  • 4
  • 16
  • 6
  • 16

問題1.2

(/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5)))))
   (* 3 (- 6 2) (- 2 7)))

問題1.3

(defun f (x y z)
  (if (< x y)  
      (if (< x z)
          (+ (* y y) (* z z))
          (+ (* x x) (* y y)))
      (if (< y z)
          (+ (* x x) (* z z))
          (+ (* x x) (* y y)))))