課題1

速度に関する微分方程式として考えてみて下さい.x=vと書くべきだったかもしれませんが,例に合わ
せたのと,微分方程式のアルゴリズム上は単なる1階の微分方程式と考えていただければ充分なの
です.

関数は

double fnc(double t, double x){
 double a=0.1;
 double b=0.02;
 double g=9.807;
 return -1.0 * b * x * x - a * x + g;
}

tは出てきません.x'(t)=sin(5.0 * t)のような場合はtを使いますので,このような関数に対応するための
形式を残しておいたのがxとtを引数にした理由です.

ルンゲクッタ法

// 課題1
#include <stdio.h>

const double a = 0.1;
const double b = 0.02;
const double g = 9.807;

// 1階微分方程式
double fnc(double t,double x)
{
 return -b*x*x - a*x +g;
}

// ルンゲクッタ法
double rkm(double(* fnc)(double,double), double t, double x, double h){
double ck0, ck1, ck2, ck3;

 ck0=(*fnc)(t, x)*h;
 ck1=(*fnc)(t+h*0.5, x+ck0*0.5)*h;
 ck2=(*fnc)(t+h*0.5, x+ck1*0.5)*h;
 ck3=(*fnc)(t+h, x+ck2)*h;

 return (x+(ck0+(ck1+ck2)*2.0+ck3)/6.0);
}

int main()
{
 int i;
 double t0 = 0.0; // 始点
 double tn = 5.0; // 終点
 double h = 0.01; // 刻み
 int n = (int)((tn - t0) / h); // 刻み数
 double t;
 double x=0.0;

 for(i=0;i<n;i++){
  t = t0 + i*h;
  x = rkm(fnc,t,x,h);
  if(i%10==0) printf("%lf\n",x);
 }
 return;
}

ルンゲクッタ・ジル法

// 課題1
#include <stdio.h>

const double a = 0.1;
const double b = 0.02;
const double g = 9.807;

// 1階微分方程式
double fnc(double t,double x)
{
 return -b*x*x - a*x +g;
}

// ルンゲクッタ・ジル法
double rkgm(double (*fnc)(double, double), double t, double x, double h, double *q){
 static double p1=0.2928932188134524, p2=1.70710678118547;
 double ck0, ck1, ck2, ck3;
 double q1, q2, q3;
 double r1, r2, r3, r4;
 double tt, x1, x2, x3;

 ck0 = (*fnc)(t,x) * h;
 r1 = ck0 * 0.5 - *q;
 x1 = x + r1;
 q1 = *q + r1 * 3.0 - ck0 * 0.5;
 tt = t + h * 0.5;
 ck1 = (*fnc)(tt,x1) * h;
 r2 = (ck1-q1) * p1;
 x2 = x1 + r2;
 q2 = q1 + r2 *3.0 - ck1 * p1;
 ck2 = (*fnc)(tt,x2) * h;
 r3 = (ck2-q2) * p2;
 x3 = x2 + r3; // x2=x2+r3になっていたのを修正
 q3 = q2 + r3 * 3.0 - ck2 * p2; // q2=q2+r3*3.0-ck2*p2になっていたのを修正
 tt = t + h;
 ck3 = (*fnc)(tt,x3) * h;
 r4 = (ck3-q3-q3) / 6.0;
 *q = q3 + r4 * 3.0 - ck3 * 0.5;

 return (x3+r4);

}


int main()
{
 int i;
 double t0 = 0.0; // 始点
 double tn = 5.0; // 終点
 double h = 0.01; // 刻み
 int n = (int)((tn - t0) / h); // 刻み数
 double t;
 double x=0.0;
 double q = 0;

for(i=0;i<n;i++){
 t = t0 + i*h;
 x = rkgm(fnc,t,x,h,&q);
 if(i%10==0) printf("%lf\n",x);
 }
 return;
}

なお,授業中で省略した,ミルンの方法とアダムスの方法は,授業の始めで説明します.

課題2

細部は異なりますが基本の流れは同様です.

// 課題2
#include <stdio.h>

const double cm = 0.02;
const double km = 0.1;
const double um = 1;

// 1階微分方程式
double f(double t,double x,double z)
{
 return z;
}

// 2階微分方程式
double g(double t,double x,double z)
{
 return -cm*x - km*x + um;
}

// ルンゲクッタ法
double rkm_2(double(* f)(double,double,double),double(* g)(double,double,double), double t, double x, double h, double z){
double k, k0, k1, k2, k3, l, l0, l1, l2, l3;

 k0 = h*(*f)(t, x, z);
 l0 = h*(*g)(t, x, z);
 k1 = h*(*f)(t + h/2, x + k0/2, z + l0/2);
 l1 = h*(*g)(t + h/2, x + k0/2, z + l0/2);
 k2 = h*(*f)(t + h/2, x + k1/2, z + l1/2);
 l2 = h*(*g)(t + h/2, x + k1/2, z + l1/2);
 k3 = h*(*f)(t + h, x + k2, z + l2);
 l3 = h*(*g)(t + h, x + k2, z + l2);

 k = (k0 + 2*k1 + 2*k2 + k3)/6.0;
 l = (l0 + 2*l1 + 2*l2 + l3)/6.0;

 x = x + k;
 z = z + l;

 return x;
}

int main()
{
 int i;
 double t0 = 0.0; // 始点
 double tn = 5.0; // 終点
 double h = 0.01; // 刻み
 int n = (int)((tn - t0) / h); // 刻み数
 double t;
 double z = 1.0;
 double x = 0.05;

 for(i=0;i<n;i++){
 t = t0 + i*h;
 x = rkm_2(f,g,t,x,h,z);
 if(i%10==0) printf("%lf\n",x);
 }
 return;
}