戻る
ルンゲクッタ法のサンプル
ルンゲクッタ法(4次) とオイラー法との比較をしてみる計算です。
(2009.6)
プログラムのダウンロード
解いている問題は、dy/dx = f(x,y) で初期値 x0 y0 になっています。
具体的には、x0=0; y0=2; x_end=2; f(x,y) = 2*x*(1-y*y) で解いています。
答えは、(3*exp(2*x*x)+1)/(3*exp(2*x*x)-1) のはずです。
比較してみると、既に16分割くらいで良い答えになっていますが、
その後、Runge-Kutta で 128分割、Euler 法で 8192 あたりで最高の精度を
示し、それ以上分割すると、数値誤差のために却って精度が落ちて行きます。
どんどん分割数を増やせば、精度がどんどん上るということを、実際の
プログラム上で実装するには、数値誤差に対する考察が必要なことが良く分かります。
gfortran43 rungekutta.f90
./a.out
# n Euler Runge-Kutta Answer
2 -4.0000000 -0.56600535 1.0002236
4 0.40625000 1.1356248 1.0002236
8 1.0000620 1.0007145 1.0002236
16 1.0000006 1.0002341 1.0002236
32 1.0000477 1.0002241 1.0002236
64 1.0001163 1.0002238 1.0002236
128 1.0001647 1.0002236 1.0002236
256 1.0001930 1.0002238 1.0002236
512 1.0002081 1.0002236 1.0002236
1024 1.0002156 1.0002239 1.0002236
2048 1.0002197 1.0002235 1.0002236
4096 1.0002218 1.0002239 1.0002236
8192 1.0002229 1.0002241 1.0002236
16384 1.0002277 1.0002282 1.0002236
32768 1.0002435 1.0002439 1.0002236
65536 1.0002440 1.0002440 1.0002236
131072 1.0004882 1.0004882 1.0002236
262144 1.0009761 1.0009761 1.0002236
524288 1.0019512 1.0019512 1.0002236
1048576 1.0038986 1.0038986 1.0002236
プログラム上の注意点は、関数副プログラムを
ちゃんと external 指定してやって、関数を引数にうける
サブルーチンに渡していることです。call する側、受ける側双方で
external 指定が必要なようです。
program main
implicit none
real :: x0, y0, x_end, y_end_r, y_end_e
real, external :: func, answer
integer :: i, n
x0=0; y0=2; x_end=2
write(*,*) '# n Euler Runge-Kutta Answer'
n=1
do i=1, 20
n = n*2
call euler(x0, y0, n, x_end, func, y_end_e)
call rungekutta(x0, y0, n, x_end, func, y_end_r)
write(*,*) n, y_end_e, y_end_r, answer(x_end)
end do
end
subroutine euler( x0, y0, n, x_end, f, y_end)
implicit none
real :: x0, y0, x_end, y_end
real, external :: f
real :: k1, k2, k3, k4
real :: x, y, dx
integer :: i, n
dx = (x_end - x0)/n
x=x0
y=y0
do i=1, n
y = y + dx*f(x,y)
x = x + dx
! write(*,*) 'Euler = ',i, x, y
end do
y_end=y
! write(*,*) 'Euler ', x0, dx, n, x_end, y_end
end subroutine
subroutine rungekutta( x0, y0, n, x_end, f, y_end)
implicit none
real :: x0, y0, x_end, y_end
real, external :: f
real :: k1, k2, k3, k4
real :: x, y, dx
integer :: i, n
dx = (x_end - x0)/n
x=x0
y=y0
do i=1, n
k1 = dx * f(x, y)
k2 = dx * f( x+dx/2, y+k1/2 )
k3 = dx * f( x+dx/2, y+k2/2 )
k4 = dx * f( x+dx , y+k3 )
y = y + (1.0/6.0)*(k1+2*k2+2*k3+k4)
x = x + dx
! write(*,*) x, y
end do
y_end=y
! write(*,*) 'Rungekutta ', x0, dx, n, x_end, y_end
end subroutine
real function func(x, y)
real :: x, y
func = 2*x*(1-y*y)
end function
real function answer( x )
real :: x
answer = (3*exp(2*x*x)+1)/(3*exp(2*x*x)-1)
end function
玉川一郎