外部ファイルと異なり、'-' は読み直しが出来ないので、 データは一つ一つ出力する (対応する 'e' もグラフの個数分必要になる) というのがミソです。
| 3sukumi2.c |
/*
* 3sukumi2.c --- 3すくみ方程式の数値シミュレーション
* http://phi.med.gunma-u.ac.jp/oldlec/ecology_p14.html の 3sukumi.R を
* C 言語に焼き直し
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define N (200)
void runif(double *v, int n, double a, double b)
{
int i;
for (i = 0; i < n; i++) v[i] = a + (b - a) * drand48();
}
int main(void)
{
double N1[N+1],N2[N+1],N3[N],yr[N];
double cc[3] = {0.01, 0.01, 0.01};
double a[] = {0.2, 0.4, 0.1, 0.1, 0.2, 0.4, 0.4, 0.1, 0.2};
double r[] = {0.5, 0.6, 0.7};
double rand1[N], rand2[N], rand3[N];
int i;
FILE *gp;
double hw;
N1[0] = 1; N2[0] = 1; N3[0] = 1;
/* yr = {1,2,...,N} */
for (i = 0; i < N; i++)
yr[i] = i + 1;
/* drand48() の seed設定 --- 適当 */
srand48(1L);
/* [-0.1,0.1) の一様乱数 N 個 */
hw = 0.1;
runif(rand1, N, -hw, hw); runif(rand2, N, -hw, hw); runif(rand3, N, -hw, hw);
/* 微分方程式を Euler 法で解く */
for (i = 0; i < N-1; i++) {
N1[i+1] = cc[0] + exp(r[0]+rand1[i]-a[0]*N1[i]-a[1]*N2[i]-a[2]*N3[i])*N1[i];
N2[i+1] = cc[1] + exp(r[1]+rand2[i]-a[3]*N1[i]-a[4]*N2[i]-a[5]*N3[i])*N2[i];
N3[i+1] = cc[2] + exp(r[2]+rand3[i]-a[6]*N1[i]-a[7]*N2[i]-a[8]*N3[i])*N3[i];
}
/* gnuplot を呼び出して描画 */
gp = popen("gnuplot -persist", "w");
fprintf(gp, "plot '-' with lines, '-' with lines, '-' with lines\n");
for (i = 0; i < N; i++) fprintf(gp, "%f %f\n", yr[i], N1[i]); fprintf(gp, "e\n");
for (i = 0; i < N; i++) fprintf(gp, "%f %f\n", yr[i], N2[i]); fprintf(gp, "e\n");
for (i = 0; i < N; i++) fprintf(gp, "%f %f\n", yr[i], N3[i]); fprintf(gp, "e\n");
fprintf(gp, "quit\n");
pclose(gp);
return 0;
}
|
(2021/4/28追記) 上の 3sukumi.c は元ネタが自分で書いたものでなくて、 どうも良く分からないところがあるので、別のサンプル・プログラムを用意した。
| lotka-volterra-rk2.c |
/*
* lotka-volterra-rk2.c (Runge-Kutta method for Lotka-Volterra model)
* ターミナルで
* cc lotka-volterra-rk2.c
* ./a.out > lotka-volterra-rk2.data
* 500
* gnuplot
* gnuplot> に対して
* plot "lotka-volterra-rk2.data" using 2:3 with l
*/
#include <stdio.h>
double a,b,c,d;
int main(void)
{
int i, N;
double t, x, y, dt, k1x, k1y, k2x, k2y, k3x, k3y, k4x, k4y;
double fx(double, double, double), fy(double, double, double), x0, y0;
double Tmax;
// パラメーター決定
a = 2; b = 1; c = 3; d = 1;
// 最終時刻
Tmax = 10.0;
// 時間刻み
printf("# N: "); scanf("%d", &N);
dt = Tmax / N;
// 初期値
x0 = 3.0;
for (y0 = 0.5; y0 <= 5.0; y0 += 0.5) {
// 初期値
t = 0.0;
x = x0;
y = y0;
printf("# t x y\n");
printf("%f %f %f\n", t, x, y);
// Euler 法
for (i = 0; i < N; i++) {
k1x = dt * fx(x, y, t);
k1y = dt * fy(x, y, t);
k2x = dt * fx(x + k1x / 2, y + k1y / 2, t + dt / 2);
k2y = dt * fy(x + k1x / 2, y + k1y / 2, t + dt / 2);
k3x = dt * fx(x + k2x / 2, y + k2y / 2, t + dt / 2);
k3y = dt * fy(x + k2x / 2, y + k2y / 2, t + dt / 2);
k4x = dt * fx(x + k3x, y + k3y, t + dt);
k4y = dt * fy(x + k3x, y + k3y, t + dt);
x += (k1x + 2 * k2x + 2 * k3x + k4x) / 6;
y += (k1y + 2 * k2y + 2 * k3y + k4y) / 6;
t = (i + 1) * dt;
printf("%f %f %f\n", t, x, y);
}
printf("e\n\n"); // 線を切る
}
return 0;
}
double fx(double x, double y, double t)
{
return a * x - b * x * y;
}
double fy(double x, double y, double t)
{
return - c * y + d * x * y;
}
|
| コンパイル、実行、可視化 |
% cc lotka-volterra-rk2.c % ./a.out > lotka-volterra-rk2.data 500 % gnuplot gnuplot> plot "lotka-volterra-rk2.data" using 2:3 with l |