diff --git a/1-3.org b/1-3.org index 11cfc67..3f6f723 100644 --- a/1-3.org +++ b/1-3.org @@ -269,4 +269,389 @@ layout: org #+END_SRC * Procedures as General Methods + #+BEGIN_SRC scheme :tangle yes + ;; ------------------------------------------------------------------- + ;; 1.3.3: Procedures as General Methods + ;; ------------------------------------------------------------------- + + (define (average x y) + (/ (+ x y) 2)) + + (define (search f neg-point pos-point) + (let ((midpoint (average neg-point pos-point))) + (if (close-enough? neg-point pos-point) + midpoint + (let ((test-value (f midpoint))) + (cond ((positive? test-value) + (search f neg-point midpoint)) + ((negative? test-value) + (search f midpoint pos-point)) + (else midpoint)))))) + + (define (close-enough? x y) + (< (abs (- x y)) 0.001)) + + (define (half-interval-method f a b) + (let ((a-value (f a)) + (b-value (f b))) + (cond ((and (negative? a-value) (positive? b-value)) + (search f a b)) + ((and (negative? b-value) (positive? a-value)) + (search f b a)) + (else + (error "Values are not of opposite sign" a b))))) + + (define tolerance 0.00001) + + (define (fixed-point f first-guess) + (define (close-enough? v1 v2) + (< (abs (- v1 v2)) tolerance)) + (define (try guess) + (let ((next (f guess))) + (if (close-enough? guess next) + next + (try next)))) + (try first-guess)) + + (define (sqrt x) + (fixed-point (lambda (y) (average y (/ x y))) + 1.0)) + + #+END_SRC +** Exercise 1.35: + Show that the golden ratio [phi] (section *Note 1-2-2::) is a fixed + point of the transformation x |-> 1 + 1/x, and use this fact to + compute [phi] by means of the `fixed-point' procedure. + + ---------------------------------------------------------------------- + + #+BEGIN_SRC scheme :tangle yes + ;; ------------------------------------------------------------------- + ;; Exercise 1.35 + ;; ------------------------------------------------------------------- + + (define phi + (fixed-point (lambda (x) (+ 1 (/ 1 x))) + 1.0)) + #+END_SRC +** Exercise 1.36: + Modify `fixed-point' so that it prints the sequence of + approximations it generates, using the `newline' and `display' + primitives shown in *Note Exercise 1-22::. Then find a solution to + x^x = 1000 by finding a fixed point of x |-> `log'(1000)/`log'(x). + (Use Scheme's primitive `log' procedure, which computes natural + logarithms.) Compare the number of steps this takes with and + without average damping. (Note that you cannot start `fixed-point' + with a guess of 1, as this would cause division by `log'(1) = 0.) + + ---------------------------------------------------------------------- + + #+BEGIN_SRC scheme :tangle yes + ;; ------------------------------------------------------------------- + ;; Exercise 1.36 + ;; ------------------------------------------------------------------- + + (define (fixed-point-display f first-guess) + (define (close-enough? v1 v2) + (< (abs (- v1 v2)) tolerance)) + (define (try guess) + (let ((next (f guess))) + (display (list "Trying" next)) + (newline) + (if (close-enough? guess next) + next + (try next)))) + (try first-guess)) + + (fixed-point-display + (lambda (x) (/ (log 1000) (log x))) + 1.5) + + ;(Trying 17.036620761802716) + ;(Trying 2.436284152826871) + ;(Trying 7.7573914048784065) + ;(Trying 3.3718636013068974) + ;(Trying 5.683217478018266) + ;(Trying 3.97564638093712) + ;(Trying 5.004940305230897) + ;(Trying 4.2893976408423535) + ;(Trying 4.743860707684508) + ;(Trying 4.437003894526853) + ;(Trying 4.6361416205906485) + ;(Trying 4.503444951269147) + ;(Trying 4.590350549476868) + ;(Trying 4.532777517802648) + ;(Trying 4.570631779772813) + ;(Trying 4.545618222336422) + ;(Trying 4.562092653795064) + ;(Trying 4.551218723744055) + ;(Trying 4.558385805707352) + ;(Trying 4.553657479516671) + ;(Trying 4.55677495241968) + ;(Trying 4.554718702465183) + ;(Trying 4.556074615314888) + ;(Trying 4.555180352768613) + ;(Trying 4.555770074687025) + ;(Trying 4.555381152108018) + ;(Trying 4.555637634081652) + ;(Trying 4.555468486740348) + ;(Trying 4.555580035270157) + ;(Trying 4.555506470667713) + ;(Trying 4.555554984963888) + ;(Trying 4.5555229906097905) + ;(Trying 4.555544090254035) + ;(Trying 4.555530175417048) + ;(Trying 4.555539351985717) + ;;Value: 4.555539351985717 + ; + (fixed-point-display + (lambda (x) (average x (/ (log 1000) (log x)))) + 1.5) + + ;(Trying 9.268310380901358) + ;(Trying 6.185343522487719) + ;(Trying 4.988133688461795) + ;(Trying 4.643254620420954) + ;(Trying 4.571101497091747) + ;(Trying 4.5582061760763715) + ;(Trying 4.555990975858476) + ;(Trying 4.555613236666653) + ;(Trying 4.555548906156018) + ;(Trying 4.555537952796512) + ;(Trying 4.555536087870658) + ;;Value: 4.555536087870658 + + #+END_SRC +** Exercise 1.37: + a. An infinite "continued fraction" is an expression of the form + + #+BEGIN_EXAMPLE + N_1 + f = --------------------- + N_2 + D_1 + --------------- + N_3 + D_2 + --------- + D_3 + ... + #+END_EXAMPLE + + As an example, one can show that the infinite continued + fraction expansion with the n_i and the D_i all equal to 1 + produces 1/[phi], where [phi] is the golden ratio (described + in section *Note 1-2-2::). One way to approximate an + infinite continued fraction is to truncate the expansion + after a given number of terms. Such a truncation--a + so-called finite continued fraction "k-term finite continued + fraction"--has the form + + #+BEGIN_EXAMPLE + N_1 + ----------------- + N_2 + D_1 + ----------- + ... N_K + + ----- + D_K + #+END_EXAMPLE + + Suppose that `n' and `d' are procedures of one argument (the + term index i) that return the n_i and D_i of the terms of the + continued fraction. Define a procedure `cont-frac' such that + evaluating `(cont-frac n d k)' computes the value of the + k-term finite continued fraction. Check your procedure by + approximating 1/[phi] using + + #+BEGIN_SRC scheme + (cont-frac (lambda (i) 1.0) + (lambda (i) 1.0) + k) + #+END_SRC + + for successive values of `k'. How large must you make `k' in + order to get an approximation that is accurate to 4 decimal + places? + + b. If your `cont-frac' procedure generates a recursive process, + write one that generates an iterative process. If it + generates an iterative process, write one that generates a + recursive process. + +** Exercise 1.38: + In 1737, the Swiss mathematician Leonhard Euler published a memoir + `De Fractionibus Continuis', which included a continued fraction + expansion for e - 2, where e is the base of the natural logarithms. + In this fraction, the n_i are all 1, and the D_i are successively + 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, .... Write a program that uses + your `cont-frac' procedure from *Note Exercise 1-37:: to + approximate e, based on Euler's expansion. + +** Exercise 1.39: + A continued fraction representation of the tangent function was + published in 1770 by the German mathematician J.H. Lambert: + + #+BEGIN_EXAMPLE + x + tan x = --------------- + x^2 + 1 - ----------- + x^2 + 3 - ------- + 5 - ... + + #+END_EXAMPLE + + where x is in radians. Define a procedure `(tan-cf x k)' that + computes an approximation to the tangent function based on + Lambert's formula. `K' specifies the number of terms to compute, + as in *Note Exercise 1-37::. + * Procedures as Returned Values + #+BEGIN_SRC scheme :tangle yes + ;; ------------------------------------------------------------------- + ;; 1.3.4: Procedures as Returned Values + ;; ------------------------------------------------------------------- + + (define (average-damp f) + (lambda (x) (average x (f x)))) + + (define (sqrt x) + (fixed-point (average-damp (lambda (y) (/ x y))) + 1.0)) + + (define (cube-root x) + (fixed-point (average-damp (lambda (y) (/ x (square y)))) + 1.0)) + + (define (deriv g) + (lambda (x) + (/ (- (g (+ x dx)) (g x)) + dx))) + (define dx 0.00001) + + (define (cube x) (* x x x)) + + (define (newton-transform g) + (lambda (x) + (- x (/ (g x) ((deriv g) x))))) + + (define (newtons-method g guess) + (fixed-point (newton-transform g) guess)) + + (define (sqrt x) + (newtons-method (lambda (y) (- (square y) x)) + 1.0)) + + (define (fixed-point-of-transform g transform guess) + (fixed-point (transform g) guess)) + + (define (sqrt x) + (fixed-point-of-transform (lambda (y) (/ x y)) + average-damp + 1.0)) + + (define (sqrt x) + (fixed-point-of-transform (lambda (y) (- (square y) x)) + newton-transform + 1.0)) + + + #+END_SRC + +** Exercise 1.40 + Define a procedure `cubic' that can be used together with the + `newtons-method' procedure in expressions of the form + + #+begin_src scheme + (newtons-method (cubic a b c) 1) + #+end_src + + to approximate zeros of the cubic x^3 + ax^2 + bx + c. + +** Exercise 1.41 + Define a procedure `double' that takes a procedure of one argument + as argument and returns a procedure that applies the original + procedure twice. For example, if `inc' is a procedure that adds 1 + to its argument, then `(double inc)' should be a procedure that + adds 2. What value is returned by + + #+begin_src scheme + (((double (double double)) inc) 5) + #+end_src + +** Exercise 1.42 + Let f and g be two one-argument functions. The "composition" f + after g is defined to be the function x |-> f(g(x)). Define a + procedure `compose' that implements composition. For example, if + `inc' is a procedure that adds 1 to its argument, + + #+begin_src scheme + ((compose square inc) 6) + 49 + #+end_src + +** Exercise 1.43 + If f is a numerical function and n is a positive + integer, then we can form the nth repeated application of f, which + is defined to be the function whose value at x is + f(f(...(f(x))...)). For example, if f is the function x |-> x + + 1, then the nth repeated application of f is the function x |-> x + + n. If f is the operation of squaring a number, then the nth + repeated application of f is the function that raises its argument + to the 2^nth power. Write a procedure that takes as inputs a + procedure that computes f and a positive integer n and returns the + procedure that computes the nth repeated application of f. Your + procedure should be able to be used as follows: + + #+begin_src scheme + ((repeated square 2) 5) + 625 + #+end_src + + Hint: You may find it convenient to use `compose' from *Note + Exercise 1-42::. + +** Exercise 1.44 + The idea of "smoothing" a function is an important concept in + signal processing. If f is a function and dx is some small number, + then the smoothed version of f is the function whose value at a + point x is the average of f(x - dx), f(x), and f(x + dx). Write a + procedure `smooth' that takes as input a procedure that computes f + and returns a procedure that computes the smoothed f. It is + sometimes valuable to repeatedly smooth a function (that is, smooth + the smoothed function, and so on) to obtained the "n-fold smoothed + function". Show how to generate the n-fold smoothed function of + any given function using `smooth' and `repeated' from *Note + Exercise 1-43::. + +** Exercise 1.45 + We saw in section *Note 1-3-3:: that attempting to compute square + roots by naively finding a fixed point of y |-> x/y does not + converge, and that this can be fixed by average damping. The same + method works for finding cube roots as fixed points of the + average-damped y |-> x/y^2. Unfortunately, the process does not + work for fourth roots--a single average damp is not enough to make + a fixed-point search for y |-> x/y^3 converge. On the other hand, + if we average damp twice (i.e., use the average damp of the average + damp of y |-> x/y^3) the fixed-point search does converge. Do some + experiments to determine how many average damps are required to + compute nth roots as a fixed-point search based upon repeated + average damping of y |-> x/y^(n-1). Use this to implement a simple + procedure for computing nth roots using `fixed-point', + `average-damp', and the `repeated' procedure of *Note Exercise + 1-43::. Assume that any arithmetic operations you need are + available as primitives. + +** Exercise 1.46 + Several of the numerical methods described in this chapter are + instances of an extremely general computational strategy known as + "iterative improvement". Iterative improvement says that, to + compute something, we start with an initial guess for the answer, + test if the guess is good enough, and otherwise improve the guess + and continue the process using the improved guess as the new guess. + Write a procedure `iterative-improve' that takes two procedures as + arguments: a method for telling whether a guess is good enough and + a method for improving a guess. `Iterative-improve' should return + as its value a procedure that takes a guess as argument and keeps + improving the guess until it is good enough. Rewrite the `sqrt' + procedure of section *Note 1-1-7:: and the `fixed-point' procedure + of section *Note 1-3-3:: in terms of `iterative-improve'.