3.2.5 で解説した Euler 法は簡単で、
これですべてが片付けば喜ばしいのですが、
残念ながらあまり効率が良くありません。高精度の解を計算するために
は、分割数
をかなり大きく取る(=大量の計算をする)必要があります。特
別な場合13を除けば、実際に使われることは滅多にないでしょう。率直
にいって実用性は低いです。
より高精度の公式は、現在まで様々なものが開発されていますが、比較的簡
単で、精度がまあまあ高いものに Runge-Kutta 法と呼ばれるものがあります。
それは
から
を求める漸化式として次のものを用います。
![]() |
| reidai5-3.c |
/*
* reidai5-3.c --- 常微分方程式の初期値問題を Runge-Ketta 法で解く
* http://nalab.mind.meiji.ac.jp/~mk/program/ode_prog/reidai5-3.c
*/
#include <stdio.h>
#include <math.h>
int main(void)
{
/* 開始時刻と終了時刻 */
double a = 0.0, b = 1.0;
/* 初期値 */
double x0;
/* 変数と関数の宣言 */
int N,j;
double t,x,h,f(double,double),k1,k2,k3,k4;
/* 初期値の設定 */
x0 = 1.0;
/* 区間の分割数 N を入力してもらう */
printf("N="); scanf("%d", &N);
/* 小区間の幅 */
h = (b-a) / N;
/* 開始時刻と初期値のセット */
t = a;
x = x0;
printf("%f %f\n", t, x);
/* Runge-Kutta 法による計算 */
for (j = 0; j < N; j++) {
k1 = h * f(t, x);
k2 = h * f(t + h / 2, x + k1 / 2);
k3 = h * f(t + h / 2, x + k2 / 2);
k4 = h * f(t + h, x + k3);
x += (k1 + 2 * k2 + 2 * k3 + k4) / 6;
t += h;
printf("%f %f\n", t, x);
}
printf("%f %17.15f\n", t, x);
return 0;
}
/* 微分方程式 x'=f(t,x) の右辺の関数 f の定義 */
double f(double t, double x)
{
return x;
}
|
コンパイル・実行の結果は次のようになるはずです。
oyabun% gcc reidai5-3.c oyabun% ./a.out N=10 0.000000 1.000000 0.100000 1.105171 0.200000 1.221403 0.300000 1.349858 0.400000 1.491824 0.500000 1.648721 0.600000 1.822118 0.700000 2.013752 0.800000 2.225540 0.900000 2.459601 1.000000 2.718280 1.000000 2.718279744135166 ← たくさんの桁数を表示 oyabun% |
たった
等分なのに相対誤差が
以下になっています
(
であるので、相対誤差
=
)。
Runge-Kutta 法の公式は Euler 法よりは大部面倒ですが、
それに見合うだけの価値があることが納得できるでしょう。