やっとどうやらシミュレーションが出来るようになったので, 今回はその話題である.
SICPの206ページのintegral関数のように披積分関数を遅延にするのが味噌である. SICPの例題では独立変数がdtだけだが, 微分解析機では「回す」値になにが入るかわからないので, こちらもstreamに対応させなければならない.
独立変数に方は回転角の差が必要になるので, stream-cadrからstream-carを引いたものに, 披積分関数のstream-carを掛けることになる.
まずintegratorは次のようだ.
(define (integrator delayed-integrand delayed-variable initial-value) (define (mult-streams a b) (cons-stream (* (stream-car a) (- (stream-car (stream-cdr b)) (stream-car b))) (mult-streams (stream-cdr a) (stream-cdr b)))) (define int (cons-stream initial-value (let ((integrand (force delayed-integrand)) (variable (force delayed-variable))) (add-stream (mult-streams integrand variable) int)))) int)integratorが貰う引数は遅延になっており, intの計算のなかでforceを使って強制する.
次はシミュレーションに必要な補助関数である. これらはSICPにあるものだ.
(define (scale-stream stream factor) (stream-map (lambda (x) (* x factor)) stream)) (define (add-streams s1 s2) (stream-map + s1 s2)) (define (integers-starting-from n) (cons-stream n (integers-starting-from (+ n 1)))) (define integers (integers-starting-from 1))さてこれらが用意できたら, 8月14日のブログにあった関数からやってみる. ブログの図を見ながらプログラムを眺めてほしい.
最初はsin zとcos z. 独立変数はzである. zは整数のstreamで, 1/1000にして使う. sin zのπ/4, つまり45度での値をstream-refで見ることにする.
(define z (scale-stream integers 0.001)) (define sinz (integrator (delay cosz) (delay z) 0)) (define cosz (integrator (delay (scale-stream sinz -1)) (delay z) 1.0)) (display (stream-ref sinz 785)) ;=>.7071024791302126値としてはよさそうである.
次はez.
(define z (scale-stream integers 0.001)) (define ez (integrator (delay ez) (delay z) 1.0)) (stream-ref ez 1000) ;=>2.716923932235898z2とz3は次のようにする.
(define z (scale-stream integers 0.001)) (define izdz (integrator (delay z) (delay z) 0)) (define z2 (scale-stream izdz 2)) (define iz2dz (integrator (delay z2) (delay z) 0)) (define z3 (scale-stream iz2dz 3)) (stream-ref z3 500) ;=>.12499950000000003 (stream-ref z2 500) ;=>.250499999999999940.5の3乗が0.125, 2乗が0.25ならまぁ問題はない.
tan zは多少手強いが,
(define ones (cons-stream 1 ones)) (define z (scale-stream integers 0.001)) (define tanz (integrator (delay 1+tanzsq) (delay z) 0)) (define itanzdtanz (integrator (delay tanz) (delay tanz) 0)) (define tanzsq (scale-stream itanzdtanz 2)) (define 1+tanzsq (add-streams tanzsq ones)) (stream-ref tanz 785) ;=>.9979528633948679 (stream-ref tanz (quotient 3142 6)) ;=>.5761873674775257テストしたのはtan π/4とtan π/6で, 1/√ 3=.5773502691896258だからこれも合格である.
1/zとlogezはzを1から積分する. z=1での初期値はどちらも0である.
(define z (scale-stream integers 0.001)) (define logez (integrator (delay 1/z) (delay z) 0)) (define 1/z (integrator (delay (scale-stream 1/z -1)) (delay logez) 1)) (stream-ref 1/z 500) ;=>.6664863143078377 (stream-ref logez 1718) ;=>.9999481260920271/zの500での値は1/1.5なので0.6666.... logezの1718はloge2.718だから1でいいわけである. zの値は1からのはずだが, 回転角の差を使うから, integersで構わない.
微分解析機には結果を描画する出力装置もついているから, 次はscheme-graphicsを駆動するようなプログラムにしたい.
0 件のコメント:
コメントを投稿