mirror of
https://github.com/correl/sicp.git
synced 2024-11-23 19:19:56 +00:00
430 lines
18 KiB
Scheme
430 lines
18 KiB
Scheme
|
;; ====================================================================
|
||
|
;; 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<n, and try your procedure on
|
||
|
;; the given Carmichael numbers.
|
||
|
;;
|
||
|
;; *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<n and raise a
|
||
|
;; to the (n - 1)st power modulo n using the `expmod' procedure.
|
||
|
;; However, whenever we perform the squaring step in `expmod', we
|
||
|
;; check to see if we have discovered a "nontrivial square root of 1
|
||
|
;; modulo n," that is, a number not equal to 1 or n - 1 whose square
|
||
|
;; is equal to 1 modulo n. It is possible to prove that if such a
|
||
|
;; nontrivial square root of 1 exists, then n is not prime. It is
|
||
|
;; also possible to prove that if n is an odd number that is not
|
||
|
;; prime, then, for at least half the numbers a<n, computing a^(n-1)
|
||
|
;; in this way will reveal a nontrivial square root of 1 modulo n.
|
||
|
;; (This is why the Miller-Rabin test cannot be fooled.) Modify the
|
||
|
;; `expmod' procedure to signal if it discovers a nontrivial square
|
||
|
;; root of 1, and use this to implement the Miller-Rabin test with a
|
||
|
;; procedure analogous to `fermat-test'. Check your procedure by
|
||
|
;; testing various known primes and non-primes. Hint: One convenient
|
||
|
;; way to make `expmod' signal is to have it return 0.
|