SICP 3.5.4 - 3.5.5

3.77

#lang racket
(define (integral delayed-integrand initial-value dt)
  (let ((integrand (froce delayed-integrand)))
    (cons-stream initial-value
      (if (stream-null? integrand)
          the-empty-stream
          (integral (stream-cdr integrand)
                    (+ (* dt (stream-car integrand))
                       initial-value)
                    dt)))))

3.78

(define (solve-2nd a b dt y0 dy0)
  (define (y (integral dy) y0 dt))
  (define (dy (integral ddy) dy0 dt))
  (define (ddy (+ (scale-stream dy a)
                  (scale-stream y b))))
  y)

3.79

(define (solve-2nd f dt y0 dy0)
  (define (y (integral dy) y0 dt))
  (define (dy (integral ddy) dy0 dt))
  (define (ddy (stream-map f dy y))))
  y)

3.80

(define (RLC R L C dt)
  (define (rlc vC0 iL0)
    (define vC (integral (delay dvC) vC0 dt))
    (define iL (integral (delay diL) iL0 dt))
    (define dvC (scale-stream iL (/ -1 C)))
    (define diL (add-streams
                  (scale-stream vC (/ 1 L))
                  (scale-stream iL (- (/ R L)))))
    (stream-map (lambda (x y) (cons x y)) vC iL))
  rlc)

(define RLC1 (RLC 1 1 0.2 0.1))

(display-stream-head (RLC1 10 0) 10)

結果

(10 . 0)
(10 . 1.0)
(9.5 . 1.9)
(8.55 . 2.66)
(7.220000000000001 . 3.249)
(5.5955 . 3.6461)
(3.77245 . 3.84104)
(1.8519299999999999 . 3.834181)
(-0.0651605000000004 . 3.6359559)
(-1.8831384500000004 . 3.2658442599999997)
'done

実行には以下のhelperを使う

#lang racket
(require (prefix-in strm: racket/stream))

;helper
(define-syntax cons-stream
  (syntax-rules ()
    ((_ a b) (strm:stream-cons a b))))
(define stream-car strm:stream-first)
(define stream-cdr strm:stream-rest)
(define stream-null? strm:stream-empty?)
(define (scale-stream stream factor)
  (stream-map (lambda (x) (* x factor)) stream))
(define the-empty-stream strm:empty-stream)
(define (display-stream s) (stream-for-each display-line s))
(define (add-streams s1 s2) (stream-map + s1 s2))
(define (stream-map proc . argstreams)
  (if (stream-null? (car argstreams))
      the-empty-stream
      (cons-stream
       (apply proc (map stream-car argstreams))
       (apply stream-map
              (cons proc (map stream-cdr argstreams))))))
(define (display-stream-head s n)
  (define (iter s n)
    (if (<= n 0)
      'done
      (begin
        (display (stream-car s))
        (newline)
        (iter (stream-cdr s) (- n 1)))))
  (iter s n))
(define (display-line x)
  (newline)
  (display x))

(define (integral delayed-integrand initial-value dt)
  (define int
    (cons-stream initial-value
                 (let ((integrand (force delayed-integrand)))
                   (add-streams (scale-stream integrand dt)
                                int))))
  int)

3.81

入力として ‘generate が来たら乱数を生成。

数値が来たらその数値を使ってresetという動作。

3.6では代入で値を保持していたが、ストリームの中に閉じるので代入は不要になる。

ストリームの中で値を都度 last-value として次の処理に渡してやれば良い。

(define (rand-stream s seed)
  (define (generate last-val)
    ;lcdとか使うと乱数っぽくなるが、テストを楽にするために簡略化
    (+ last-val 1))
  (define (reset new-seed) new-seed)
  (define (dispatch req last-val)
    (cond ((eq? req 'generate) (generate last-val))
          ((number? req) (reset req))
          (else (error "unknown message" req))))
  (define (iter req-stream last-val)
    (let ((new-val (dispatch (car req-stream) last-val)))
      (cons-stream
        new-val
        (iter (stream-cdr req-stream) new-val))))
  (iter s seed))

;test
(define test-stream (list 'generate 5 'generate 'generate 10 20 'generate))

(display-stream-head (rand-stream test-stream 1) 7)

結果。(実行時は3.80と同じhelperを追加する)

2
5
6
7
10
20
21
'done

3.82

試行毎に乱数生成していたのを、あらかじめ乱数ストリームを渡すように改造する。

(define (estimate-integral p x1 x2 y1 y2 )
  (define x-stream (random-in-range-stream x1 x2))
  (define y-stream (random-in-range-stream y1 y2))
  (stream-map
   (lambda (p) (* (* (- x2 x1) (- y2 y1))p))
   (monte-carlo (stream-map p x-stream y-stream) 0 0)))


;単位円をテストする関数
(define (circle x y)
   (<= (+ (square x) (square y)) 1.0))

;中心 (50,50) 半径 50 の円
(define (circle2 x y)
  (<= (+ (square (- x 50.0)) (square (- y 50.0))) (square 50.0)))

; piの見積もり (pi=S/r^2)
; S 円の面積
; r 半径
(define (estimate-pi-stream S-stream r)
  (stream-map (lambda (s) (/ s (square r))) S-stream))

;test
(display-stream-head
 (estimate-pi-stream (estimate-integral circle -1 1 -1 1) 1.0) 100)

(display-stream-head
 (estimate-pi-stream (estimate-integral circle2 0 100 0 100) 50.0) 100)

実行結果(長いので最後の要素だけ)

;単位円
...
2.72
'done
;半径50の円
3.16
'done

精度がいまいちなのは乱数の質の問題?

なお、実行には以下のhelperを使う

#lang racket
(require (prefix-in strm: racket/stream))

;helper
(define-syntax cons-stream
  (syntax-rules ()
    ((_ a b) (strm:stream-cons a b))))
(define stream-car strm:stream-first)
(define stream-cdr strm:stream-rest)
(define stream-null? strm:stream-empty?)
(define (scale-stream stream factor)
  (stream-map (lambda (x) (* x factor)) stream))
(define the-empty-stream strm:empty-stream)
(define (display-stream s) (stream-for-each display-line s))
(define (add-streams s1 s2) (stream-map + s1 s2))
(define (stream-map proc . argstreams)
  (if (stream-null? (car argstreams))
      the-empty-stream
      (cons-stream
       (apply proc (map stream-car argstreams))
       (apply stream-map
              (cons proc (map stream-cdr argstreams))))))
(define (display-stream-head s n)
  (define (iter s n)
    (if (<= n 0)
      'done
      (begin
        (display (stream-car s))
        (newline)
        (iter (stream-cdr s) (- n 1)))))
  (iter s n))
(define (display-line x)
  (newline)
  (display x))
(define ones (cons-stream 1.0 ones))
(define (square x) (* x x))
(define (random-in-range low high)
  (let ((range (- high low)))
    (+ low (random range))))
(define (random-in-range-stream low high)
  (stream-map
   (lambda (x) (random-in-range low high))
   ones))

(define (monte-carlo experiment-stream passed failed)
  (define (next passed failed)
    (cons-stream
     (/ passed (+ passed failed))
     (monte-carlo
      (stream-cdr experiment-stream) passed failed)))
  (if (stream-car experiment-stream)
      (next (+ passed 1) failed)
      (next passed (+ failed 1))))