久しぶりに Runge-Kutta法でローレンツ方程式を解いたよ

Runge-Kutta法っていうのは、常微分方程式数値計算するための、一解法です。
ルンゲ=クッタ法 - Wikipedia


ローレンツ方程式というのは、カオス理論にとって
極めて重要な方程式であります。
ローレンツ方程式 - Wikipedia


で、これを解いて何をしようとしているか、というと
プロフィール画像をつくろう!というわけです。
kei-os2007という idは、学生時代の
chaos理論との素晴らしい出会いから、きています。
自分の人生は、どんなアトラクタにひきこまれていくのだろう、
というワクワクする気持ちも入っています。


というわけで、解きますー。

Cソース


Cで書いてみます。
Wikipedia には y' = f(t,y) ってなってますが
ここでは、より一般的(と思われる) f(x,y) を使って書いてます。
きざみ幅の h は step にしています。
lorenz.c ということで。

$ cat lorenz.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define OLD (0)
#define NEW (1)

// 3D data type
typedef struct {
    double x;
    double y;
    double z;
} Point;

// parameter
double Pr = 10.0;
double b = 8.0/3.0;
double r = 28.0;

static double dxdt(double x, double y, double z)
{
    return Pr*(y - x);
}

static double dydt(double x, double y, double z)
{
    return (-x*z + r*x - y);
}

static double dzdt(double x, double y, double z)
{
    return (x*y - b*z);
}

static double rk4_3d(double (*proc)(double x, double y, double z), 
                        Point *p, double step)
{
    double k0, k1, k2, k3;

    k0 = step*proc(p->x, p->y, p->z);
    k1 = step*proc(p->x*k0/2, p->y+k0/2, p->z+k0/2);
    k2 = step*proc(p->x+k1/2, p->y+k1/2, p->z+k1/2);
    k3 = step*proc(p->x+k2, p->y+k2, p->z+k2);

    return ((k0 + 2*k1 + 2*k2 + k3)/6);
}


int main(int argc, char **argv)
{
    double step = 0.001;
    int i, iter = 100000;
    Point   points[2];

    FILE *fp;
    if (NULL == (fp = fopen("./lorenz.dat", "w"))) {
        fprintf(stderr, "cannot open output file\n");
        exit(-1);
    }

    // initial condition
    points[OLD].x = 0.1;
    points[OLD].y = 0.1;
    points[OLD].z = 0.1;

    for (i = 0; i < iter; i++) {
        points[NEW].x = points[OLD].x + rk4_3d(dxdt, &points[OLD], step);
        points[NEW].y = points[OLD].y + rk4_3d(dydt, &points[OLD], step);
        points[NEW].z = points[OLD].z + rk4_3d(dzdt, &points[OLD], step);

        fprintf(fp, "%lf\t%lf\t%lf\n", points[NEW].x, points[NEW].y, points[NEW].z);

        // for next calc.
        memcpy((void *)&points[OLD], (void *)&points[NEW], sizeof(Point));
    }
    fclose(fp);

    return 0;
}

コンパイル&実行

UNIX系ということにして、gccを使うことにします。

$ gcc lorenz.c
$ ./a.out

lorenz.dat に X(t), Y(t), Z(t) のデータが出力されます。

gnuplotPNG画像を出力

gunplotを使うとします。

$ gnuplot

で起動したら、以下のコマンドを入力していきます。
terminalは x11で動いてるものとします。

set parametric
set terminal png medium size 640,480
set output "lorenz.png"
set pointsize 0.01
splot "lorenz.dat" with points
set terminal x11


これで、lorenz.pngができます。


追記 バタフライ効果


バタフライ効果 - Wikipedia
より。

この表現はエドワード・ローレンツが1972年にアメリカ科学振興協会でおこなった講演のタイトル『予測可能性-ブラジルでの蝶の羽ばたきはテキサスでトルネードを引き起こすか』に由来する。


ローレンツ方程式で得られるトラジェクトリー(軌跡)が
蝶が羽を広げた様子に似ていることからきている、という説があったはず。
まさに、ここで得られる図です。
kei-os2007が、このブログ空間でバタバタと羽ばたくことで
世界のどこかに大嵐を起こせるはず、と信じています。