Add solutions to exercises from section 1.2

To be noted: the drawing in exercise 1.14 is unfinished. I did it in a
notebook, but haven't yet had the time to put it in a txt file.
This commit is contained in:
Petar Kapriš 2025-02-04 20:58:24 +01:00
parent 16d2911936
commit a697f52405
21 changed files with 525 additions and 0 deletions

25
ex-1.09.txt Normal file
View file

@ -0,0 +1,25 @@
For the first +:
(+ 4 5)
(inc (+ (dec 4) 5))
(inc (+ 3 5))
(inc (inc (+ (dec 3) 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
For the second +:
(+ 4 5)
(+ (dec 4) (inc 5))
(+ 3 6)
(+ 2 7)
(+ 1 8)
(+ 0 9)
9
First is recursive, second is iterative.

8
ex-1.10.scm Normal file
View file

@ -0,0 +1,8 @@
#lang sicp
(define (A x y)
(cond ((= y 0) 0)
((= x 0) (* 2 y))
((= y 1) 2)
(else (A (- x 1)
(A x (- y 1))))))

17
ex-1.10.txt Normal file
View file

@ -0,0 +1,17 @@
(A 1 10)
2**10
(A 2 4)
(A 1 (A 2 3))
(A 1 (A 1 (A 2 ))
2**2**2**2
(A 3 3)
(A 2 (A 3 2))
2P3
2t(4)
(f n) == 2*n
(g n) == 2**n
(h n) == 2**2**...**2 <- n times

24
ex-1.11.scm Normal file
View file

@ -0,0 +1,24 @@
#lang sicp
; recursive process
(define (f1 n)
(if (< n 3)
n
(+ (f1 (- n 1))
(* 2 (f1 (- n 2)))
(* 3 (f1 (- n 3))))))
; iterative process
; loop for n >= 3
(define (f-iter iter-count acc1 acc2 acc3)
(let ((new-val (+ acc1 (* 2 acc2) (* 3 acc3))))
(if (= iter-count 0)
new-val
(f-iter (- iter-count 1) new-val acc1 acc2))))
(define (f2 n)
(if (< n 3)
n
(f-iter (- n 3) (f2 2) (f2 1) (f2 0))))
; these are just 2,1,0 respectively, but I felt this looks cleaner

5
ex-1.12.scm Normal file
View file

@ -0,0 +1,5 @@
#lang sicp
(define (solution n k)
(cond ((or (= n 1) (= k 1) (= k n)) 1)
(else (+ (solution (- n 1) (- k 1)) (solution (- n 1) k)))))

11
ex-1.13.txt Normal file
View file

@ -0,0 +1,11 @@
Dokazemo indukcijom, prvo za Fib(0) i Fib(1), kao baze.
Zatim dokazemo da Fib(n), sto je Fib(n-1) + Fib(n-2) sto je (phi^(n-1) -
psi^(n-1) + phi^(n-2) - psi^(n-2))/sqrt(5) da je to jednako (phi^n -
psi^n)/sqrt(5), sto je ustv lako jer je: phi^n = phi^(n-1) + phi^(n-2), isto
vazi i za psi, pa se to malo razbije i pretumba, i dobijemo: Fib(n) = (phi^n -
psi^n)/sqrt(5), za svako n u N.
Razlog zasto ovo dokazuje da je Fib(n) uvek najblizi ceo broj jeste to da psi
~=~ 0.36 < 0.5, a psi^n <<< psi, kad n>1. Dakle razlika između Fib(n) i
phi^n/sqrt(5) je uvek manja od 0.5. Dakle najblizi je ceo broj.

10
ex-1.14.txt Normal file
View file

@ -0,0 +1,10 @@
It's a very big tree.....
57 nodes, i'm not gonna type it out, around half of it is cases like:
(cc 11 1)
/ \
(cc 11 0) (cc 10 1)
/ \
(cc 10 0) (cc 9 1)
/ \
(cc 9 0) (cc 8 1)
..................................

5
ex-1.15.txt Normal file
View file

@ -0,0 +1,5 @@
Every time we call sine we end up calling
(p (sine angle/3)), which becomes
(p (p (sine angle/3/3))), etc.
So we end up calling it once each time until angle reaches 0.1/
In other words ceil(log3(12.15/0.1)) = 5

8
ex-1.16.scm Normal file
View file

@ -0,0 +1,8 @@
#lang sicp
(define (expt-aux b n a)
(cond ((= n 0) a)
((even? n) (expt-aux (* b b) (/ n 2) a))
(else (expt-aux b (- n 1) (* a b)))))
(define (solution b n)
(expt-aux b n 1))

10
ex-1.17.scm Normal file
View file

@ -0,0 +1,10 @@
#lang sicp
(define (double x) (* 2 x))
(define (halve x) (/ x 2))
(define (*aux a b acc)
(cond ((= b 0) acc)
((even? b) (*aux (double a) (halve b) acc))
(else (*aux a (- b 1) (+ acc a)))))
(define (fast-mul a b)
(*aux a b 0))

1
ex-1.18.scm Normal file
View file

@ -0,0 +1 @@
; Actually, this was already solved in the previous exercise

17
ex-1.19.scm Normal file
View file

@ -0,0 +1,17 @@
#lang sicp
(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
(+ (* p p) (* q q))
(+ (* 2 p q) (* q q))
(/ count 2)))
(else (fib-iter (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(- count 1)))))

52
ex-1.20.txt Normal file
View file

@ -0,0 +1,52 @@
(gcd 206 40)
; 40 != 0
(gcd 40 (remainder 206 40))
; in the if, remainder is computed and it isn't zero - 1c
; but I'm pretty sure this doesn't mean the remainder as the
; argument to gcd is evaluated, so we get:
(gcd (remainder 206 40) (remainder 40 (remainder 206 40)))
; in the next if, the two nested remainders must be computed - 2c
; and we're left with
(gcd (remainder 40 (remainder 206 40))
(remainder (remainder 206 40)
(remainder 40 (remainder 206 40))))
; in the next, the nested 4 are computed - 4c
; and we have:
(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)))))
; and the nested 7 must be computed. This will actually return 0, and then the
; first arg (4 nested remainder calls) must be evaluated, and that's it, but
; let's generalize.
; counting n from 0:
; notice in each n-th recursive pass, we evaluate remainder G(n+1) times.
; But also our arguments (a, b) end up having G(n+1)-level and G(n+2)-level
; indented applications of remainder, respectively, previously they had G(n)
; and G(n+1)
; here G(0)=G(1)=0, and for n>1 G(n) = G(n-1) + G(n-2) + 1
; so: G | n
----+---
; 0 | 0
; 0 | 1
; 1 | 2
; 2 | 3
; 4 | 4
; 7 | 5
; this happens until n=4, which is the last function call because that's when
; the right hand (G(5) calls) will evaluate to zero. And the final result, the
left hand, (G(4) calls) will be evaluated, and return 2.
So ultimately, the number of calls that must happen is:
G(1) + G(2) + ... + G(5) + G(4) (again)
or, more generally, if it takes n steps to compute the GCD:
Sum(G(1)..G(n+1)) + G(n) calls to remainder will be required.
In out situation, this will be 0+1+2+4+7+4 = 18 calls.
In the applicative order, the situation is far simpler, every recursive pass,
except the last, requires 1 call to remainder, so 4 calls.

18
ex-1.21.scm Normal file
View file

@ -0,0 +1,18 @@
#lang sicp
(define (square x) (* x x))
(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))
;> (smallest-divisor 199)
;199
;> (smallest-divisor 1999)
;1999
;> (smallest-divisor 19999)
;7

171
ex-1.22.scm Normal file
View file

@ -0,0 +1,171 @@
#lang sicp
(define (square x) (* x x))
(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)))
(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))
#| BEGIN (Write your solution here) |#
(define (search-for-primes a b)
(cond ((> a b) '())
((not (even? a))
(timed-prime-test a)
(search-for-primes (+ a 2) b))
(else (search-for-primes (+ a 1) b))))
#| END |#
;;;; TESTED OUTPUT
;> (search-for-primes 1000 2000)
;
;1001
;1003
;1005
;1007
;1009 *** 1
;1011
;1013 *** 1
;1015
;1017
;1019 *** 1
;1021 *** 1
;1023
;1025
;1027
;1029
;1031 *** 2
;1033 *** 1
;1035
;1037
;1039 *** 2
;1041
;1043
;1045
;> (search-for-primes 10000 10100)
;
;10001
;10003
;10005
;10007 *** 7
;10009 *** 5
;10011
;10013
;10015
;10017
;10019
;10021
;10023
;10025
;10027
;10029
;10031
;10033
;10035
;10037 *** 4
;10039 *** 5
;10041
;10043
;10045
;10047
;10049
;10051
;10053
;10055
;10057
;10059
;10061 *** 5
;10063
;10065
;10067 *** 5
;10069 *** 4
;10071
;10073
;10075
;10077
;10079 *** 4
;10081
;10083
;10085
;10087
;10089
;10091 *** 5
;10093 *** 9
;10095
;10097
;10099 *** 7()
;> (search-for-primes 100000 100100)
;
;100001
;100003 *** 16
;100005
;100007
;100009
;100011
;100013
;100015
;100017
;100019 *** 15
;100021
;100023
;100025
;100027
;100029
;100031
;100033
;100035
;100037
;100039
;100041
;100043 *** 15
;100045
;100047
;100049 *** 17
;100051
;100053
;100055
;100057 *** 16
;100059
;100061
;100063
;100065
;100067
;100069 *** 16
;100071
;100073
;100075
;100077
;100079
;100081
;100083
;100085
;100087
;100089
;100091
;100093
;100095
;100097
;100099()

26
ex-1.23.scm Normal file
View file

@ -0,0 +1,26 @@
#lang sicp
(define (square x) (* x x))
;(define (next x)
; (if (= x 2)
; 3
; (+ x 2)))
(define (next x) (+ x 1))
(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 (next test-divisor)))))
(define (divides? a b)
(= (remainder b a) 0))
(define (time-sd n)
(define start-time (runtime))
(smallest-divisor n)
(- (runtime) start-time))
; comparing (time-sd 100069) with the new and old versions
; it's roughly a 1.5 factor increase, likely because the if
; statement checking is slightly more expensive than simply increasing
; by one

54
ex-1.24.scm Normal file
View file

@ -0,0 +1,54 @@
#lang sicp
(define (square x) (* x x))
(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))))
(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 (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) true)
((fermat-test n) (fast-prime? n (- times 1)))
(else false)))
(define (prime? n)
(= n (smallest-divisor n)))
(define (timed-prime-test n)
(newline)
(display n)
(start-prime-test n (runtime)))
(define (start-prime-test n start-time)
(if (fast-prime? n 10)
(report-prime (- (runtime) start-time))))
(define (report-prime elapsed-time)
(display " *** ")
(display elapsed-time))
(define (search-for-primes a b)
(cond ((> a b) '())
((not (even? a))
(timed-prime-test a)
(search-for-primes (+ a 2) b))
(else (search-for-primes (+ a 1) b))))

2
ex-1.25.txt Normal file
View file

@ -0,0 +1,2 @@
No, because it would produce extremely large exponentials, which
would be computed more slowly. And computing the remainder would be extremely slow.

10
ex-1.26.txt Normal file
View file

@ -0,0 +1,10 @@
Essentially, instead of us halving the time of computation,
every time we have an even number, we instead halve the expmod call
but then, make that call twice, in other words, instead of
(specifically for even number):
T(n) = O(1) + T(n/2)
we get:
T(n) = O(1) + 2*T(n/2), which is going to give us roughly:
O(n)
We get a recursion tree, growing exponentially with at every recursive call,
of which there are O(log n), so exp(log(n)) = n.

27
ex-1.27.scm Normal file
View file

@ -0,0 +1,27 @@
#lang sicp
(define (square x) (* x x))
(define (expmod-aux base exp n acc)
(cond ((= 0 exp) acc)
((even? exp) (expmod-aux (remainder (square base) n) (/ exp 2) n acc))
(else (expmod-aux base (- exp 1) n (remainder (* acc base) n)))))
(define (expmod base exp n)
(expmod-aux base exp n 1))
(define (carmichael-test-aux n a)
(cond ((>= a n) #t)
((= (expmod a n n) a) (carmichael-test-aux n (+ a 1)))
(else #f)))
(define (carmichael-test n)
(carmichael-test-aux n 0))
;> (carmichael-test 561)
;#t
;> (carmichael-test 1105)
;#t
;> (carmichael-test 1729)
;#t
;> (carmichael-test 2465)
;#t
;> (carmichael-test 2821)
;#t
;> (carmichael-test 6601)
;#t
;>

24
ex-1.28.scm Normal file
View file

@ -0,0 +1,24 @@
#lang sicp
(define (square n) (* n n))
(define (expmod-aux base exp n acc)
(define sqr (remainder (square base) n))
(cond ((= 0 exp) acc)
((even? exp)
(if (and (= sqr 1)
(not (= base 1))
(not (= base (- n 1))))
0
(expmod-aux sqr (/ exp 2) n acc)))
(else (expmod-aux base (- exp 1) n (remainder (* acc base) n)))))
(define (expmod base exp n)
(expmod-aux base exp n 1))
(define (miller-rabin-test n)
(define (try-it a)
(= (expmod a (- n 1) n) 1))
(if (and (even? n) (not (= 2 n)))
#f
(try-it (+ 1 (random (- n 1))))))