P402 Note: Numerical Solutions of Second-Order Differential Equations — The Runge-Kutta Method 참조
Elementary Differential Equations and Boundary Value Problems , 8.6 Systems of First Order Equations 참조
무더운 여름, 컴퓨터를 켜지않고 아이패드에서 pixie scheme III 로 노는것이 너무 재밌다. ^ ^
그래서 전에 올렸던 너무나 Imperative 한 Runge-Kutta Method 소스를 함수프로그래밍 기법으로 다시 작성해봤다.
먼저 , P402 Note 를 보면, 단진동의 미분방정식을 어떻게 프로그래밍하는지를 가장 쉽게 알수있다.
f = ma 는 a = f/m 즉, 위의 두번째 식으로 쓸수있다. (첫번째 식은 후크의 법칙)
위의 이계도 미분방정식은 다시 두개의 first-order 방정식으로 변환된다.
미분의 정의만 알아도 위 식들을 쉽게 이해할수있다.
P402 Note 의 C 소스를 그대로 scheme 으로 옮겨보면,
(define mass 1.0)
(define spring-constant 1.0)
(define (f time x v)
(- (/(* spring-constant x) mass)))
(define (euler fn time x v tmax n i)
(display time) (display " ")
(display x) (display " ")
(display v) (display " ")
(newline)
(if (> i n)
'()
(euler fn (+ time (/ tmax (- n 1)))
(+ x (* v (/ tmax (- n 1))))
(+ v (* (fn time x v) (/ tmax (- n 1))))
tmax n (+ i 1))))
;(euler f 0.0 1.0 1.0 20 100 0)
마지막의 i 는 카운터 역활이다. 런지 쿠타 방식을 적용하기 위해서는 함수의 인자수를 바꾸어야한다.
(이 부분이 현재의 난점이다)
(define (g time x)
(- (/(* spring-constant x) mass)))
(define (rk fn time x v tmax n i)
(begin
(display time) (display " ")
(display x) (display " ")
(display v) (display " ")
(newline)
(if (> i n)
'()
(let* ((dt (/ tmax (- n 1)))
(k1 (* dt (fn time x)))
(k2 (* dt (fn (+ time (/ dt 2)) (+ x (/ k1 2)))))
(k3 (* dt (fn (+ time (/ dt 2)) (+ x (/ k2 2)))))
(k4 (* dt (fn time (+ x k3))))
(v2 (/ (+ k1 (* 2 k2) (* 2 k3) k4) 6))
(dx (* dt v2))
(x2 (/ (+ dx (* 2 dx) (* 2 dx) dx) 6))
(t2 (+ dt time)))
(rk fn t2 x2 v2 tmax n (+ i 1))))))
(rk g 0.0 1.0 1.0 20 100 0) 와 같이 사용한다.
이제 보다 일반화 시켜서
Elementary Differential Equations and Boundary Value Problems ,
8.6 Systems of First Order Equations 의 문제에 적용시켜보면
(define (f1 t x y)
(- x (* 4 y)))
(define (g1 t x y)
(- y x))
(define (euler2 f g t0 x0 y0 tmax n i)
(begin
(display x0) (display " ") (display y0)
(newline)
(if (> i n)
'()
(let* ((h (/ tmax n))
(k1 (f t0 x0 y0))
(x1 (+ x0 (* h k1)))
(p1 (g t0 x0 y0))
(y1 (+ y0 (* h p1)))
(t1 (+ h t0)))
(euler2 f g t1 x1 y1 tmax n (+ i 1))))))
(euler2 f1 g1 0.0 1.0 0 1 10 0)
1. 0
1.1000000000000001 -0.10000000000000001
1.25 -0.22000000000000003
1.4630000000000001 -0.36699999999999999
1.7561 -0.55
2.15171 -0.78061
2.679125 -1.073842
3.3765743000000001 -1.4491387
4.2938872100000003 -1.93171
5.4959599310000007 -2.5542697209999998
7.0672638125000011 -3.3592926861999999
9.117707268230001 -4.4019483360700002
와 같이 사용할수있다. 다시 런지쿠타 를 위해 인자수를 두개로 조절한다. ㅡ ㅡ
(define (f2 x y)
(- x (* 4 y)))
(define (g2 x y)
(- y x))
(define (rk2 f g t0 x0 y0 tmax n i)
(begin
(display x0) (display " ") (display y0)
(newline)
(if (> i n)
'()
(let* ((h (/ tmax n))
(k1 (f x0 y0))
(p1 (g x0 y0))
(k2 (f (+ x0 (* h 0.5 k1)) (+ y0 (* h 0.5 p1))))
(p2 (g (+ x0 (* h 0.5 k1)) (+ y0 (* h 0.5 p1))))
(k3 (f (+ x0 (* h 0.5 k2)) (+ y0 (* h 0.5 p2))))
(p3 (g (+ x0 (* h 0.5 k2)) (+ y0 (* h 0.5 p2))))
(k4 (f (+ x0 (* h k3)) (+ y0 (* h p3))))
(p4 (g (+ x0 (* h k3)) (+ y0 (* h p3))))
(x1 (+ x0 (* (/ h 6) (+ k1 (* 2 k2) (* 2 k3) k4))))
(y1 (+ y0 (* (/ h 6) (+ p1 (* 2 p2) (* 2 p3) p4))))
(t1 (+ h t0)))
(rk2 f g t1 x1 y1 tmax n (+ i 1))))))
(rk2 f2 g2 0 1.0 0 1 10 0) 를 실행하면,
1. 0
1.1273374999999999 -0.11125
1.3203960889062498 -0.25083259375
1.6001525300960995 -0.42966705404746086
1.9951137919483317 -0.6623967515154204
2.5439331488549146 -0.9687011072157672
3.2989432289082439 -1.3750656472659641
4.330926625352701 -1.917170503340736
5.7355568684951788 -2.6431137893803749
7.6420939775114505 -3.6177619931556868
10.225123206327096 -4.9286217159572976
13.720401496213769 -6.6937650404168991
(rk2 f2 g2 0 1.0 0 1 5 0)
1. 0
1.3200666666666667 -0.2506666666666667
1.9939111155555558 -0.6617934222222223
3.2956538046225186 -1.3734184898607409
5.727563671522403 -2.63911452154088
10.206918044252664 -4.91951640306368
18.406447393088314 -9.05262374289693
등과 같은 결과를 얻는다.
원래 shoot 까지 구현하려고 했는데, 함수인자 갯수 부분이 완전히 이해 되지않아 다음으로 미룬다.