;; ==================================================================== ;; SICP - 1.2: Procedures and the Processes They Generate ;; ==================================================================== ;; ==================================================================== ;; 1.2.1: Linear Recursion and Iteration ;; ==================================================================== (define (inc n) (+ n 1)) (define (dec n) (- n 1)) ;; ==================================================================== ;; *Exercise 1.9:* Each of the following two procedures defines a ;; method for adding two positive integers in terms of the procedures ;; `inc', which increments its argument by 1, and `dec', which ;; decrements its argument by 1. ;; ;; (define (+ a b) ;; (if (= a 0) ;; b ;; (inc (+ (dec a) b)))) ;; ;; (define (+ a b) ;; (if (= a 0) ;; b ;; (+ (dec a) (inc b)))) ;; ;; Using the substitution model, illustrate the process generated by ;; each procedure in evaluating `(+ 4 5)'. Are these processes ;; iterative or recursive? ;; -------------------------------------------------------------------- ;; First procedure: Recursive (+ 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 ;; Second procedure: Iterative (+ 4 5) (+ 3 6) (+ 2 7) (+ 1 8) (+ 0 9) 9 ;; ==================================================================== ;; *Exercise 1.10:* The following procedure computes a mathematical ;; function called Ackermann's function. ;; (define (A x y) (cond ((= y 0) 0) ((= x 0) (* 2 y)) ((= y 1) 2) (else (A (- x 1) (A x (- y 1)))))) ;; ;; What are the values of the following expressions? ;; ;; (A 1 10) 1024 ;; (A 2 4) 65536 ;; ;; (A 3 3) 65536 ;; ;; Consider the following procedures, where `A' is the procedure ;; defined above: ;; ;; (define (f n) (A 0 n)) ;; ;; (define (g n) (A 1 n)) ;; ;; (define (h n) (A 2 n)) ;; ;; (define (k n) (* 5 n n)) ;; ;; Give concise mathematical definitions for the functions computed ;; by the procedures `f', `g', and `h' for positive integer values of ;; n. For example, `(k n)' computes 5n^2. ;; -------------------------------------------------------------------- ;; `(f n)' computes 2n ;; `(g n)' computes ... ;; ==================================================================== ;; 1.2.2: Tree Recursion ;; ==================================================================== (define (fib n) (fib-iter 1 0 n)) (define (fib-iter a b count) (if (= count 0) b (fib-iter (+ a b) a (- count 1)))) ;; ==================================================================== ;; *Exercise 1.11:* A function f is defined by the rule that f(n) = n ;; if n<3 and f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3) if n>= 3. ;; Write a procedure that computes f by means of a recursive process. ;; Write a procedure that computes f by means of an iterative ;; process. ;; -------------------------------------------------------------------- (define (f-recursive n) (if (< n 3) n (+ (f-recursive (- n 1)) (* 2 (f-recursive (- n 2))) (* 3 (f-recursive (- n 3)))))) (define (f-iterative n) (define (do-iter a b c n) (if (< n 3) a (do-iter (+ a (* 2 b) (* 3 c)) a b (- n 1)))) (if (< n 3) n (do-iter 2 1 0 n))) ;; ==================================================================== ;; *Exercise 1.12:* The following pattern of numbers is called "Pascal's ;; triangle". ;; ;; 1 ;; 1 1 ;; 1 2 1 ;; 1 3 3 1 ;; 1 4 6 4 1 ;; ;; The numbers at the edge of the triangle are all 1, and each number ;; inside the triangle is the sum of the two numbers above it.(4) ;; Write a procedure that computes elements of Pascal's triangle by ;; means of a recursive process. ;; -------------------------------------------------------------------- (define (pascal row column) (cond ((= column 1) 1) ((= row column) 1) (#t (+ (pascal (- row 1) (- column 1)) (pascal (- row 1) column))))) ;; ==================================================================== ;; *Exercise 1.13:* Prove that _Fib_(n) is the closest integer to ;; [phi]^n/[sqrt](5), where [phi] = (1 + [sqrt](5))/2. Hint: Let ;; [illegiblesymbol] = (1 - [sqrt](5))/2. Use induction and the ;; definition of the Fibonacci numbers (see section *Note 1-2-2::) to ;; prove that _Fib_(n) = ([phi]^n - [illegiblesymbol]^n)/[sqrt](5). ;; -------------------------------------------------------------------- ;; http://www.billthelizard.com/2009/12/sicp-exercise-113-fibonacci-and-golden.html ;; ==================================================================== ;; 1.2.3: Orders of Growth ;; ==================================================================== ;; *Exercise 1.14:* Draw the tree illustrating the process generated ;; by the `count-change' procedure of section *Note 1-2-2:: in making ;; change for 11 cents. What are the orders of growth of the space ;; and number of steps used by this process as the amount to be ;; changed increases? ;; ;; *Exercise 1.15:* The sine of an angle (specified in radians) can ;; be computed by making use of the approximation `sin' xapprox x if ;; x is sufficiently small, and the trigonometric identity ;; ;; x x ;; sin x = 3 sin --- - 4 sin^3 --- ;; 3 3 ;; ;; to reduce the size of the argument of `sin'. (For purposes of this ;; exercise an angle is considered "sufficiently small" if its ;; magnitude is not greater than 0.1 radians.) These ideas are ;; incorporated in the following procedures: ;; ;; (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. How many times is the procedure `p' applied when `(sine ;; 12.15)' is evaluated? ;; ;; 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? ;; ==================================================================== ;; 1.2.4: Exponentiation ;; -------------------------------------------------------------------- ;; *Exercise 1.16:* Design a procedure that evolves an iterative ;; exponentiation process that uses successive squaring and uses a ;; logarithmic number of steps, as does `fast-expt'. (Hint: Using the ;; observation that (b^(n/2))^2 = (b^2)^(n/2), keep, along with the ;; exponent n and the base b, an additional state variable a, and ;; define the state transformation in such a way that the product a ;; b^n is unchanged from state to state. At the beginning of the ;; process a is taken to be 1, and the answer is given by the value ;; of a at the end of the process. In general, the technique of ;; defining an "invariant quantity" that remains unchanged from state ;; to state is a powerful way to think about the design of iterative ;; algorithms.) ;; ;; *Exercise 1.17:* The exponentiation algorithms in this section are ;; based on performing exponentiation by means of repeated ;; multiplication. In a similar way, one can perform integer ;; multiplication by means of repeated addition. The following ;; multiplication procedure (in which it is assumed that our language ;; can only add, not multiply) is analogous to the `expt' procedure: ;; ;; (define (* a b) ;; (if (= b 0) ;; 0 ;; (+ a (* a (- b 1))))) ;; ;; This algorithm takes a number of steps that is linear in `b'. Now ;; suppose we include, together with addition, operations `double', ;; which doubles an integer, and `halve', which divides an (even) ;; integer by 2. Using these, design a multiplication procedure ;; analogous to `fast-expt' that uses a logarithmic number of steps. ;; ;; *Exercise 1.18:* Using the results of *Note Exercise 1-16:: and ;; *Note Exercise 1-17::, devise a procedure that generates an ;; iterative process for multiplying two integers in terms of adding, ;; doubling, and halving and uses a logarithmic number of steps.(4) ;; ;; *Exercise 1.19:* There is a clever algorithm for computing the ;; Fibonacci numbers in a logarithmic number of steps. Recall the ;; transformation of the state variables a and b in the `fib-iter' ;; process of section *Note 1-2-2::: a <- a + b and b <- a. Call ;; this transformation T, and observe that applying T over and over ;; again n times, starting with 1 and 0, produces the pair _Fib_(n + ;; 1) and _Fib_(n). In other words, the Fibonacci numbers are ;; produced by applying T^n, the nth power of the transformation T, ;; starting with the pair (1,0). Now consider T to be the special ;; case of p = 0 and q = 1 in a family of transformations T_(pq), ;; where T_(pq) transforms the pair (a,b) according to a <- bq + aq + ;; ap and b <- bp + aq. Show that if we apply such a transformation ;; T_(pq) twice, the effect is the same as using a single ;; transformation T_(p'q') of the same form, and compute p' and q' in ;; terms of p and q. This gives us an explicit way to square these ;; transformations, and thus we can compute T^n using successive ;; squaring, as in the `fast-expt' procedure. Put this all together ;; to complete the following procedure, which runs in a logarithmic ;; number of steps:(5) ;; ;; (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 ;; ; compute p' ;; ; compute q' ;; (/ count 2))) ;; (else (fib-iter (+ (* b q) (* a q) (* a p)) ;; (+ (* b p) (* a q)) ;; p ;; q ;; (- count 1))))) ;; ==================================================================== ;; 1.2.5: Greatest Common Divisors ;; ==================================================================== ;; *Exercise 1.20:* The process that a procedure generates is of ;; course dependent on the rules used by the interpreter. As an ;; example, consider the iterative `gcd' procedure given above. ;; Suppose we were to interpret this procedure using normal-order ;; evaluation, as discussed in section *Note 1-1-5::. (The ;; normal-order-evaluation rule for `if' is described in *Note ;; Exercise 1-5::.) Using the substitution method (for normal ;; order), illustrate the process generated in evaluating `(gcd 206 ;; 40)' and indicate the `remainder' operations that are actually ;; performed. How many `remainder' operations are actually performed ;; in the normal-order evaluation of `(gcd 206 40)'? In the ;; applicative-order evaluation? ;; ==================================================================== ;; 1.2.6: Example: Testing for Primality ;; ==================================================================== ;; *Exercise 1.21:* Use the `smallest-divisor' procedure to find the ;; smallest divisor of each of the following numbers: 199, 1999, ;; 19999. ;; ;; *Exercise 1.22:* Most Lisp implementations include a primitive ;; called `runtime' that returns an integer that specifies the amount ;; of time the system has been running (measured, for example, in ;; microseconds). The following `timed-prime-test' procedure, when ;; called with an integer n, prints n and checks to see if n is ;; prime. If n is prime, the procedure prints three asterisks ;; followed by the amount of time used in performing the test. ;; ;; (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)) ;; ;; Using this procedure, write a procedure `search-for-primes' that ;; checks the primality of consecutive odd integers in a specified ;; range. Use your procedure to find the three smallest primes ;; larger than 1000; larger than 10,000; larger than 100,000; larger ;; than 1,000,000. Note the time needed to test each prime. Since ;; the testing algorithm has order of growth of [theta](_[sqrt]_(n)), ;; you should expect that testing for primes around 10,000 should ;; take about _[sqrt]_(10) times as long as testing for primes around ;; 1000. Do your timing data bear this out? How well do the data ;; for 100,000 and 1,000,000 support the _[sqrt]_(n) prediction? Is ;; your result compatible with the notion that programs on your ;; machine run in time proportional to the number of steps required ;; for the computation? ;; ;; *Exercise 1.23:* The `smallest-divisor' procedure shown at the ;; start of this section does lots of needless testing: After it ;; checks to see if the number is divisible by 2 there is no point in ;; checking to see if it is divisible by any larger even numbers. ;; This suggests that the values used for `test-divisor' should not ;; be 2, 3, 4, 5, 6, ..., but rather 2, 3, 5, 7, 9, .... To ;; implement this change, define a procedure `next' that returns 3 if ;; its input is equal to 2 and otherwise returns its input plus 2. ;; Modify the `smallest-divisor' procedure to use `(next ;; test-divisor)' instead of `(+ test-divisor 1)'. With ;; `timed-prime-test' incorporating this modified version of ;; `smallest-divisor', run the test for each of the 12 primes found in ;; *Note Exercise 1-22::. Since this modification halves the number ;; of test steps, you should expect it to run about twice as fast. ;; Is this expectation confirmed? If not, what is the observed ratio ;; of the speeds of the two algorithms, and how do you explain the ;; fact that it is different from 2? ;; ;; *Exercise 1.24:* Modify the `timed-prime-test' procedure of *Note ;; Exercise 1-22:: to use `fast-prime?' (the Fermat method), and test ;; each of the 12 primes you found in that exercise. Since the ;; Fermat test has [theta](`log' n) growth, how would you expect the ;; time to test primes near 1,000,000 to compare with the time needed ;; to test primes near 1000? Do your data bear this out? Can you ;; explain any discrepancy you find? ;; ;; *Exercise 1.25:* Alyssa P. Hacker complains that we went to a lot ;; of extra work in writing `expmod'. After all, she says, since we ;; already know how to compute exponentials, we could have simply ;; written ;; ;; (define (expmod base exp m) ;; (remainder (fast-expt base exp) m)) ;; ;; Is she correct? Would this procedure serve as well for our fast ;; prime tester? Explain. ;; ;; *Exercise 1.26:* Louis Reasoner is having great difficulty doing ;; *Note Exercise 1-24::. His `fast-prime?' test seems to run more ;; slowly than his `prime?' test. Louis calls his friend Eva Lu Ator ;; over to help. When they examine Louis's code, they find that he ;; has rewritten the `expmod' procedure to use an explicit ;; multiplication, rather than calling `square': ;; ;; (define (expmod base exp m) ;; (cond ((= exp 0) 1) ;; ((even? exp) ;; (remainder (* (expmod base (/ exp 2) m) ;; (expmod base (/ exp 2) m)) ;; m)) ;; (else ;; (remainder (* base (expmod base (- exp 1) m)) ;; m)))) ;; ;; "I don't see what difference that could make," says Louis. "I ;; do." says Eva. "By writing the procedure like that, you have ;; transformed the [theta](`log' n) process into a [theta](n) ;; process." Explain. ;; ;; *Exercise 1.27:* Demonstrate that the Carmichael numbers listed in ;; *Note Footnote 1-47:: really do fool the Fermat test. That is, ;; write a procedure that takes an integer n and tests whether a^n is ;; congruent to a modulo n for every a