通常,我々がテイラー展開を使う場合,この式 (2)〜式(4) のいずれかである.
(6) |
オイラー法での数値計算では,計算の刻み幅は十分に小さいとして,
(7) |
オイラー法では,次の漸化式に従い数値計算を進める.解である が同じ手続きで計算できる. 実際にプログラムを行うときは,forやwhileを用いて繰り 返し計算を行い,結果のとは,配列x[i] や y[i]に格納するのが常套手段である.
この方法の計算のイメージは,図1の通りである.明らか に,出発点の導関数のみ利用しているために精度が悪いことが理解できる.式 も対称でないため,逆から計算すると元に戻らない.
(9) |
1 #include <stdio.h> 2 #include <math.h> 3 #define NSTEPS 2000 // 計算ステップ数 4 #define NDIM 30000 // 用意する配列の大きさ 5 #define X_MAX 2.0 // 計算する最大 x 6 7 double f(double x, double y); // プロトタイプ宣言 8 9 //============================================================ 10 // main 関数 11 //============================================================ 12 int main(void){ 13 double x[NDIM], y[NDIM]; // 計算結果を入れる 14 double dx; 15 int i; 16 FILE *out; // 計算結果を保存するファイル 17 18 19 dx = X_MAX/NSTEPS; // 計算のきざみ幅dxの計算 20 x[0] = 0; 21 y[0] = 0; 22 23 24 //------- オイラー法の計算 -------- 25 26 for(i=0; i<NSTEPS; i++){ 27 x[i+1] = x[0]+(i+1)*dx; //x[i+1]=x[i]+dxよりも精度が良い 28 y[i+1] = y[i]+f(x[i],y[i])*dx; 29 } 30 31 32 //------- 計算結果をファイルに保存 -------- 33 34 out = fopen("result.txt","w"); 35 for(i=0; i<=NSTEPS; i++){ 36 fprintf(out,"%20.15f\t%20.15f\n", x[i],y[i]); 37 } 38 39 40 fclose(out); 41 42 return 0; 43 } 44 45 //============================================================ 46 // dy/dx = f(x,y) の f(x,y)を計算する関数 47 //============================================================ 48 double f(double x, double y){ 49 return x*x*sin(x)+sqrt(y)*cos(y); 50 }
2次の精度ということは,テイラー展開より
(10) |
式(11)から分かるように,の増分を計算する ためには,1階微分と2階微分の2項を満たす式が必要である.そうすると少なく とも,2点の値が必要となる.2点として,計算区間の両端の導関数の値を 使う.この導関数は問題として与えられているので,計算は簡単である.そ うして,区間の増分を をパラメーターとした和で表すことに する.即ち,以下の通りである.
(13) |
(17) |
ということで,皆さんが常微分方程式を計算する必要が生じたときは,何はともあれ4次 のルンゲ・クッタで計算すべきである.普通の科学に携わる人にとって,4次のルンゲ・ クッタは常微分方程式の最初で最後の解法である.
4次のルンゲ・クッタの公式は,式(20)に示す通りである.そし て,これは図4のように表すことができる.
#include <stdio.h> #include <math.h> #define IMAX 100001 double func(double x, double y); /*================================================================*/ /* main function */ /*================================================================*/ int main(void){ double x[IMAX], y[IMAX]; double final_x, h; double k1, k2, k3, k4; int ncal, i; /*--- set initial condition and cal range ---*/ x[0]=0.0; y[0]=0.0; final_x=10.0; ncal=10000; /* --- size of calculation step --- */ h=(final_x-x[0])/ncal; /* --- 4th Runge Kutta Calculation --- */ for(i=0; i < ncal; i++){ k1=h*func(x[i],y[i]); k2=h*func(x[i]+h/2.0, y[i]+k1/2.0); k3=h*func(x[i]+h/2.0, y[i]+k2/2.0); k4=h*func(x[i]+h, y[i]+k3); x[i+1]=x[i]+h; y[i+1]=y[i]+1.0/6.0*(k1+2.0*k2+2.0*k3+k4); } return 0; } /*================================================================*/ /* define function */ /*================================================================*/ double func(double x, double y){ double dydx; dydx=sin(x)*cos(x)-y*cos(x); return(dydx); }