From: Ramakrishnan Muthukrishnan Date: Sat, 24 Apr 2010 18:14:19 +0000 (+0530) Subject: solutions for exercises upto 1.26 X-Git-Url: https://git.rkrishnan.org/(%5B%5E?a=commitdiff_plain;h=fa7805f0d3970441e2eb968e0754a326392e1d05;p=sicp.git solutions for exercises upto 1.26 --- diff --git a/src/sicp/ch1_1.clj b/src/sicp/ch1_1.clj index 34856cd..bfe8bdc 100644 --- a/src/sicp/ch1_1.clj +++ b/src/sicp/ch1_1.clj @@ -1,18 +1,12 @@ -(ns sicp.ch1-1) - -(defn square [x] (* x x)) +(ns sicp.ch1-1 + (:use sicp.utils)) (defn sum-of-squares [x y] (+ (square x) (square y))) -(defn f [a] +(defn ff [a] (sum-of-squares (+ a 1) (* a 2))) -(defn abs - "find absolute value of x" - [x] - (if (< x 0) (- x) x)) - (defn abs1 [x] (cond (< x 0) (- x) :else x)) @@ -114,7 +108,7 @@ (average guess (/ x guess))) (defn good-enough? [guess x] - (< (abs (- (square guess) x)) 0.001)) + (< (myabs (- (square guess) x)) 0.001)) (defn sqrt-iter [guess x] (if (good-enough? guess x) @@ -217,7 +211,7 @@ (/ (+ x y) 2)) (defn good-enough? [old-guess new-guess x] - (< (/ (abs (- new-guess old-guess)) new-guess) 0.0001)) + (< (/ (myabs (- new-guess old-guess)) new-guess) 0.0001)) (defn sqrt [x] (sqrt-iter x 1.0 x)) @@ -248,9 +242,6 @@ user> (sqrt 81) 9.000000000007091 ) ;; exercise 1.8: cube root -(defn cube [x] - (* x x x)) - (defn improve [guess x] (/ (+ (/ x (square guess)) (* 2 guess)) 3)) @@ -291,7 +282,7 @@ user> (/ (+ x y) 2)) (defn- good-enough? [guess x] - (< (abs (- (square guess) x)) 0.001)) + (< (myabs (- (square guess) x)) 0.001)) (defn sqrt [x] (sqrt-iter 1.0 x)) diff --git a/src/sicp/ch1_2.clj b/src/sicp/ch1_2.clj index cd210be..43cebbe 100644 --- a/src/sicp/ch1_2.clj +++ b/src/sicp/ch1_2.clj @@ -1,5 +1,7 @@ (ns sicp.ch1-2 - (:use (clojure.contrib trace math))) + (:use [sicp utils] + [clojure.contrib.math :only (sqrt expt)] + [clojure.contrib.trace :only (dotrace)])) (defn factorial [n] @@ -141,14 +143,14 @@ :else (A (- x 1) (A x (- y 1))))) -(comment -user> (A 1 10) -1024 -user> (A 2 4) -65536 -user> (A 3 3) -65536 -) +;; (comment +;; user> (A 1 10) +;; 1024 +;; user> (A 2 4) +;; 65536 +;; user> (A 3 3) +;; 65536 +;; ) (defn f [n] (A 0 n)) ; f(n) = 2n (defn g [n] (A 1 n)) ; g(n) = 2^n @@ -216,10 +218,10 @@ user> (A 3 3) (* 2 (f (- n 2))) (* 3 (f (- n 3)))))) -(comment -user> (map f (range 10)) -(0 1 2 4 11 25 59 142 335 796) -) +;; (comment +;; user> (map f (range 10)) +;; (0 1 2 4 11 25 59 142 335 796) +;; ) ;; ex 1.11: iterative version (defn f-iter [count prev0 prev1 prev2] @@ -419,12 +421,10 @@ See the pdfs in the directory for the answers. ;; b. What is the order of growth in space and number of steps (as ;; a function of a) used by the process generated by the sine ;; procedure when (sine a) is evaluated? -(defn cube [x] (* x x x)) - (defn p [x] (- (* 3 x) (* 4 (cube x)))) (defn sine [angle] - (if (not (> (abs angle) 0.1)) + (if (not (> (myabs angle) 0.1)) angle (p (sine (/ angle 3.0))))) @@ -475,9 +475,6 @@ See the pdfs in the directory for the answers. (* b (myexpt b (dec n))))) ;; fast version -(defn square [x] - (* x x)) - (defn fast-expt [b n] (cond (= n 0) 1 (even? n) (square (fast-expt b (/ n 2))) @@ -499,12 +496,6 @@ See the pdfs in the directory for the answers. (+ a (mult a (- b 1))))) ;; double -(defn twice [x] - (* 2 x)) - -(defn half [x] - (/ x 2)) - ;; product = 2 * (a * (b/2)) for even b ;; = a + (a * (b - 1)) for odd b (defn fast-mult [a b] @@ -561,4 +552,212 @@ See the pdfs in the directory for the answers. (defn mygcd [a b] (if (= b 0) a - (mygcd b (rem a b)))) \ No newline at end of file + (mygcd b (rem a b)))) + +;;; exercise 1.20. +;; +;; normal order - 18, applicative order - 4. +;; +;; too lazy to scan things from the notebook. May be I should instead +;; use a wiki. + +;;; section 1.2.6 Primality testing. +(defn prime? [n] + (= (smallest-divisor n) n)) + +(defn smallest-divisor [n] + (find-divisor n 2)) + +(defn find-divisor [n test-divisor] + (cond (> (square test-divisor) n) n + (divides? test-divisor n) test-divisor + :else (find-divisor n (inc test-divisor)))) + +(defn divides? [a b] + (= (rem b a) 0)) + +;; fermat's little theorem +(defn expmod [base exp m] + (cond (= exp 0) 1 + (even? exp) (rem (square (expmod base (/ exp 2) m)) + m) + :else (rem (* base (expmod base (dec exp) m)) + m))) + +(defn fermat-test [n] + (try-it (+ 1 (rand-int (- n 1))) n)) + +(defn try-it [a n] + (= a (expmod a n n))) + +(defn fast-prime? [n times] + (cond (= times 0) true + (fermat-test n) (fast-prime? n (dec times)) + :else false)) + +;; exercise 1.21 +(comment +user> (smallest-divisor 199) +199 +user> (smallest-divisor 1999) +1999 +user> (smallest-divisor 19999) +7 +) + +;; exercise 1.22 +(defn timed-prime-test [n] + (prn) + (print n) + (start-prime-test n (System/nanoTime))) + +(defn start-prime-test [n start-time] + (if (prime? n) + (report-prime (- (System/nanoTime) start-time)))) + +(defn report-prime [elapsed-time] + (print " *** ") + (print elapsed-time)) + +(defn search-for-primes [a b] + (cond (>= a b) nil + (even? a) (search-for-primes (+ 1 a) b) + (timed-prime-test a) (search-for-primes (+ 2 a) b) + :else (search-for-primes (+ 2 a) b))) + +;;; three smallest primes greater than 1000 +;;; 1009, 1013, 1019 +(take 3 (filter #(prime? %) (iterate inc 1000))) +;=> (1009 1013 1019) + +;=> 0.9642028750000001 + +;;; > 10,000: 10007, 10009, 10037 +(take 3 (filter #(prime? %) (iterate inc 10000))) +;=> (10007 10009 10037) + +;=> 1.5897884999999998 + +;;; > 100,000: 100003, 100019, 100043 +(take 3 (filter #(prime? %) (iterate inc 100000))) +;=> (100003 100019 100043) + +;=> 1.8525091250000003 + +;;; > 1,000,000: 1000003, 1000033, 1000037 +(take 3 (filter #(prime? %) (iterate inc 1000000))) +;=> (1000003 1000033 1000037) + +;=> 1.908832125 + +;; time taken seem to increase as the range increases. +;; but they are totally random on the jvm, so I can't find +;; the exact relation. + +(comment +user> (microbench 10 (take 3 (filter #(prime? %) (iterate inc 1000)))) +Warming up! +Benchmarking... +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +(1009 1013 1019) +Total runtime: 0.28404500000000005 +Highest time : 0.083949 +Lowest time : 0.019416 +Average : 0.022585000000000008 +(0.083949 0.019416 0.023257 0.020394 0.024165 0.024514 0.024374 0.020813 0.021721 0.021442) +user> (microbench 10 (take 3 (filter #(prime? %) (iterate inc 10000)))) +Warming up! +Benchmarking... +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +(10007 10009 10037) +Total runtime: 0.26462800000000003 +Highest time : 0.067118 +Lowest time : 0.020533 +Average : 0.022122125000000006 +(0.067118 0.022698 0.024095 0.023537 0.020533 0.020882 0.020603 0.021372 0.020603 0.023187) +user> (microbench 10 (take 3 (filter #(prime? %) (iterate inc 100000)))) +Warming up! +Benchmarking... +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +(100003 100019 100043) +Total runtime: 0.265118 +Highest time : 0.073263 +Lowest time : 0.020254 +Average : 0.021450125000000004 +(0.073263 0.023048 0.023467 0.021022 0.020394 0.020254 0.021302 0.020812 0.020743 0.020813) +) + +;;; can't make out any sqrt(10) relation between the numbers. may be because +;;; jvm compilation is doing some behind the scene tricks. + +;;; exercise 1.23 +(defn next-divisor [n] + (if (= n 2) + 3 + (+ n 2))) + + +(defn find-divisor [n test-divisor] + (cond (> (square test-divisor) n) n + (divides? test-divisor n) test-divisor + :else (find-divisor n (next-divisor test-divisor)))) + +(comment + I can't see any noticable difference in the speed. + ) + +;;; exercise 1.24 +(comment + (microbench 10 (take 3 (filter #(fast-prime? %) (iterate inc 1000)))) + ... + "I did not observe any difference". + ) + +;; exercise 1.25 +(defn expmod [base exp m] + (rem (fast-expt base exp) m)) + +(comment + In the case of the original expmod implementation, square and remainder + calls are interspersed, so square always deals with a small number, whereas + with the above way, we do a series of squaring and then in the end take + remainder. Squaring of big numbers are very inefficient as the CPU has to + do multi-byte arithmetic which consumes many cycles. + + So the new version is several times slower than the original. +) + +;;; exercise 1.26 +(comment + "Instead of calling (square x), Louis now makes does (* x x). In the former, + case, x is evaluated only once, where as in the second, x gets evaluated + 2x, 4x, 8x, 16x and so on (for any x which is recursive). So, if the original + computation is considered T(log_n), then the new process T(n). This can also + be illustrated with the call tree." +) + +;; exercise 1.27 diff --git a/src/sicp/core.clj b/src/sicp/core.clj new file mode 100644 index 0000000..692b8d8 --- /dev/null +++ b/src/sicp/core.clj @@ -0,0 +1,2 @@ +(ns sicp.core) + diff --git a/src/sicp/utils.clj b/src/sicp/utils.clj new file mode 100644 index 0000000..6ebbe24 --- /dev/null +++ b/src/sicp/utils.clj @@ -0,0 +1,52 @@ +(ns sicp.utils) + +(defn square [x] (* x x)) + +(defn myabs + "find absolute value of x" + [x] + (if (< x 0) (- x) x)) + +(defn cube [x] + (* x x x)) + +(defn twice [x] + (* 2 x)) + +(defn half [x] + (/ x 2)) + +(defmacro microbench + " Evaluates the expression n number of times, returning the average + time spent in computation, removing highest and lowest values. + + If the body of expr returns nil, only the timing is returned otherwise + the result is printed - does not affect timing. + + Before timings begin, a warmup is performed lasting either 1 minute or + 1 full computational cycle, depending on which comes first." + [n expr] {:pre [(> n 2)]} + `(let [warm-up# (let [start# (System/currentTimeMillis)] + (println "Warming up!") + (while (< (System/currentTimeMillis) (+ start# (* 60 1000))) + (with-out-str ~expr) + (System/gc)) + (println "Benchmarking...")) + timings# (doall + (for [pass# (range ~n)] + (let [start# (System/nanoTime) + retr# ~expr + timing# (/ (double (- (System/nanoTime) start#)) + 1000000.0)] + (when retr# (println retr#)) + (System/gc) + timing#))) + runtime# (reduce + timings#) + highest# (apply max timings#) + lowest# (apply min timings#)] + (println "Total runtime: " runtime#) + (println "Highest time : " highest#) + (println "Lowest time : " lowest#) + (println "Average : " (/ (- runtime# (+ highest# lowest#)) + (- (count timings#) 2))) + timings#))