From 604cf09ccdc5bd8f04b985a8f595b118070a8dd2 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Thu, 15 Oct 2020 20:38:28 -0400 Subject: [PATCH] Finish exercises for chapter 1 section 1 and 2 --- ex-1_1-8.scm => ex-1_01-10.scm | 114 +++++++++++++- ex-1_11-20.scm | 186 +++++++++++++++++++++++ ex-1_21-28.scm | 264 +++++++++++++++++++++++++++++++++ run | 8 + run.sh | 3 - 5 files changed, 567 insertions(+), 8 deletions(-) rename ex-1_1-8.scm => ex-1_01-10.scm (55%) create mode 100644 ex-1_11-20.scm create mode 100644 ex-1_21-28.scm create mode 100755 run delete mode 100755 run.sh diff --git a/ex-1_1-8.scm b/ex-1_01-10.scm similarity index 55% rename from ex-1_1-8.scm rename to ex-1_01-10.scm index 306c69c..0da75e4 100644 --- a/ex-1_1-8.scm +++ b/ex-1_01-10.scm @@ -18,12 +18,12 @@ ; (else -1)) ; (+ a 1)) -> -(display "ex-1.2 - ") +(display "\nex-1.2 - ") (display (/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5))))) (* 3 (- 6 2) (- 2 7)))) (newline) -(display "ex-1.3 - ") +(display "\nex-1.3 - ") (define (sum-squares a b) (+ (* a a) (* b b))) @@ -35,11 +35,11 @@ (display (sum-squares-max 2 -6 1)) (newline) -(display "ex-1.4 - Operator becomes + or - depending on the value of b\n") +(display "\nex-1.4 - Operator becomes + or - depending on the value of b\n") ; (define (a-plus-abs-b a b) ; ((if (> b 0) + -) a b)) -(display "ex-1.5 - Only normal-order terminates\n") +(display "\nex-1.5 - Only normal-order terminates\n") ;(define (p) (p)) ;(define (test x y) ; (if (= x 0) 0 y)) @@ -47,7 +47,7 @@ ; Will not terminate: ;(display (test 0 (p))) -(display "\n; Square roots via Newton's Method") (newline) +(display "\nexample - Square roots via Newton's Method") (newline) (define (sqrt-iter guess x) (if (good-enough? guess x) guess @@ -131,3 +131,107 @@ (display (cbrt 27)) (newline) (display (cbrt 0.001)) (newline) + + +(newline) (display "ex-1.9 - see comments in code\n") + +;(define (+ a b) +; (if (= a 0) +; b +; (inc (+ (dec a) b)))) +; + 3 2 +; (inc (+ 2 2)) +; (inc (inc (+ 1 2))) +; (inc (inc (inc (+ 0 2))) +; (inc (inc (inc 2))) +; -> recursive process + +;(define (+ a b) +; (if (= a 0) +; b +; (+ (dec a) (inc b)))) +; (+ 3 2) +; (+ 2 3) +; (+ 1 4) +; (+ 0 5) +; 5 +; -> iterative process + + +(display "\nex-1.10 - Ackermann") (newline) +(define (A x y) + (cond ((= y 0) 0) + ((= x 0) (* 2 y)) + ((= y 1) 2) + (else (A (- x 1) + (A x (- y 1)))))) + +(display (A 1 10)) (newline) +; (A 1 10) +; (A 0 (A 1 9)) +; (* 2 (A 1 9)) +; (* 2 (A 0 (A 1 8)) +; (* 2 (* 2 (A 1 8)) +; -> 2^10 = 1024 +(display (A 2 4)) (newline) +; (A 2 4) +; (A 1 (A 2 3)) +; (2 ^ (A 2 3)) +; (2 ^ (A 1 (A 2 2)) +; (2 ^ 2 ^ (A 2 2) +; -> 2^2^2^2 = 65536 +(display (A 3 3)) (newline) +; (A 3 3) +; (A 2 (A 3 2)) +; (2 ^* (A 3 2)) +; (2 ^* (A 2 (A 3 1))) +; (2 ^* 2 ^* 2) +; (2 ^* (2^2)) +; (2^2^2^2) = 65536 + +(define (f n) (A 0 n)) ; f(n)=2*n +(display (f 111)) (newline) + +(define (g n) (A 1 n)) ; g(n)=2^n +(display (g 12)) (newline) + +(define (h n) (A 2 n)) ; h(n)=2^2^2^... or 2^(h(n-1)) +(display (h 4)) (newline) + + +(newline) (display "example - Couting Change") (newline) + +(define (dec x) (- x 1)) + +(define (counting-change-iter amount current-coin) + (cond ((= amount 0) 1) + ((= current-coin 0) 0) + ((< amount 0) 0) + (else (+ (counting-change-iter (- amount (list-of-coins current-coin)) current-coin) + (counting-change-iter amount (dec current-coin))) +))) + +(define (list-of-coins coin-index) + (cond ((= coin-index 1) 1) + ((= coin-index 2) 5) + ((= coin-index 3) 10) + ((= coin-index 4) 25) + ((= coin-index 5) 50))) + +(define (count-change amount) (counting-change-iter amount 5)) + +(display "(count-change 100) = ") +(display (count-change 100)) (newline) + +; Try to implement a better version. Worked in Python. See Euler. +;(define (counting-change-iter amount count-coin current-coin) +; (cond ((= current-coin 0) 0) +; ((<= amount 0) 0) +; (else (+ (counting-change-iter (- amount (list-of-coins current-coin)) (+ count-coin 1) current-coin) +; (if (and (= count-coin 0)(= (modulo amount (list-of-coins current-coin)) 0)) 1 0) +; (counting-change-iter amount 0 (- current-coin 1)) +; )))) + +;(display "(count-change 100) = ") +;(display (counting-change-iter 100 0 5)) (newline) + diff --git a/ex-1_11-20.scm b/ex-1_11-20.scm new file mode 100644 index 0000000..b273b7b --- /dev/null +++ b/ex-1_11-20.scm @@ -0,0 +1,186 @@ +(display "ex-1.11\n") + +(define (f_rec n) + (cond ((< n 3) n) + (else (+ (f_rec (- n 1)) (* 2 (f_rec (- n 2))) (* 3 (f_rec (- n 3))))))) + +(display "(f_rec 10) = ") +(display (f_rec 10)) (newline) + +(define (f_iter_step n a b c count) + (cond ((= n count) c) + (else (f_iter_step n b c (+ (* 3 a) (* 2 b) c) (+ count 1))))) + +(define (f_iter n) + (cond ((< n 3) n) + (else (f_iter_step n 0 1 2 2)))) + +(display "(f_iter 10) = ") +(display (f_iter 10)) (newline) + + +(newline) (display "ex-1.12") (newline) +(define (pascal row col) + (cond ((= row 1) 1) + ((= col 1) 1) + ((= col row) 1) + (else (+ (pascal (- row 1) (- col 1)) (pascal (- row 1) col))))) + +(display "(pascal 6 2) = ") +(display (pascal 6 3)) (newline) + + +(newline) (display "ex-1.13") (newline) +(display "I was not able to prove this.\n") + + +(newline) (display "ex-1.14") (newline) +(display "I did that on paper. See page 92 Bullet Journal 2018.") +(newline) + +(newline) (display "ex-1.15") (newline) + +(define (cube x) (* x x x)) +(define (p x) (- (* 3 x) (* 4 (cube x)))) +(define (sine angle) + (if (not (> (abs angle) 0.1)) + angle + (p (sine (/ angle 3.0))))) + +; a) (/ 12.5 3) -> 4.16 -> 1.38888 -> 0.462 -> 0.154 -> 0.051 (count = 5) +; 12.5 / 3^n < 0.1 <=> 125 < 3^n <=> 4.39 < n +(define (count-sine-calls value count) + (if (> (abs value) 0.1) (count-sine-calls (/ value 3.0) (+ 1 count)) count)) +(display "a) Calls for 12.5 = ") (display (count-sine-calls 12.5 0)) (newline) +(display (sine (* 3.14 0.5))) (newline) + +; 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? +; O(log_3 a) + +(newline) (display "ex-1.16") (newline) + +(define (expt-rec x n) + (cond ((= n 0) 1) + (else (* x (expt-rec x (- n 1)))))) + +(define (expt-iter b counter product) + (cond ((= counter 0) product) + (else (expt-iter b (- counter 1) (* product b))))) + +(define (expt b n) (expt-iter b n 1)) + +(define (fast-expt b n) + (cond ((= n 0) 1) + ((even? n) (square (fast-expt b (/ n 2)))) + (else (* b (fast-expt b (- n 1)))))) + +(define (even? n) + (= (remainder n 2) 0)) + +(define (expt-fast b n) + (expt-iter-fast b n 1)) + +(define (expt-iter-fast b n a) + (cond ((= n 1) (* b a)) + ((even? n) (expt-iter-fast (* b b) (/ n 2) a)) + (else (expt-iter-fast b (- n 1) (* a b))))) + +(display "expt-fast 2 37 = ") +(display (expt-fast 2 37)) +(newline) + + +(newline) (display "ex-1.17") (newline) +(define (mul a b) + (if (= b 0) + 0 + (+ a (mul a (- b 1))))) + +(define (double x) (+ x x)) +(define (half x) (/ x 2)) + +(define (mul-rec a b) + (cond ((= b 0) 0) + ((even? b) (double (mul-rec a (half b)))) + (else (+ a (mul-rec a (- b 1)))))) + +(display "mul-rec 17 53 = ") +(display (mul-rec 17 53)) (newline) + + +(newline) (display "ex-1.18") (newline) +(define (mul-fast-iter b n a) + (cond ((= n 0) a) + ((even? n) (mul-fast-iter (double b) (half n) a)) + (else (mul-fast-iter b (- n 1) (+ a b))))) + +(define (mul-fast a b) (mul-fast-iter a b 0)) + +(display "mul-fast 17 53 = ") +(display (mul-fast 17 53)) +(newline) + + +(newline) (display "ex-1.19") (newline) +(define (fib n) + (fib-iter 1 0 0 1 n)) +(define (fib-iter a b p q count) + (cond ((= count 0) b) + ((even? count) + (fib-iter a + b + (+ (* q q) (* p p)) ; compute p' + (+ (* q q) (* 2 p q)) ; compute q' + (/ count 2))) + (else (fib-iter (+ (* b q) (* a q) (* a p)) + (+ (* b p) (* a q)) + p + q + (- count 1))))) + +(display "fib 19 = ") +(display (fib 19)) +(newline) + + +(newline) (display "ex-1.20") (newline) +(define (gcd-naiv a b) + (cond ((= a b) a) + ((> a b) (gcd-naiv (- a b) b)) + (else (gcd-naiv a (- b a))))) + +(display "gcd-naiv 60 14 = ") +(display (gcd-naiv 60 14)) +(newline) + +(define (gcd a b) + (if (= b 0) + a + (gcd b (remainder a b)))) + +(display "gcd 60 14 = ") +(display (gcd 60 14)) +(newline) +; normaler order +; gcd 206 40 +; gcd 40 (r 206 40) +; zero? (r 206 40) +; zero? 6 +; gcd (r 206 40) (r 40 (r 206 40)) +; zero? (r 40 (r 206 40)) +; gcd (r 40 (r 206 40)) (r (r 206 40) (r 40 (r 206 40)))) +; etc. + +; applicative order +; gcd 206 40 +; gcd 40 (r 206 40) +; gcd 40 6 +; gcd 6 (r 40 6) +; gcd 6 4 +; gcd 4 (r 6 4) +; gcd 4 2 +; gcd 2 (r 4 2) +; gcd 2 0 -> 2 + + diff --git a/ex-1_21-28.scm b/ex-1_21-28.scm new file mode 100644 index 0000000..3eb24aa --- /dev/null +++ b/ex-1_21-28.scm @@ -0,0 +1,264 @@ +(display "ex-1.21") (newline) + +(define (smallest-divisor n) + (find-divisor n 2)) + +(define (find-divisor n test-divisor) + (cond ((> (square test-divisor) n) n) + ((divides? test-divisor n) test-divisor) + (else (find-divisor n (+ test-divisor 1))))) + +(define (divides? a b) (= (remainder b a) 0)) + +(define (prime? n) (= n (smallest-divisor n))) + +(display "(smallest-divisor 33) = ") +(display (smallest-divisor 33)) +(newline) +(display "(prime? 37) = ") +(display (prime? 37)) +(newline) + +;a^n = a (mod n) ; fermat's theorem +(define (expmod base exp m) + (cond ((= exp 0) 1) + ((even? exp) + (remainder (square (expmod base (/ exp 2) m)) + m)) + (else + (remainder (* base (expmod base (- exp 1) m)) + m)))) + +(display "(expmod 11 23 3) = ") +(display (expmod 11 23 3)) +(newline) + +(define (fermat-test n) + (define (try-it a) + (= (expmod a n n) a)) + (try-it (+ 1 (random (- n 1))))) + +(define (fast-prime? n times) + (cond ((= times 0) #t) + ((fermat-test n) (fast-prime? n (- times 1))) + (else #f))) + +(display "(fast-prime? 11) = ") +(display (fast-prime? 11 1)) + +(display "\n\nex-1.22\n") +(display "(smallest-divisor 199) = ") +(display (smallest-divisor 199)) (newline) +(display "(smallest-divisor 1999) = ") +(display (smallest-divisor 1999)) (newline) +(display "(smallest-divisor 19999) = ") +(display (smallest-divisor 19999)) (newline) + +(newline) (display "ex-1.23 - more tests in comments") + +(define (timed-prime-test n) + (newline) + (display n) + (start-prime-test n (runtime))) +(define (start-prime-test n start-time) + (if (prime? n) + (report-prime (- (runtime) start-time)))) +(define (report-prime elapsed-time) + (display " *** ") + (display elapsed-time) + ) + +(define (search-for-primes n a) + (cond ((= a 3) 0) + ((prime? n) (timed-prime-test n) (search-for-primes (+ n 1) (+ a 1))) + ((search-for-primes (+ n 1) a)))) + +(newline) (display "search-for-primes 1000") +(search-for-primes 1000 0) + +;(newline)(newline) (display "search-for-primes 100000000") +;(search-for-primes 100000000 0) +; +;(newline)(newline) (display "search-for-primes 1000000000") +;(search-for-primes 1000000000 0) +; +;(newline)(newline) (display "search-for-primes 10000000000") +;(search-for-primes 10000000000 0) +; +;(newline)(newline) (display "search-for-primes 100000000000") +;(search-for-primes 100000000000 0) + +; It is amazing how exact the time represents the expected increase. If we +; increase n by factor 10 the time increase by factor sqrt(10) = 3.3. I knew +; this, but it is cool to see it. +; search-for-primes 1000000000 +; 1000000007 *** 4.0000000000000036e-2 +; 1000000009 *** .04999999999999999 +; 1000000021 *** 5.0000000000000044e-2 +; +; search-for-primes 10000000000 +; 10000000019 *** .15000000000000002 +; 10000000033 *** .1399999999999999 +; 10000000061 *** .1399999999999999 +; +; search-for-primes 100000000000 +; 100000000003 *** .4500000000000002 +; 100000000019 *** .43999999999999995 +; 100000000057 *** .4299999999999997 + +(display "ex-1.23") (newline) + +(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))))) + +;(timed-prime-test 100000000003) +;(timed-prime-test 100000000019) +;(timed-prime-test 100000000057) + +; I did not really expect the time to half, but to 2/3. That is because we skip +; one number per every three numbers. For example, instead of 3,4,5 we do 3,5 +; 100000000003 *** .30000000000000004 +; 100000000019 *** .29 +; 100000000057 *** .28 + +(newline) (display "ex-1.24") + +(define (prime? n) (fast-prime? n 100)) + +(define (start-prime-test n start-time) + (if (prime? n) + (report-prime (- (runtime) start-time)))) + +(newline)(newline) (display "search-for-primes 1000000000000") +(search-for-primes 1000000000000 0) + +(newline)(newline) (display "search-for-primes 1000000000000000000000") +(search-for-primes 1000000000000000000000000 0) + +; This very nicely shows that in order to double the execution time we have to +; square the input. For example: + +;search-for-primes 1000000000000 +; 1.9999999999999962e-2 / 9.999999999999981e-3 = 2 + +; I feel like all discrepancies must be explained by the random function. If +; the factor is bigger it takes more steps. +;1000000000039 *** 9.999999999999981e-3 +;1000000000061 *** 9.999999999999981e-3 +;1000000000063 *** 9.999999999999981e-3 + +;search-for-primes 1000000000000000000000 +;1000000000000000000000007 *** 1.9999999999999962e-2 +;1000000000000000000000049 *** .02999999999999997 +;1000000000000000000000121 *** 3.0000000000000027e-2 + +(newline)(newline) (display "ex-1.25") (newline) + +;(define (square m) +; (display "square ")(display m)(newline) +; (* m m)) + +(define (expmod-naiv base exp m) + (remainder (fast-expt base exp) m)) + +; Terminates quickly. +(display "(expmod 10000000000 1000000 100) ; runs quickly") (newline) +(expmod 10000000000 1000000 100) + +; Scheme can handle arbitrary precision arithmetic. However, +; when the numbers get really big that takes more time. When +; using expmod the number to be squared stays lower than the +; prime to be tested. +; When we do the naiv approach the numbers become really big +; and the same call will not terminate. +(display "(expmod-naiv 10000000000 1000000 100) ; doesn't terminate") +;(expmod-naiv 10000000000 1000000 100) + +(newline)(newline) (display "ex-1.26 - see comment") (newline) +; With that function for every of the log(n) steps the +; function is called twice which means the complexity +; grows exponantially which gives us log(n)^2 a O(n) process. + +(newline) (display "ex-1.27") (newline) + +; Do fermat tests for all a in [2, p - 1]. +(define (fermat-test-all n) (fermat-test-all-iter 2 n)) + +(define (fermat-test-all-iter x n) + (cond ((= x n) #t) + ((= (expmod x n n) x) (fermat-test-all-iter (+ x 1) n)) + (else (display x)(newline) #f))) + + +; 561, 1105, 1729, 2465, 2821, and 6601 +(display (fermat-test-all 561)) (newline) +(display (fermat-test-all 1105)) (newline) +(display (fermat-test-all 1729)) (newline) +(display (fermat-test-all 2465)) (newline) +(display (fermat-test-all 2821)) (newline) +(display (fermat-test-all 6601)) (newline) +; I am really unhappy with that exercise, because it wouldn't +; worked if we used the condition a ** (p - 1) = 1 (mod p) and +; I don't know the exact reason for that. + +(newline) (display "ex-1.28") (newline) + +; Exercise 1.28. One variant of the Fermat test that cannot be fooled is called +; the Miller-Rabin test (Miller 1976; Rabin 1980). This starts from an alternate +; form of Fermat's Little Theorem, which states that if n is a prime number and +; a is any positive integer less than n, then a raised to the (n - 1)st power is +; congruent to 1 modulo n. To test the primality of a number n by the +; Miller-Rabin test, we pick a random number a