12
2 編 第 6 章 常微分方程式 微分方程式とは未知関数とその導関数からなる等式のことであり,常微分方程式とは独 立変数が一つの微分方程式である.その例を次に示す. () = (, ) 常微分方程式が線形のときには解析的に解が得られるが,それが非線形のときには解析的 な解が得られることはほとんど期待できない.一方,数値的解法は,線形や非線形にかか わらず,解を数値的に得られる.工学では非線形な問題を扱うことが多いので,数値的解 法が非常に役に立つ. 初めに数値的解法の中で最も簡単なEuler オイラー 法について説明する.次にそれよりも精度や効 率が良いRunge -Kutta クッタ 法,Runge -Kutta クッタ -Gill ジル 法およびMilne 法について説明する. 6.0 Euler オイラー 次の 1 階常微分方程式を数値的に解く方法について述べる. = (, ) 教科書(1)図の曲線 y (x) について,任意の x のときの y の値は次のように求められる. まず,x x0 から微小な量 h だけ増加した点 x1x1 = x0 + h )における y1 は,y の増分, y1 y0 f 1 次近似(初期値 (x0, y0) 周りでテイラー展開)で示すと, 1 = 0 + ℎ ∙ ( 0 , 0 ) (P1)同様に 2 = 1 + ℎ ∙ ( 1 , 1 ) +1 = + ℎ ∙ ( , ) (P2)つまり初期値( 0 , 0 )が与えられた後,(P2)式の計算を繰り返すことで,任意の xi のときの yi の値が次々に求められる,これは関数 y (x)を得た(常微分方程式を解いた)ことになる. オイラー法は AB 間が直線でよく近似できるような適切な h を選ぶと精密な解が得られる. 6.0 オイラー法 y(x) 1 B A 0 h x0 x1

bj.base.ibaraki.ac.jpbj.base.ibaraki.ac.jp/distribution/2_6m.pdf第2編 第6章 常微分方程式 微分方程式とは未知関数とその導関数からなる等式のことであり,常微分方程式とは独

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

第 2 編 第 6 章 常微分方程式

微分方程式とは未知関数とその導関数からなる等式のことであり,常微分方程式とは独

立変数が一つの微分方程式である.その例を次に示す.

𝑑𝑦(𝑥)

𝑑𝑥= 𝑓(𝑥, 𝑦)

常微分方程式が線形のときには解析的に解が得られるが,それが非線形のときには解析的

な解が得られることはほとんど期待できない.一方,数値的解法は,線形や非線形にかか

わらず,解を数値的に得られる.工学では非線形な問題を扱うことが多いので,数値的解

法が非常に役に立つ.

初めに数値的解法の中で最も簡単なEulerオイラー

法について説明する.次にそれよりも精度や効

率が良いRungeル ン ゲ

-Kuttaク ッ タ

法,Rungeル ン ゲ

-Kuttaク ッ タ

-Gillジ ル

法およびMilneミ ル ン

法について説明する.

6.0 Eulerオイラー

次の 1 階常微分方程式を数値的に解く方法について述べる.

𝑑𝑦

𝑑𝑥= 𝑓(𝑥, 𝑦) 教科書(1)式

図の曲線 y (x) について,任意の x のときの y の値は次のように求められる.

まず,x が x0から微小な量 h だけ増加した点 x1( x1 = x0 + h )における y1は,y の増分,

y1 - y0を f の 1 次近似(初期値 (x0, y0) 周りでテイラー展開)で示すと,

𝑦1 = 𝑦0 + ℎ ∙ 𝑓(𝑥0, 𝑦0) (P1)式

同様に

𝑦2 = 𝑦1 + ℎ ∙ 𝑓(𝑥1, 𝑦1)

𝑦𝑖+1 = 𝑦𝑖 + ℎ ∙ 𝑓(𝑥𝑖 , 𝑦𝑖) (P2)式

つまり初期値(𝑥0, 𝑦0)が与えられた後,(P2)式の計算を繰り返すことで,任意の xi のときの

yiの値が次々に求められる,これは関数 y (x)を得た(常微分方程式を解いた)ことになる.

オイラー法は AB 間が直線でよく近似できるような適切な h を選ぶと精密な解が得られる.

図 6.0 オイラー法

y(x)

𝑦1 B

A

𝑦0 h

x0 x1

6.1 Rungeル ン ゲ

-Kuttaク ッ タ

𝑑𝑦

𝑑𝑥= 𝑓(𝑥, 𝑦) 教科書(1)式

上において,x が x0から h だけ増加した点,x1 (x1 = x0 + h)における y1は Runge-Kutta の公

式でも計算できる.

𝑘1 = ℎ ∙ 𝑓(𝑥0, 𝑦0) 教科書(2)式

𝑘2 = ℎ ∙ 𝑓 (𝑥0 +ℎ

2, 𝑦0 +

𝑘1

2) 教科書(3)式

𝑘3 = ℎ ∙ 𝑓 (𝑥0 +ℎ

2, 𝑦0 +

𝑘2

2) 教科書(4)式

𝑘4 = ℎ ∙ 𝑓(𝑥0 + ℎ, 𝑦0 + 𝑘3) 教科書(5)式

𝑘 =1

6(𝑘1 + 2𝑘2 + 2𝑘3 + 𝑘4) 教科書(6)式

𝑦1 = 𝑦0 + 𝑘

同様に

𝑦2 = 𝑦1 + 𝑘𝑛𝑒𝑤

𝑦𝑖+1 = 𝑦𝑖 + 𝑘𝑛𝑒𝑤 (P3)式

𝑘𝑛𝑒𝑤は教科書(2)から(6)式によって毎回更新される.つまり初期値(𝑥0, 𝑦0)が与えられた後,

(P3)式の計算を繰り返すことで,任意の xiのときの yiの値が求められる.式の意味は次のと

おり.(下の図も参照のこと.)

① 傾き f (x0, y0) で 点 (x0, y0) から x が h だけ進むと,k1を得る.

② この線上の点 (x0 + h/2, y0+k1/2) での傾きで (x0, y0) から h だけ進むと,k2を得る.

③ 同様に,傾き f (x0+h/2, y0+k2/2) で (x0, y0) から h だけ進むと,k3を得る.

④ 次に,傾き f (x0+h, y0+k3) で (x0, y0) から h だけ進むと,k4を得る.

⑤ 次に,①から④の加重平均 k を教科書(6) 式で求め,y1を得る.

つまり①から⑤は x0における傾きを 4 次の式で求めていることに相当する.

図 6.1 Rungeル ン ゲ

-Kuttaク ッ タ

ここでルンゲ・クッタ法のプログラムを示す.下の常微分方程式について,初期条件(x0, y0)

= (0, 0)のもとで x = π / 2 まで,15 等分して求める.

𝑑𝑦

𝑑𝑥= sin 𝑥 + cos𝑦

コンパイルするときは次のように行う.

gcc -o sp6_1 sp6_1.c -lm

例題 6.1 のプログラム(演習問題 1,2,4,7,8,10 に関係する.)

#include<stdio.h>

#include<math.h>

#define F(x, y) (sin(x)+cos(y)) /* 式を必ず括弧でくくること */

#define h 3.1416/(2.*15.) /* 刻み値 h の定義(rad 単位),π/2 を 15 等分 */

void main(void)

{

double x, y, k1, k2, k3, k4;

int i=0; /* i: 計算のステップ数 */

x = y = 0.; /* 初期値の設定,x0, y0 */

printf("i x y¥n"); /* 変数名の画面表示 */

printf("%2d %6.3f %6.3f¥n", i, x, y); /* 初期値の画面表示 */

do {

i++; /*ステップを 1 つ進める */

k1=h*F(x, y); /* 教科書(2)式の計算 */

k2=h*F(x+h/2., y+k1/2.); /* 教科書(3)式の計算 */

k3=h*F(x+h/2., y+k2/2.); /* 教科書(4)式の計算 */

k4=h*F(x+h, y+k3); /* 教科書(5)式の計算 */

y+=(k1+2.*k2+2.*k3+k4)/6.; /* 教科書(6)式の計算 */

x+=h; /* x の増分の計算:xi+1=xi+h */

printf("%2d %6.3f %6.3f¥n", i, x, y); /* 値の表示 */

} while(i-15<0); /* 計算を 15 回繰り返す */

}

define文の注意: define 文で宣言した式はコンパイル時に次のように解釈される.

define 文は式の計算の優先順序を考慮して括弧をつける必要がある.例をあげる.

#define F1(x, y) (x+y) /* 式を括弧でくくる場合 */

#define F2(x, y) x+y /* 式を括弧でくくらない場合 */

int a, b;

a = 2 * F1(1, 1); /* 左の式は次のように計算される:a = 2 * (1 + 1) = 4 */

b = 2 * F2(1, 1); /* 左の式は次のように計算される:a = 2 * 1 + 1 = 3 */

define 文での式の表現の煩わしさを避けるなら,副関数を使えばよい.

(例) double F2(double x, double y) { return( x + y ); }

演習問題を解くときに注意が必要である.

6.2 Rungeル ン ゲ

-Kuttaク ッ タ

-Gillジ ル

𝑑𝑦

𝑑𝑥= 𝑓(𝑥, 𝑦) 教科書(1)式

上において,x が x0から h だけ増加した点 x1 (x1 = x0 + h)における y1は次の Runge-Kutta-Gill

の公式でも計算できる.

𝑘1 = ℎ ∙ 𝑓(𝑥0, 𝑦0),𝑟1 =1

2(𝑘1 − 2𝑞0)

𝑦(1) = 𝑦0+ 𝑟1,𝑞1 = 𝑞0 + 3𝑟1 −

1

2𝑘1

𝑘2 = ℎ ∙ 𝑓 (𝑥0 +ℎ

2, 𝑦(1)),𝑟2 = (1 −

√2

2) (𝑘2 − 𝑞1)

𝑦(2) = 𝑦(1) + 𝑟2,𝑞2 = 𝑞1 + 3𝑟2 − (1 −√2

2) 𝑘2

𝑘3 = ℎ ∙ 𝑓 (𝑥0 +ℎ

2, 𝑦(2)),𝑟3 = (1 +

√2

2) (𝑘3 − 𝑞2)

𝑦(3) = 𝑦(2) + 𝑟3,𝑞3 = 𝑞2 + 3𝑟3 − (1 +√2

2) 𝑘3

𝑘4 = ℎ ∙ 𝑓(𝑥0 + ℎ, 𝑦(3)),𝑟4 =

1

6(𝑘4 − 2𝑞3)

𝑦1= 𝑦(3) + 𝑟4,𝑞4 = 𝑞3 + 3𝑟4 −

1

2𝑘4 }

教科書(7)式

ただし q0の初期値は 0 である.

次に,得られた x1を x0,y1を y0,また q4を q0と置き直した後,再び教科書(7)式を計算す

る.これらを繰り返すことによって,任意の xiのときの yiの値を求めることができる.

ここでルンゲ・クッタ・ジル法のプログラムを示す.問題は例題 6.1 と同じである.下の

常微分方程式について,初期条件(x0, y0) = (0, 0)のもとで x = π / 2 まで,15 等分して求める.

𝑑𝑦

𝑑𝑥= sin 𝑥 + cos𝑦

コンパイルするときは次のように行う.

gcc -o sp6_2 sp6_2.c -lm

例題 6.2 のプログラム(演習問題 5,8,9 に関係する.)

#include<stdio.h>

#include<math.h>

#define F(x, y) (sin(x)+cos(y)) /* 関数の定義 */

#define h 3.1416/(2.*15.) /* 刻み値 h の定義(rad 単位),π/2 を 15 等分 */

#define s sqrt(2.)/2. /* ルンゲ・クッタ・ジル法で使う定数√2/2 の定義 */

void main(void)

{

double k1, k2, k3, k4, r1, r2, r3, r4, q0=0., q1, q2, q3, q4; /* q0 の初期値は 0 */

double x=0., y=0., y1, y2, y3; /* 初期値設定 x0=0,y0=0 */

int i=0; /* 計算のステップ数 */

printf("i x y¥n"); /* 変数名の画面表示 */

printf("%2d %6.3f %6.3f¥n", i, x, y); /* 初期値の画面表示 */

do {

i++; /* ステップ数を増やす */

k1=h*F(x, y); r1=(k1-2.*q0)/2.; /* 教科書(7)式の計算 */

y1=y+r1; q1=q0+3.*r1-k1/2.;

k2=h*F(x+h/2., y1); r2=(1.-s)*(k2-q1);

y2=y1+r2; q2=q1+3.*r2-(1.-s)*k2;

k3=h*F(x+h/2., y2); r3=(1.+s)*(k3-q2);

y3=y2+r3; q3=q2+3.*r3-(1.+s)*k3;

k4=h*F(x+h, y3); r4=(k4-2.*q3)/6.;

y=y3+r4; q4=q3+3.*r4-k4/2.;

x+=h; q0=q4; /* x1を x0,また q4を q0と置き直おす */

printf("%2d %6.3f %6.3f¥n", i, x, y); /* 計算値の表示 */

} while(i-15<0); /* 計算を 15 回繰り返す */

}

6.3 Milneミ ル ン

図で四つの座標データ,A(xi-3,yi-3) ,B(xi-2,

yi-2) ,C(xi-1,yi-1) ,D(xi ,yi)および f が与えら

れたとき,座標 E(xi+1,yi+1) が求められる.𝑥𝑖+1に

ついては 𝑥𝑖+1 = 𝑥𝑖 + ℎ からすぐ求められる.一

方,𝑦𝑖+1は予測子と修正子による二つの値から

求められる.前者はLagrangeラ グ ラ ン ジ ュ

の補間多項式で近

似した 𝑦𝑖+1 の値,後者はSimpsonシ ン プ ソ ン

の 1/3 公式で

近似したその値である.

ここで教科書(1)式をもう一度示す.

𝑑𝑦

𝑑𝑥= 𝑓(𝑥, 𝑦) 教科書(1)式

ここで yi+1を予測したもの,予測子(predictor),

𝑦𝑖+1(𝑃)を使う.教科書(1)式を点 A から E まで積分すると,

𝑦𝑖+1(𝑃)− 𝑦𝑖−3 = ∫ 𝑓(𝑥, 𝑦)

𝑥𝑖+1

𝑥𝑖−3

𝑑𝑥 ≈ ∫ 𝑃(𝑥)𝑥𝑖+1

𝑥𝑖−3

𝑑𝑥

教科書(8)式

左辺は 𝑦𝑖+1(𝑃)

と 𝑦𝑖−3 の差を意味し,中央は区間 h ごとの高さの差の累積を意味する.右辺

は f を補間多項式 P (x)で近似している.今,f を 3 次のLagrangeラ グ ラ ン ジ ュ

の補間多項式で近似するな

ら P (x)は教科書 p. 148,(2)式より,

𝑃(𝑥) = 𝑓𝑖−3(𝑥+ℎ)𝑥(𝑥−ℎ)

(−ℎ)(−2ℎ)(−3ℎ)+ 𝑓𝑖−2

(𝑥+2ℎ)𝑥(𝑥−ℎ)

ℎ∙(−ℎ)∙(−2ℎ)+ 𝑓𝑖−1

(𝑥+2ℎ)(𝑥+ℎ)(𝑥−ℎ)

2ℎ∙ℎ∙(−ℎ)+ 𝑓𝑖

(𝑥+2ℎ)(𝑥+ℎ)𝑥

3ℎ∙2ℎ∙ℎ

教科書(9)式

教科書(9)式を教科書(8)式へ代入し,P (x)を 𝑥𝑖−1を中心として𝑥𝑖−3から 𝑥𝑖+1まで,つまり-2h

から 2h まで積分すると,予測子𝑦𝑖+1(𝑃)は

𝑦𝑖+1(𝑃)

= 𝑦𝑖−3 +4

3ℎ(2𝑓𝑖−2 − 𝑓𝑖−1 + 2𝑓𝑖) 教科書(10)式

A

B

C

D

E

yi+1 f

yi

yi-1

yi-2

yi-3

h h h h h

xi-3 xi-2 xi-1 xi xi+1

図 6.2 Milne法

ただし𝑓𝑖 = 𝑓(𝑥𝑖 , 𝑦𝑖)である.(付録,計算 1 参照.)

次に予測子を修正するもの,修正子(corrector)について考える.今,

𝑓𝑖+1 = 𝑓 (𝑥𝑖+1, 𝑦𝑖+1(𝑃) ) 教科書(11)式

として,xi-1,xi,xi+1の 3 つの点に Simpson の 1/3 公式(教科書 P.158,教科書(11)式)を適

用すると,修正子 𝑦𝑖+1(𝐶)は

𝑦𝑖+1(𝐶)

= 𝑦𝑖−1 +ℎ

3(𝑓𝑖−1 + 4𝑓𝑖 + 𝑓𝑖+1) 教科書(12)式

予測子と修正子を求め,それらの差が非常に小さくなったとき, 𝑦𝑖+1 を予測子の値とす

る.その判定式は次のとおり.

|𝑦𝑖+1(𝑃)− 𝑦𝑖+1

(𝐶)| < 𝜀 教科書(13)式

ε はゼロに近い値で,教科書の例題 6.3 では ε = 1×10-5を採用している.

もし上が成立しなければ,修正子 𝑦𝑖+1(𝐶)

を予測子 𝑦𝑖+1(𝑃)

に置き換え,教科書 (11) 式の 𝑓𝑖+1

を計算し,𝑦𝑖+1(𝐶)

を計算する.これらの手順を繰り返す.(教科書の 𝑓𝑖+1(𝐶)

は 𝑦𝑖+1(𝐶)

の間違い

である.例題 6.3 のプログラムを見よ.)

ミルン法の長所は,過去に計算した値から新しい y を求めるため,計算量はルンゲ・クッ

タ法より少ないことである.一方その短所は,計算を始めるために四つの点のデータを他

のアルゴリズムで求める手間があり,またオイラー法やルンゲクッタ法より計算の安定性

が劣ることである.

ここでミルン法のプログラムを示す.問題は例題 6.1 と同じである.下の常微分方程式に

ついて,初期条件(x0, y0) = (0, 0)のもとで x = π/2 まで,x を 15 等分して求める.初期値から

3 点はルンゲ・クッタ法で求めている.

𝑑𝑦

𝑑𝑥= sin 𝑥 + cos𝑦

コンパイルするときは次のように行う.

gcc -o sp6_3 sp6_3.c -lm

例題 6.3 のプログラム(演習問題 3,6,8 に関係する.)

#include<stdio.h>

#include<math.h>

#define F(x, y) (sin(x)+cos(y)) /* 関数の定義 */

#define h 3.1416/(2.*15.) /* 刻み値 h の定義 */

#define eps 1.e-5 /* 収束判定条件で使われる閾値(しきいち) */

void main(void)

{

double x[16], y[16], k1, k2, k3, k4;

double f, f1, f2, fp, yp, yc;

int i=0; /*ステップ数 */

x[0]=y[0]=0.; /* 初期値設定 x0=0,y0=0 */

printf("i x y¥n"); /* 変数名の画面表示 */

printf("%2d %6.3f %6.3f¥n", i, x[0], y[0]); /* 初期値の画面表示 */

for(i=0; i<3; ++i) { /* x1 から x3 までの計算 */

k1=h*F(x[i], y[i]); /* ルンゲ・クッタ法による計算 */

k2=h*F(x[i]+h/2., y[i]+k1/2.);

k3=h*F(x[i]+h/2., y[i]+k2/2.);

k4=h*F(x[i]+h, y[i]+k3);

y[i+1]=y[i]+(k1+2.*k2+2.*k3+k4)/6.;

x[i+1]=x[i]+h;

printf("%2d %6.3f %6.3f¥n", i+1, x[i+1], y[i+1]); /* 計算結果の表示 */

}

for(i=3; i<15; ++i){ /* ミルン法による計算 */

x[i+1]=x[i]+h; /* xi+1=xi+h */

f=F(x[i], y[i]); /* fi の計算 */

f1=F(x[i-1], y[i-1]); /* fi-1 の計算 */

f2=F(x[i-2], y[i-2]); /* fi-2 の計算 */

yp=y[i-3]+4./3.*h*(2.*f2-f1+2*f); /* 教科書(10)式の計算 */

while(1) {

fp=F(x[i+1], yp); /* 教科書(11)式の計算 */

yc=y[i-1]+h/3.*(f1+4*f+fp); /* 教科書(12)式の計算 */

if(fabs(yp-yc)<eps) break; /* 教科書(13)式の収束判定 */

yp=yc; /* */

}

y[i+1]=yc;

printf("%2d %6.3f %6.3f¥n", i+1, x[i+1], y[i+1]);

}

}

6.4 連立常微分方程式(n 階常微分方程式の解き方)

ここでは n 階常微分方程式の解き方について学ぶ.手順は,

(手順 1)n 階常微分方程式を n 元連立 1 階常微分方程式へ変換する.

(手順 2)n 個の 1 階常微分方程式をオイラー法やルンゲ・クッタ法などで解く

これらの処理により,n 階常微分方程式の数値解が得られる.

例として次の 2 階常微分方程式(例題 6.4)について説明する.

𝑑2𝑦

𝑑𝑥2= 𝑥

𝑑𝑦

𝑑𝑥+ 𝑦 (P3)式

手順 1: 𝑑𝑦1

𝑑𝑥= 𝑦2とおくと,上の式は次の 2 元連立 1 階常微分方程式へ変換できる.

{

𝑑𝑦1

𝑑𝑥= 𝑦2

𝑑𝑦2

𝑑𝑥= 𝑥𝑦2 + 𝑦1

(P4)式

上の二つの式をベクトル表示で 1 つにまとめると次式のように表現できる.

𝑑𝒚

𝑑𝑥= 𝒇(𝑥, 𝒚) (P5)式

ただし𝒚 = [𝑦1𝑦2],𝒇 = [

𝑓1(𝑥, 𝑦1, 𝑦2)

𝑓2(𝑥, 𝑦1, 𝑦2)] = [

𝑦2𝑥𝑦2 + 𝑦1

]である.

手順 2: 上の二つの式をオイラー法で解くならば,式は次のようになる.

𝑦1 𝑖+1 = 𝑦1 𝑖 + ℎ ∙ 𝑦2 𝑖 (P6a)式

𝑦2 𝑖+1 = 𝑦2 𝑖 + ℎ ∙ (𝑥𝑖 ∙ 𝑦2 𝑖 + 𝑦1 𝑖+1) (P6b)式

初期条件,つまり i = 0 のときの x0,y10,y20を用意して,(P6a)式,次に(P6b)式を計算すれ

ば数値解が得られる.

n 階常微分方程式について扱うならば,yと fを次のように拡張すればよい.

𝒚 = [

𝑦1𝑦2⋮𝑦𝑛

], 𝒇 =

[ 𝑓1(𝑥, 𝑦1, 𝑦2,⋯,𝑦𝑛)

𝑓2(𝑥, 𝑦1, 𝑦2,⋯,𝑦𝑛)

⋮𝑓𝑛(𝑥, 𝑦1, 𝑦2,⋯,𝑦𝑛)]

これをルンゲ・クッタ法で解くならば,教科書(2)式は次式に書き換えられる.

𝒌1 = ℎ𝒇(𝑥0, 𝒚0)

ただし

[

𝑘11𝑘12⋮𝑘1𝑛

] =

[ ℎ𝑓1(𝑥0, 𝑦10, 𝑦20,⋯,𝑦𝑛0)

ℎ𝑓2(𝑥0, 𝑦10, 𝑦20,⋯,𝑦𝑛0)

⋮ℎ𝑓𝑛(𝑥0, 𝑦10, 𝑦20,⋯,𝑦𝑛0)]

である.同様にして,教科書(3),(4),(5) 式からベクトル k2,k3,k4が求められる.従って

教科書 (6) 式も同様に次のように書き換えられる.

𝒌 = [

𝑘1𝑘2⋮𝑘𝑛

] =1

6{[

𝑘11𝑘12⋮𝑘1𝑛

] + 2 [

𝑘21𝑘22⋮𝑘2𝑛

] + 2 [

𝑘31𝑘32⋮𝑘3𝑛

] + [

𝑘41𝑘42⋮𝑘4𝑛

]}

これらより,

𝒚1 = 𝒚0 + 𝒌

同様に

𝒚2 = 𝒚1 + 𝒌𝑛𝑒𝑤

𝒚𝑖+1 = 𝒚𝑖 + 𝒌𝑛𝑒𝑤

以上の手順で n 階常微分方程式の解を得ることができる.

ここで 2 階常微分方程式の解法についてのプログラムを示す.下の 2 階常微分方程式に

ついて,初期条件(x0, y0) = (0, 1) ,𝑑𝑦

𝑑𝑥=1 のもとで,ルンゲ・クッタ法により x = 1 まで,10

等分して求める.

𝑑2𝑦

𝑑𝑥2= 𝑥

𝑑𝑦

𝑑𝑥+ 𝑦

上の式を次のように 1 階連立常微分方程式に変換して求めればよい.

𝑑𝑦1

𝑑𝑥= 𝑦2,

𝑑𝑦2

𝑑𝑥= 𝑥𝑦2 + 𝑦1

コンパイルするときは次のように行う.

gcc -0 sp6_4 sp6_4.c -lm

例題 6.4 のプログラム(演習問題 7,9,10 に関係する.)

#include<stdio.h>

/* RNGKUT:ルンゲ・クッタ法の計算をする副関数,FUN:方程式の右辺を計算する副関数 */

void RNGKUT(int, double, double *, double *, double *, double *, double *, double *, double);

double FUN(int, double, double *, int);

void main(void)

{

double y[2], k1[2], k2[2], k3[2], k4[2], ywork[2]; /* y, k1, k2, k3, k4, 作業領域 ywork */

/* double x, h;

int i, n;

scanf("%d %lf", &n, &h); /* キーボードからのデータ入力 */

scanf("%lf %lf %lf", &x, &y[0], &y[1]);

*/

int i;

double n=2, h=0.1; /* n: 階数, h: 刻み値 */

double x=0.0; /* x の初期値: x0 */

y[0]=1.0, y[1]=1.0; /* y[0]: y1 の初期値: y0, y[1]: y2=dy1/dx の初期値 */

printf("i x y1 y2¥n"); /* 変数名の画面表示 */

printf("0 %6.3f %6.3f %6.3f¥n", x, y[0], y[1]); /* 初期値の画面表示 */

for(i=1; i<=10; i++){ /* 10 回の計算を繰り返す */

RNGKUT(n, x, y, k1, k2, k3, k4, ywork, h); /* 変数名の画面表示 */

x+=h; /* xi+1=xi+h */

printf("%2d %6.3f %6.3f %6.3f¥n", i, x, y[0], y[1]); /*結果の表示 */

}

}

/* ルンゲ・クッタ法の計算をする副関数*/

void RNGKUT(int n, double x, double *py, double *pk1, double *pk2, double *pk3, double *pk4,

double *pywork, double h)

{

int i;

for(i=0; i<n; i++){ /* k1 の計算 */

*(pk1+i)=h*FUN(i, x, py, n);

}

for(i=0; i<n; i++){ /* y の変化量 y+k1/2 を配列 ywork に記憶する */

*(pywork+i)=*(py+i)+*(pk1+i)/2;

}

for(i=0; i<n; i++){ /* k2 の計算 */

*(pk2+i)=h*FUN(i, x+h/2, pywork, n);

}

for(i=0; i<n; i++){ /* y の変化量 y+k2/2 を配列 ywork に記憶する */

*(pywork+i)=*(py+i)+*(pk2+i)/2;

}

for(i=0; i<n; i++){ /* k3 の計算 */

*(pk3+i)=h*FUN(i, x+h/2, pywork, n);

}

for(i=0; i<n; i++){ /* y の変化量 y+k3 を配列 ywork に記憶する */

*(pywork+i)=*(py+i)+*(pk3+i);

}

for(i=0; i<n; i++){ /* k4 の計算*/

*(pk4+i)=h*FUN(i, x+h, pywork, n);

}

for(i=0; i<n; i++){ /* y1 の計算 */

*(py+i)+=(*(pk1+i)+2**(pk2+i)+2**(pk3+i)+*(pk4+i))/6;

}

return;

}

double FUN(int i, double x, double *py, int n) /* 方程式の右辺を計算する副関数 */

{

double f;

if((i+1)==1){ /* i=1 のとき,dy1/dx=y2 の計算 */

f=*(py+1);

return f;

} else { /* i=2 のとき,dy2/dx=xy2+y1 の計算*/

f=x**(py+1)+*(py+0);

return f;

}

}

6.5 演習問題について

1. define を使わず,副関数で計算するとよい.次のように define 文をコメントにする.

/* #define F(x, y) ((x*x)+y) */

次に main 文の前に副関数を挿入する.

double F(double x, double y) { return(x*x+y); }

お勧めしませんが,もし define 文を使うなら,次のように記述する.

#define F(x, y) (pow(x, 2)+y)

4.初期条件より 15 = C・exp( - 02/2)より C = 15.つまり y = 15exp( -12 / 2 ) = 0.9097959896.項目

1 も参照のこと.

5.,6.項目 4 を参照のこと.

7.一般解は y = sin(x)である.

付録

(計算 1)

教科書(9)式の計算

Lagrange の補間多項式は,次数 n = 3,教科書 P.148,(2)式より,次のようになる.

𝑃3(𝑥) = 𝑎0𝑥3 + 𝑎1𝑥

2 + 𝑎2𝑥1 + 𝑎3 (C1)式

=∑𝑓𝑖

3

𝑖=0

(𝑥 − 𝑥0)(𝑥 − 𝑥1)… (𝑥 − 𝑥𝑖−1)(𝑥 − 𝑥𝑖+1)… (𝑥 − 𝑥3)

(𝑥𝑖 − 𝑥0)(𝑥𝑖 − 𝑥1)… (𝑥𝑖 − 𝑥𝑖−1)(𝑥𝑖 − 𝑥𝑖+1)… (𝑥𝑖 − 𝑥3)

= 𝑓0(𝑥 − 𝑥1)(𝑥 − 𝑥2)(𝑥 − 𝑥3)

(𝑥0 − 𝑥1)(𝑥0 − 𝑥2)(𝑥0 − 𝑥3)+ 𝑓1

(𝑥 − 𝑥0)(𝑥 − 𝑥2)(𝑥 − 𝑥3)

(𝑥1 − 𝑥0)(𝑥1 − 𝑥2)(𝑥1 − 𝑥3)

+ 𝑓2(𝑥 − 𝑥0)(𝑥 − 𝑥1)(𝑥 − 𝑥3)

(𝑥2 − 𝑥0)(𝑥2 − 𝑥1)(𝑥2 − 𝑥3)+ 𝑓3

(𝑥 − 𝑥0)(𝑥 − 𝑥1)(𝑥 − 𝑥2)

(𝑥3 − 𝑥0)(𝑥3 − 𝑥1)(𝑥3 − 𝑥2)

(C2)式

(C2)式の右辺の各項は教科書(9)式について次のように対応している.

第 1 項:𝑝1 ≡ 𝑓𝑖−3(𝑥+ℎ)𝑥(𝑥−ℎ)

(−ℎ)(−2ℎ)(−3ℎ)

第 2 項:𝑝2 ≡ 𝑓𝑖−2(𝑥+2ℎ)𝑥(𝑥−ℎ)

ℎ(−ℎ)(−2ℎ)

第 3 項:𝑝3 ≡ 𝑓𝑖−1(𝑥+2ℎ)(𝑥+ℎ)(𝑥−ℎ)

2ℎ∙ℎ(−ℎ)

第 4 項:𝑝4 ≡ 𝑓𝑖(𝑥+2ℎ)(𝑥+ℎ)𝑥

3ℎ∙2ℎ∙ℎ

(変換のルール)

R1: xの添え字が小さいものから大きいものを引くとき hは負となり,その逆は正となる.

上の式において黄色でマークされているところは,教科書と記述が異なるが,分母の符号

は変わらないので,結果(計算値)は同じである.

変換の具体的な例を示す.

|←h→|←h→|←h→|←h→|

xi-3 xi-2 xi-1 xi xi+1 :教科書(9)式の記号

x0 x1 x2 x3 x4 :(C2)式の記号

原点

原点は x2なので,例えば(C2)式の記号と教科書(9)式の対応は次のようになる.

(𝑥 − 𝑥1) → (𝑥2 − 𝑥1) ∶ 𝑥 + ℎ

(𝑥 − 𝑥2) → (𝑥2 − 𝑥2) ∶ 𝑥

(𝑥 − 𝑥3) → (𝑥2 − 𝑥3) ∶ 𝑥 − ℎ

となっている.

教科書(10)式の計算

まず (10)式は次のように展開できる.

𝑦𝑖+1(𝑃)

= 𝑦𝑖−3 +4

3ℎ(2𝑓𝑖−2 − 𝑓𝑖−1 + 2𝑓𝑖) = 𝑦𝑖−3 +

8ℎ ∙ 𝑓𝑖−23

−4ℎ ∙ 𝑓𝑖−1

3+8ℎ ∙ 𝑓𝑖3

(イ) (ロ) (ハ)

次に p1から p4の積分を求めると

𝑝1 ≡ 𝑓𝑖−3(𝑥 + ℎ)𝑥(𝑥 − ℎ)

(−ℎ)(−2ℎ)(−3ℎ)= 𝑓𝑖−3

𝑥3 − ℎ2𝑥

−6ℎ3

∫ 𝑝1

2ℎ

−2ℎ

𝑑𝑥 =𝑓𝑖−3−6ℎ3

{∫ 𝑥32ℎ

−2ℎ

𝑑𝑥 − ℎ2∫ 𝑥2ℎ

−2ℎ

𝑑𝑥} = 0

同様に積分の計算結果を示すと

∫ 𝑝2

2ℎ

−2ℎ

𝑑𝑥 =8ℎ ∙ 𝑓𝑖−2

3

∫ 𝑝3

2ℎ

−2ℎ

𝑑𝑥 = −4ℎ ∙ 𝑓𝑖−1

3

∫ 𝑝4

2ℎ

−2ℎ

𝑑𝑥 =8ℎ ∙ 𝑓𝑖3

上の積分の総和は(10)式の(イ)(ロ)および(ハ)に等しい.