solutions for exercises upto 1.26
authorRamakrishnan Muthukrishnan <vu3rdd@gmail.com>
Sat, 24 Apr 2010 18:14:19 +0000 (23:44 +0530)
committerRamakrishnan Muthukrishnan <vu3rdd@gmail.com>
Sat, 24 Apr 2010 18:14:19 +0000 (23:44 +0530)
src/sicp/ch1_1.clj
src/sicp/ch1_2.clj
src/sicp/core.clj [new file with mode: 0644]
src/sicp/utils.clj [new file with mode: 0644]

index 34856cdb24e510bdecd3b657f5326d1abebf8b9f..bfe8bdc71d1336583e1eff1a381a0f3c42cff8a8 100644 (file)
@@ -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))
   (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)
   (/ (+ 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))
index cd210bedfc9148e9f1f70dd094416957018396ae..43cebbe6c45fd6b01a106e212771e8a849b2c9d0 100644 (file)
@@ -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]
        :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 (file)
index 0000000..692b8d8
--- /dev/null
@@ -0,0 +1,2 @@
+(ns sicp.core)
+
diff --git a/src/sicp/utils.clj b/src/sicp/utils.clj
new file mode 100644 (file)
index 0000000..6ebbe24
--- /dev/null
@@ -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#))