timothy235 icon

sicp-3-5-3-exploiting-the-stream-paradigm

timothy235 | PRO | 12/19/24 01:10:03 AM UTC (Edited) | 0 ⭐ | 7035 👁️ | Never ⏰ | []
Racket |

15.98 KB

|

None

|

0 👍

/

0 👎

#lang racket
(require "3-5-streams.rkt")
 
(define (average a b)
  (/ (+ a b) 2))
 
(define (sqrt-improve guess x)
  (average guess (/ x guess)))
 
(define (sqrt-stream x)
  (define guesses
    (cons-stream 1.0
                 (my-stream-map (lambda (guess)
                                  (sqrt-improve guess x))
                                guesses)))
  guesses)
 
;; Should be: 1.4142135623730951 
(display-this-many 10 (sqrt-stream 2) 'vert)
;; 1.0
;; 1.5
;; 1.4166666666666665
;; 1.4142156862745097
;; 1.4142135623746899
;; 1.414213562373095
;; 1.414213562373095
;; 1.414213562373095
;; 1.414213562373095
;; 1.414213562373095
;; 'done
 
(define (pi-summands n)
  (cons-stream (/ 1.0 n)
               (my-stream-map - (pi-summands (+ n 2)))))
 
(define pi-stream
  (scale-stream (partial-sums (pi-summands 1)) 4))
 
;; Should be: 3.141592653589793
(display-this-many 10 pi-stream 'vert)
;; 4.0
;; 2.666666666666667
;; 3.466666666666667
;; 2.8952380952380956
;; 3.3396825396825403
;; 2.9760461760461765
;; 3.2837384837384844
;; 3.017071817071818
;; 3.2523659347188767
;; 3.0418396189294032
;; 'done
 
(define (euler-transform s)
  (define s0 (my-stream-ref s 0))
  (define s1 (my-stream-ref s 1))
  (define s2 (my-stream-ref s 2))
  (cons-stream (- s2 (/ (sqr (- s2 s1))
                        (+ s0 (* -2 s1) s2)))
               (euler-transform (stream-cdr s))))
 
;; Should be: 3.141592653589793
(display-this-many 10 (euler-transform pi-stream) 'vert)
;; 3.166666666666667
;; 3.1333333333333337
;; 3.1452380952380956
;; 3.13968253968254
;; 3.1427128427128435
;; 3.1408813408813416
;; 3.142071817071818
;; 3.1412548236077655
;; 3.1418396189294033
;; 3.141406718496503
;; 'done
 
(define (make-tableau transform s)
  (cons-stream s
               (make-tableau transform
                             (transform s))))
 
(define (accelerated-sequence transform s)
  (my-stream-map stream-car
                 (make-tableau transform s)))
 
;; Should be: 3.141592653589793
(display-this-many 10 (accelerated-sequence euler-transform pi-stream) 'vert)
;; 4.0
;; 3.166666666666667
;; 3.142105263157895
;; 3.141599357319005
;; 3.1415927140337785
;; 3.1415926539752927
;; 3.1415926535911765
;; 3.141592653589778
;; 3.1415926535897953
;; 3.141592653589795
;; 'done
 
;;;;;;;;;;
;; 3.63 ;;
;;;;;;;;;;
 
(define (slow-sqrt-stream x)
  (cons-stream 1.0
               (my-stream-map (lambda (guess)
                                (sqrt-improve guess x))
                              (slow-sqrt-stream x))))
 
;; Recursing on sqrt-stream, instead of using the local variable guesses,
;; repeatedly creates new streams whose elements have not been forced and cached
;; yet.  So you lose the benefits of caching.  If you were using un-memoized
;; streams, there would be no difference.
 
(time (my-stream-ref (sqrt-stream 2) 100))
;; cpu time: 0 real time: 0 gc time: 0
;; 1.414213562373095
(time (my-stream-ref (slow-sqrt-stream 2) 100))
;; cpu time: 16 real time: 9 gc time: 0
;; 1.414213562373095
 
(time (my-stream-ref (sqrt-stream 2) 1000))
;; cpu time: 0 real time: 1 gc time: 0
;; 1.414213562373095
(time (my-stream-ref (slow-sqrt-stream 2) 1000))
;; cpu time: 703 real time: 841 gc time: 312
;; 1.414213562373095
 
;;;;;;;;;;
;; 3.64 ;;
;;;;;;;;;;
 
(define (stream-limit s tolerance)
  (define (loop s1 s2 str)
    (if (< (abs (- s1 s2)) tolerance)
      s2
      (loop s2 (stream-car str) (stream-cdr str))))
  (loop (stream-car s)
        (stream-car (stream-cdr s))
        (stream-cdr (stream-cdr s))))
 
;;;;;;;;;;
;; 3.65 ;;
;;;;;;;;;;
 
(define (ln2-summands n)
  (cons-stream (/ 1.0 n)
               (my-stream-map - (ln2-summands (add1 n)))))
 
(display-this-many 10 (ln2-summands 1) 'vert)
;; 1.0
;; -0.5
;; 0.3333333333333333
;; -0.25
;; 0.2
;; -0.16666666666666666
;; 0.14285714285714285
;; -0.125
;; 0.1111111111111111
;; -0.1
;; 'done
 
;; Actual value: ln 2 = 0.6931471805599453
 
(define ln2-stream1 (partial-sums (ln2-summands 1)))
(time (stream-limit ln2-stream1 0.001))
;; cpu time: 1531 real time: 1536 gc time: 766
;; 0.6936464315588232
 
(define ln2-stream2 (euler-transform ln2-stream1))
(time (stream-limit ln2-stream2 0.001))
;; cpu time: 0 real time: 0 gc time: 0
;; 0.6928571428571428
 
(define ln2-stream3 (accelerated-sequence euler-transform ln2-stream1))
(time (stream-limit ln2-stream3 0.001))
;; cpu time: 0 real time: 0 gc time: 0
;; 0.6931488693329254
 
;; Now let's compare the fast streams.
 
;; the euler transform
(time (stream-limit ln2-stream2 0.000000001)) ; tolerance is 10 ^ -9
;; cpu time: 609 real time: 618 gc time: 330
;; 0.6931471810586626
 
;; the accelerated sequence
(time (stream-limit ln2-stream3 0.000000001))
;; cpu time: 0 real time: 0 gc time: 0
;; 0.6931471805604039
 
;; So the transforms really speed things up.
 
;;;;;;;;;;
;; 3.66 ;;
;;;;;;;;;;
 
(define (interleave s1 s2)
  (if (stream-null? s1)
    s2
    (cons-stream (stream-car s1)
                 (interleave s2 (stream-cdr s1)))))
 
(define (pairs s t)
  (cons-stream
    (list (stream-car s) (stream-car t))
    (interleave
      (my-stream-map (lambda (x) (list (stream-car s) x))
                  (stream-cdr t))
      (pairs (stream-cdr s) (stream-cdr t)))))
 
(define prs (pairs integers integers))
 
(display-this-many 10 prs)
;; (1 1) (1 2) (2 2) (1 3) (2 3) (1 4) (3 3) (1 5) (2 4) (1 6) 
;; 'done
 
;; Let's gather some data on how these elements are arranged in the sequence of
;; pairs.
 
(define (how-many-precede s elt)
  (define (loop i str)
    (if (equal? (stream-car str) elt)
      i
      (loop (add1 i) (stream-cdr str))))
  (loop 0 s))
 
;; How many precede (n, n)?
;; Answer: 2 ^ n - 2.
(for ([i (in-range 1 11)])
  (define pr (list i i))
  (printf "~a ~a ~n" pr (how-many-precede prs pr)))
;; (1 1) 0 
;; (2 2) 2 
;; (3 3) 6 
;; (4 4) 14 
;; (5 5) 30 
;; (6 6) 62 
;; (7 7) 126 
;; (8 8) 254 
;; (9 9) 510 
;; (10 10) 1022 
 
;; How many precede (n, n + 1)?
;; Answer: 2 ^ (n - 1) more than precede (n, n).
(for ([i (in-range 1 11)])
  (define pr (list i (add1 i)))
  (printf "~a ~a ~n" pr (how-many-precede prs pr)))
;; (1 2) 1 
;; (2 3) 4 
;; (3 4) 10 
;; (4 5) 22 
;; (5 6) 46 
;; (6 7) 94 
;; (7 8) 190 
;; (8 9) 382 
;; (9 10) 766 
;; (10 11) 1534 
 
;; How many precede (n, n + 2)?
;; Answer: 2 ^ n more than precede (n, n + 1).
(for ([i (in-range 1 11)])
  (define pr (list i (+ i 2)))
  (printf "~a ~a ~n" pr (how-many-precede prs pr)))
;; (1 3) 3 
;; (2 4) 8 
;; (3 5) 18 
;; (4 6) 38 
;; (5 7) 78 
;; (6 8) 158 
;; (7 9) 318 
;; (8 10) 638 
;; (9 11) 1278 
;; (10 12) 2558 
 
;; So the number of elements preceding an element (n, k) will be:
 
;; 2 ^ n - 2 if k = n
;; 2 ^ n - 2 + 2 ^ (n - 1) if k = n + 1
;; 2 ^ n - 2 + 2 ^ (n - 1) + (k - n - 1) * 2 ^ n if k > n + 1
 
;; # elements preceding (1, 100) = 2 ^ 1 - 2 + 2 ^ 0 + 98 * 2 ^ 1 = 197
;; # elements preceding (99, 100) = 2 ^ 99 - 2 + 2 ^ 98
    ;; = 950737950171172051122527404030 
;; # elements preceding (100, 100) = 2 ^ 100 - 2 
    ;; = 1267650600228229401496703205374
 
(how-many-precede prs '(1 100))
;; 197
 
;;;;;;;;;;
;; 3.67 ;;
;;;;;;;;;;
 
;; The idea is to take out the corner element, and then interleave the top row,
;; the first column, and a recursive call to all-pairs.
 
(define (all-pairs s t)
  (cons-stream
    (list (stream-car s) (stream-car t))
    (interleave (my-stream-map (lambda (x) (list (stream-car s) x))
                               (stream-cdr t))
                (interleave (my-stream-map (lambda (x) (list x (stream-car t)))
                                           (stream-cdr s))
                            (all-pairs (stream-cdr s) (stream-cdr t))))))
 
(define all-prs (all-pairs integers integers))
 
(display-this-many 10 all-prs 'vert)
;; (1 1)
;; (1 2)
;; (2 1)
;; (1 3)
;; (2 2)
;; (1 4)
;; (3 1)
;; (1 5)
;; (2 3)
;; (1 6)
;; 'done
 
;;;;;;;;;;
;; 3.68 ;;
;;;;;;;;;;
 
(define (bad-pairs s t)
  (interleave
    (my-stream-map (lambda (x) (list (stream-car s) x))
                   t)
    (bad-pairs (stream-cdr s) (stream-cdr t))))
 
;; Evaluating (bad-pairs s t) requires evaluating
;; (bad-pairs (stream-cdr s) (stream-cdr t)), which has the same problem
;; with its own recursive call, and we fall immediately into an infinite loop.
;; This is because of eager evaluation.  Even though interleave uses the
;; stream-car of the first parameter as its own stream-car, eager evaluation
;; requires the computation of the stream-car of both parameters.
 
;; In general, when defining a stream, you cannot make a recursive call to get the
;; stream-car, only the stream-cdr.
 
;;;;;;;;;;
;; 3.69 ;;
;;;;;;;;;;
 
(define (triples s t u)
  (define ps (pairs t u))
  (cons-stream (list (stream-car s)
                     (stream-car t)
                     (stream-car u))
               (interleave (my-stream-map (lambda (p)
                                         (cons (stream-car s) p))
                                       (stream-cdr (pairs t u)))
                           (triples (stream-cdr s)
                                    (stream-cdr t)
                                    (stream-cdr u)))))
 
(define trpls (triples integers integers integers))
 
(display-this-many 10 trpls 'vert)
;; (1 1 1)
;; (1 1 2)
;; (2 2 2)
;; (1 2 2)
;; (2 2 3)
;; (1 1 3)
;; (3 3 3)
;; (1 2 3)
;; (2 3 3)
;; (1 1 4)
;; 'done
 
(define (pythagorean? triple)
  (= (+ (sqr (first triple))
        (sqr (second triple)))
     (sqr (third triple))))
 
(define pythagorean-triples (my-stream-filter pythagorean? trpls))
 
 
;; It will take a very long time to produce more than six of these.
(display-this-many 4 pythagorean-triples 'vert)
;; (3 4 5) 
;; (6 8 10) 
;; (5 12 13) 
;; (9 12 15) 
;; 'done
 
;;;;;;;;;;
;; 3.70 ;;
;;;;;;;;;;
 
(define (merge-weighted weight ps1 ps2)
  (define p1 (stream-car ps1))
  (define p2 (stream-car ps2))
  (if (> (weight p1) (weight p2))
    (cons-stream p2
                 (merge-weighted weight
                                 ps1
                                 (stream-cdr ps2)))
    (cons-stream p1
                 (merge-weighted weight
                                 (stream-cdr ps1)
                                 ps2))))
 
(define (weighted-pairs weight s t)
  (cons-stream (list (stream-car s)
                     (stream-car t))
               (merge-weighted weight
                               (my-stream-map (lambda (x) (list (stream-car s) x))
                                              (stream-cdr t))
                               (weighted-pairs weight
                                               (stream-cdr s)
                                               (stream-cdr t)))))
 
(define (weight1 pr) (+ (first pr) (second pr)))
 
(define st1 (weighted-pairs weight1 integers integers))
 
(display-this-many 10 st1 'vert)
;; (1 1) 
;; (1 2) 
;; (1 3) 
;; (2 2) 
;; (1 4) 
;; (2 3) 
;; (1 5) 
;; (2 4) 
;; (3 3) 
;; (1 6) 
;; 'done
 
(define (not-div-2-3-5? a)
  (and (not (zero? (remainder a 2)))
       (not (zero? (remainder a 3)))
       (not (zero? (remainder a 5)))))
 
(define (weight2 pr) (+ (* 2 (first pr))
                        (* 3 (second pr))
                        (* 5 (first pr) (second pr))))
 
(define st2 (weighted-pairs
              weight2
              (my-stream-filter not-div-2-3-5? integers)
              (my-stream-filter not-div-2-3-5? integers)))
 
(display-this-many 10 st2 'vert)
;; (1 1) 
;; (1 7) 
;; (1 11) 
;; (1 13) 
;; (1 17) 
;; (1 19) 
;; (1 23) 
;; (1 29) 
;; (1 31) 
;; (7 7) 
;; 'done
 
;;;;;;;;;;
;; 3.71 ;;
;;;;;;;;;;
 
(define (generalized-ramanujan f n)
  ;; Generate all positive numbers that can be written as f(i, j) for 0 < i < j
  ;; in n different ways, n > 1, alongwith the corresponding n-tuples.
  (define (tuples s)
    ; Break s into n-tuples.
    (define (loop i lst str)
      (if (> i n)
        (reverse lst)
        (loop (add1 i)
              (cons (stream-car str) lst)
              (stream-cdr str))))
    (cons-stream (loop 1 empty s)
                 (tuples (stream-cdr s))))
  (define (good-tuple? tuple)
    (define val (f (first tuple)))
    (for/and ([i (in-range 1 n)])
             (= (f (list-ref tuple i)) val)))
  (my-stream-map
    (lambda (tuple) (cons (f (first tuple)) tuple))
    (my-stream-filter good-tuple?
                      (tuples (weighted-pairs f
                                              integers
                                              integers)))))
 
(define (sum-of-cubes pr)
  (+ (expt (first pr) 3)
     (expt (second pr) 3)))
 
(define ramanujan (generalized-ramanujan sum-of-cubes 2))
 
(display-this-many 10 ramanujan 'vert)
;; (1729 (1 12) (9 10)) 
;; (4104 (2 16) (9 15)) 
;; (13832 (2 24) (18 20)) 
;; (20683 (10 27) (19 24)) 
;; (32832 (4 32) (18 30)) 
;; (39312 (2 34) (15 33)) 
;; (40033 (9 34) (16 33)) 
;; (46683 (3 36) (27 30)) 
;; (64232 (17 39) (26 36)) 
;; (65728 (12 40) (31 33)) 
;; 'done
 
;;;;;;;;;;
;; 3.72 ;;
;;;;;;;;;;
 
(define (sum-of-squares pr)
  (+ (sqr (first pr))
     (sqr (second pr))))
 
(define sum-of-two-squares-in-three-ways
  (generalized-ramanujan sum-of-squares 3))
 
(display-this-many 10 sum-of-two-squares-in-three-ways 'vert)
;; (325 (1 18) (6 17) (10 15)) 
;; (425 (5 20) (8 19) (13 16)) 
;; (650 (5 25) (11 23) (17 19)) 
;; (725 (7 26) (10 25) (14 23)) 
;; (845 (2 29) (13 26) (19 22)) 
;; (850 (3 29) (11 27) (15 25)) 
;; (925 (5 30) (14 27) (21 22)) 
;; (1025 (1 32) (8 31) (20 25)) 
;; (1105 (4 33) (9 32) (12 31)) 
;; (1105 (9 32) (12 31) (23 24)) 
;; 'done
 
;;;;;;;;;;
;; 3.73 ;;
;;;;;;;;;;
 
(define (integral integrand initial-value dt)
  (define int
    (cons-stream initial-value
                 (add-streams (scale-stream integrand dt)
                              int)))
  int)
 
(define (RC R C dt)
  (lambda (i v0)
    (add-streams (scale-stream (integral i v0 dt)
                               (/ 1.0 C))
                 (scale-stream i R))))
 
(define RC1 (RC 5 1 0.5))
 
(display-this-many 10 (RC1 ones 2) 'vert)
;; 7.0 
;; 7.5 
;; 8.0 
;; 8.5 
;; 9.0 
;; 9.5 
;; 10.0 
;; 10.5 
;; 11.0 
;; 11.5 
;; 'done
 
;;;;;;;;;;
;; 3.74 ;;
;;;;;;;;;;
 
(define (sign-change-detector current lastval)
  (cond [(and (negative? lastval) (>= current 0)) 1]
        [(and (>= lastval 0) (negative? current)) -1]
        [else 0]))
 
(define (get-next current)
  ; get test data by cycling from -2 up to 2 and repeating
  (if (= current 2)
    -2
    (add1 current)))
 
(define sense-data
  (cons-stream 1 (my-stream-map get-next sense-data)))
 
(define zero-crossings
  (my-stream-map sign-change-detector
                 sense-data
                 (cons-stream 0 sense-data)))
 
(display-this-many 10 sense-data)
;; 1 2 -2 -1 0 1 2 -2 -1 0 
;; 'done
 
(display-this-many 10 zero-crossings)
;; 0 0 -1 0 1 0 0 -1 0 1 
;; 'done
 
;;;;;;;;;;
;; 3.75 ;;
;;;;;;;;;;
 
;; The original version kept averaging in the average value, so avpt was no longer
;; the average of the next two stream elements.
 
(define (make-zero-crossings input-stream last-value last-avpt)
  (define avpt (average (stream-car input-stream) last-value))
  (cons-stream (sign-change-detector avpt last-avpt)
               (make-zero-crossings (stream-cdr input-stream)
                                    (stream-car input-stream)
                                    avpt)))
 
(display-this-many 10 (make-zero-crossings sense-data 0 0))
;; 0 0 0 -1 0 1 0 0 -1 0 
;; 'done
 
;;;;;;;;;;
;; 3.76 ;;
;;;;;;;;;;
 
(define (smooth input-stream)
  (my-stream-map average
                 input-stream
                 (cons-stream 0 input-stream)))
 
(define (new-make-zero-crossings input-stream)
  (define smoothed-stream (smooth input-stream))
  (my-stream-map sign-change-detector
                 smoothed-stream
                 (cons-stream 0 smoothed-stream)))
 
(display-this-many 10 (new-make-zero-crossings sense-data))
;; 0 0 0 -1 0 1 0 0 -1 0 
;; 'done

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎