» 詳しい使用方法や、エラーで展開できない際の対応方法などはこちら
ローレンツアトラクタ(GUI版)
ローレンツ方程式を4次精度ルンゲ=クッタ(RK4)法で数値的に解き、その解曲線を描くVCSSLプログラムです。描かれる解曲線は、 「 ローレンツアトラクタ 」として非常に有名です。
なお、このプログラムでは、GUIの設定パネルでパラメータを変更でき、操作に応じてリアルタイムで再計算してグラフに描画するような仕組みになっています。 それに対して、計算結果をファイルに出力して、それをグラフでプロットする、より単純なバージョンも下記にて公開しています。コード内容はそちらのほうが単純で、C言語への書き換えなども容易です。
上のファイル出力版では、計算結果を一旦ファイルに書き出すため、VCSSL付属のグラフ機能だけではなく、一般のグラフソフトで解析する事もできます。
対して今回のGUI版では、リアルタイム計算を行うのでファイルは出力しませんが、GUI設定パネルのスライダーでパラメータを操作でき、それに応じて結果のグラフも自動的に再描画されるため、手軽に楽しむ事ができます。
スポンサーリンク
使用方法
ダウンロードと展開(解凍)
まず、PC(スマホは未対応)で上の画面の「 ダウンロード 」ボタンを押してください。 するとZIP形式で圧縮されたファイルがダウンロードされます。
その後、ZIPファイルを右クリックして「すべて展開」や「ここに展開」などで展開(解凍)してください。 展開が成功すると、ZIPファイルと同じ名前のフォルダができ、その中にZIPファイルの中身が入っています。
» 展開がエラーで止まってしまう場合や、ファイル名が文字化けしてしまう場合は…
プログラムの起動
Windows をご使用の場合
上記でZIPファイルを展開したフォルダ内にある、以下のバッチファイルをダブルクリック実行してください:
もしプログラムを書き変えながら使いたい場合は、代わりに「 VCSSL_Editor__プログラム編集はこちら.bat 」を実行してください。
正常に起動できると、初回のみ、Java実行環境を入手するか等を尋ねられるので、適時答えて済ませると、プログラムが起動します。 2回目以降はすぐに起動します。
Linux 等をご使用の場合
ZIPファイルを展開したフォルダ内へコマンドライン端末で cd して、以下の通り入力して実行してください:
(プログラムの内容を書き変えながら使いたい場合は、代わりに VCSSL_Editor.jar を実行)
» javaコマンドが使用できない等のエラーが表示される場合は…
操作方法
プログラムを実行すると、3Dグラフのウィンドウと、スライダーを備えたウィンドウが表示されます。 3Dグラフのウィンドウには、ローレンツ方程式の積分解曲線( ローレンツアトラクタ )が描画されます。 スライダーを備えたウィンドウでは、ローレンツ方程式のパラメータ値を調整できます。スライダーを操作すると、パラメータ値が変化し、それに応じて3Dグラフがリアルタイムで再描画されます。
アニメーション描画
「ANIMATION」ボタンをクリックすると、ローレンツアトラクタを始点から徐々に描いていくアニメーションを見る事ができます。
終了方法
終了の際は、スライダーを備えたウィンドウの右端にある、「 END 」ボタンを押してください。
題材解説
ローレンツ方程式とローレンツアトラクタ、初期値鋭敏性
ローレンツ方程式は、以下の形で表される、1階の常微分方程式です。
\[ \frac{dx}{dt} = -sx + sy \] \[ \frac{dy}{dt} = -xz + ry - y \] \[ \frac{dz}{dt} = xy - bz \]
この方程式は、極めてシンプルかつ決定論的な方程式であるにもかかわらず、それが描く曲線が、 かなりランダム的な振る舞いを帯びる事が特徴です。
アニメーション描画で見てみるとよくわかりますが、始点から伸びていった起動は、 右へ左へと何度もロールを描きながら、蝶の羽のような特徴的な曲線 ― ローレンツアトラクタ ― を描いていきます。
そして、この奇妙なロールのパターン ― 右、左、右、右、左、右、…というような不規則的なパターン ― は、ちょうど天気予報のように、未来の予測が極めて困難な、ランダム的なものとなります。
例えば、今の瞬間から、次に右へロールするか、左へロールするかは、比較的簡単に予測できます。 しかし数回後にどちらへロールするかは、それなりに高精度に計算しないと、予測がはずれてしまいます。 さらに数十回後を予測しようとすると、一気に必要な精度のハードルが高くなります。 つまり、より遠い未来を予測しようとするほど、飛躍的に高い計算精度が必要となるのです。
この性質の発見により、今日「カオス」と呼ばれている、非常に有名な物理学の分野が開拓されました。 この性質は、現在のカオスの分野では「初期値鋭敏性 」と呼ばれており、 カオスモデルの持つ重要な性質の一つに位置づけられています。
天気予報の難しさ
気象学者であったエドワード・ローレンツ氏は、流体のシミュレーションモデルとしてこの方程式を使用し、 そしてこのような極めて単純な数理モデルでも、現実的には予測不能な振る舞いが生じるという事を見出しました。
この事は今日、いわゆる 「 バタフライ効果 ( 地球の裏側で蝶が羽ばたいただけで、未来の気象が全く変わってしまうという例え話 ) 」 などとして、一般にもよく知られています。
この事から、天気予報の難しさを説明する際に、 今日でも頻繁にローレンツアトラクタが例に挙げられます。 数あるカオスモデルの中でも、恐らく最も有名なものの一つでしょう。
コード解説
プログラム内容
はじめに、少し長いですが、プログラム全体を掲載します。以下の通りです。
処理フロー
このプログラムでは、スライダーでパラメータ値が変更されるたびに再度処理を行う必要があるため、メインループ型の処理フローを用いています。
メインループとは、プログラム開始時から終了時まで繰り返し続けるループの事で、イベント処理のために広く一般的に使用されています。 なお、メインループは、イベントループや基本ループなど、用途や分野によって別の名称で呼ばれる事もあります。
メインループ内で毎回、再計算が必要かどうかを確認し、必要なら再計算と再描画を行っています。
void main( string args[] ){
// 初期化など
// メインループ、プログラム終了まで繰り返し
while( MAIN_ROOP ){
// 再計算が必要かどうかを判定
if( PLOT_REQUEST ){
//---------------
//ここで計算
//---------------
PLOT_REQUEST = false;
}else{
sleep( 100 );
}
}
exit();
}
上のPLOT_REQUESTはbool型変数で、再計算が必要な場合にtrueになっています。この変数はスライダーのイベントハンドラでtrueにし、メインループで再計算が完了したらfalseにします。
微分方程式を数値的に解く
このVCSSLプログラムでは、ローレンツ方程式を4次収束ルンゲ=クッタ(RK4)法で数値的に解いています。RK4法は、この種の解法の方法の中でも高精度な部類に属します。 RK4法の計算を行っているのは、プログラム中の以下の部分です。
x = 0.1;
y = 0.1;
z = 0.1;
for( int i=0; i<N; i++ ){
// Integration ( RK4 )
k1_x = f_x( x, y, z );
k1_y = f_y( x, y, z );
k1_z = f_z( x, y, z );
k2_x = f_x( x+k1_x*DT*0.5, y+k1_y*DT*0.5, z+k1_z*DT*0.5 );
k2_y = f_y( x+k1_x*DT*0.5, y+k1_y*DT*0.5, z+k1_z*DT*0.5 );
k2_z = f_z( x+k1_x*DT*0.5, y+k1_y*DT*0.5, z+k1_z*DT*0.5 );
k3_x = f_x( x+k2_x*DT*0.5, y+k2_y*DT*0.5, z+k2_z*DT*0.5 );
k3_y = f_y( x+k2_x*DT*0.5, y+k2_y*DT*0.5, z+k2_z*DT*0.5 );
k3_z = f_z( x+k2_x*DT*0.5, y+k2_y*DT*0.5, z+k2_z*DT*0.5 );
k4_x = f_x( x+k3_x*DT, y+k3_y*DT, z+k3_z*DT );
k4_y = f_y( x+k3_x*DT, y+k3_y*DT, z+k3_z*DT );
k4_z = f_z( x+k3_x*DT, y+k3_y*DT, z+k3_z*DT );
x += ( k1_x + 2.0*k2_x + 2.0*k3_x + k4_x )*DT/6.0;
y += ( k1_y + 2.0*k2_y + 2.0*k3_y + k4_y )*DT/6.0;
z += ( k1_z + 2.0*k2_z + 2.0*k3_z + k4_z )*DT/6.0;
ORBIT_X[ i ] = x;
ORBIT_Y[ i ] = y;
ORBIT_Z[ i ] = z;
}
配列ORBIT_X、 ORBIT_Y、 ORBUT_Z は、軌道のX、Y、Z座標を格納する配列です。forの1ループごとに、軌道座標の新たな1点をRK4法で計算し、それを配列に格納しています。
この部分はローレンツ方程式に限らず、一般的な3次元の1階常微分方程式をRK4法て数値積分する場合に使用できます。
使用されている関数f_x、f_y、f_zは、X、Y、Z値を引数にして、その点における微分係数dx/dt、dy/dt、dz/dtを返します。
ここではローレンツ方程式を積分しているので、以下のように定義しています。
// dx / dt
macro f_x( float X, float Y, float Z ){
return -PARAM_S*X + PARAM_S*Y;
}
// dy / dt
macro f_y( float X, float Y, float Z ){
return -X*Z + PARAM_R*X - Y;
}
// dz / dt
macro f_z( float X, float Y, float Z ){
return X*Y - PARAM_B*Z;
}
この関数は非常に処理内容が少ないので、コール時のオーバーヘッドを回避するため、マクロ関数として定義しています。
グラフ描画
全ての座標点の計算が終了し、軌道の全座標が求まった後は、それを格納する配列ORBIT_X、 ORBIT_Y、 ORBUT_Zを3Dグラフソフトに転送し、描画を行っています。
// 座標値配列を3Dグラフソフトに転送し、再描画
setGraph3DData( graph, ORBIT_X, ORBIT_Y, ORBIT_Z );
なお、3Dグラフはこのように描画命令を送る前に、プログラム先頭であらかじめ生成処理とオプション設定を行っています。
// 3Dグラフソフトを生成
int graph = new Graph3D( 0, 0, 600, 500, "Graph Window" );
// 線描画オプションをONに設定
setGraph3DOption( graph, "WITH_LINES", true );
スライダーなどのGUI設計
以下の関数 createSettingWindow では、スライダーなどのGUIを生成し、ウィンドウ上に配置しています。この関数はプログラム開始後の初期化部分でコールしています。
int WINDOW;
int PARAM_S_SLIDER, PARAM_B_SLIDER, PARAM_R_SLIDER;
int PARAM_S_LABEL, PARAM_B_LABEL, PARAM_R_LABEL;
int END_BUTTON;
void createSettingWindow(){
WINDOW = newWindow( 0, 500, 600, 200, "Setting Window" );
int explainLabel1 = newTextLabel( 10, 10, 400, 15, "dx / dt = - S * x + S * y" );
mountComponent( explainLabel1, WINDOW );
int explainLabel2 = newTextLabel( 10, 25, 400, 15, "dy / dt = - x * z + R * x - y" );
mountComponent( explainLabel2, WINDOW );
int explainLabel3 = newTextLabel( 10, 40, 400, 15, "dz / dt = x * y - B * z" );
mountComponent( explainLabel3, WINDOW );
PARAM_S_LABEL = newTextLabel( 10, 60, 400, 15, "S = "+PARAM_S );
mountComponent( PARAM_S_LABEL, WINDOW );
PARAM_S_SLIDER = newHorizontalSlider( 10, 75, 400, 15, PARAM_S/50.0 );
mountComponent( PARAM_S_SLIDER, WINDOW );
PARAM_B_LABEL = newTextLabel( 10, 90, 400, 15, "B = "+PARAM_B );
mountComponent( PARAM_B_LABEL, WINDOW );
PARAM_B_SLIDER = newHorizontalSlider( 10, 105, 400, 15, PARAM_B/50.0 );
mountComponent( PARAM_B_SLIDER, WINDOW );
PARAM_R_LABEL = newTextLabel( 10, 120, 400, 15, "R ="+PARAM_R );
mountComponent( PARAM_R_LABEL, WINDOW );
PARAM_R_SLIDER = newHorizontalSlider( 10, 135, 400, 15, PARAM_R/500.0 );
mountComponent( PARAM_R_SLIDER, WINDOW );
END_BUTTON = newButton( 420, 10, 150, 140, "END" );
setComponentFontSize( END_BUTTON, 30 );
mountComponent( END_BUTTON, WINDOW );
paintComponent( WINDOW );
}
イベントハンドラ
スライダーが操作された際は、onSliderMoveイベントハンドラで受け取り、ローレンツ方程式のパラメータ値を再設定しています。そしてPLOT_REQUESTをtrueにし、メインループで再計算と再描画が行われるようにしています。
void onSliderMove( int id, float value ){
if( id==PARAM_S_SLIDER ){
PARAM_S = value * 50.0;
setComponentText( PARAM_S_LABEL, "S = "+PARAM_S );
paintComponent( PARAM_S_LABEL );
}else if( id==PARAM_B_SLIDER ){
PARAM_B = value * 50.0;
setComponentText( PARAM_B_LABEL, "B = "+PARAM_B );
paintComponent( PARAM_B_LABEL );
}else if( id==PARAM_R_SLIDER ){
PARAM_R = getComponentValue( PARAM_R_SLIDER ) * 500.0;
setComponentText( PARAM_R_LABEL, "R = "+PARAM_R );
paintComponent( PARAM_R_LABEL );
}
paintComponent( WINDOW );
PLOT_REQUEST = true;
}
また、ENDボタンがクリックされたり、ウィンドウが閉じられた際には、それぞれのイベントハンドラでMAIN_ROOPをfalseにし、メインループを脱出させる事で、プログラムが終了するようにしています。
void onButtonClick( int ID ){
MAIN_ROOP = false;
}
void onWindowClose( int ID ){
MAIN_ROOP = false;
}
このようにする事で、全ての処理が終わったタイミングで exit 命令がコールされるため、プログラムの終了が必ず正常に行われます。
これに対し、イベントハンドラの中で直接 exit 命令をコールしてしまうと、メインループが処理中でも強制終了されてしまうため、終了時にエラーが出てしまう場合があります。
ライセンス
このVCSSL/Vnanoコード( 拡張子が「.vcssl」や「.vnano」のファイル )は実質的な著作権フリー(パブリックドメイン) である CC0 の状態で公開しています※。 記事中にC言語/C++/Java言語などでのサンプルコードが掲載されいてる場合は、それらについても同様です。 そのままでのご利用はもちろん、改造や流用などもご自由に行ってください。
※ ただし、このコードの配布フォルダ内には、ダウンロード後すぐに実行できるように、 VCSSLの実行環境も同梱されており、そのライセンス文書は「 License 」フォルダ内に同梱されています (要約すると、商用・非商用問わず自由に使用できますが、使用の結果に対して開発元は一切の責任を負いません、といった具合の内容です)。 配布フォルダ内の各構成物の一覧やライセンスについては「 ReadMe_使用方法_必ずお読みください.txt 」をご参照ください。
※ Vnano の実行環境については、別途スクリプトエンジンのソースコードも一般公開しており、 何らかのソフトウェア内に組み込んでご利用いただく事も可能です。詳細はこちらをご参照ください。
この記事中の商標などについて
- OracleとJavaは、Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。
- Windows は、米国 Microsoft Corporation の米国およびその他の国における登録商標です。この記事は独立著作物であり、Microsoft Corporation と関連のある、もしくはスポンサーを受けるものではありません。
- Linux は、Linus Torvalds 氏の米国およびその他の国における商標または登録商標です。
- その他、文中に使用されている商標は、その商標を保持する各社の各国における商標または登録商標です。
Vnano版 | ローレンツ方程式を数値的に解くプログラム |
|
|
ローレンツ方程式を4次ルンゲ=クッタ法によって解き、グラフ描画用のデータを出力するプログラムです。 |
波の干渉(面上の円形波)のアニメーション表示 |
|
|
面上の円形波が干渉する様子を、パラメータを操作しながらアニメーションで見られるプログラムです。 |
円形波のアニメーション表示 |
|
|
振幅・波長・周期をスライダ―で操作しながら、円形波のグラフをアニメーションで見られるプログラムです。 |
波の干渉(線上の正弦波)のアニメーション表示 |
|
|
線上(1次元の)の正弦波が干渉する様子を、パラメータを操作しながらアニメーションで見られるプログラムです。 |
正弦波のアニメーション表示 |
|
|
振幅・波長・周期をスライダ―で操作しながら、正弦波のグラフをアニメーションで見られるプログラムです。 |
凹レンズを通過する波のシミュレーション |
|
|
凹レンズ形状の高密度媒質を通過する、波のシミュレーションです。 |
凸レンズを通過する波のシミュレーション |
|
|
凸レンズ形状の高密度媒質を通過する、波のシミュレーションです。 |
乱雑な密度分布における波のシミュレーション |
|
|
密度分布が乱雑な媒質中における、波の伝播のシミュレーションです。 |
ローレンツアトラクタ(ファイル出力版) |
|
|
4次精度ルンゲ=クッタ法により、ローレンツアトラクタを求めるプログラムです。 |
波の屈折のシミュレーション |
|
|
密度の異なる領域を、波が屈折しながら通過するシミュレーションです。 |
力学アルゴリズムによる波のシミュレーション(面上の波) |
|
|
媒質をバネと格子点で近似し、力学的なアルゴリズムで動かす事による、波のシミュレーションです。 |
手動で波を発生させるシミュレーション |
|
|
スライダーをマウスで動かす事により、波を発生させるシミュレーションです。 |
力学アルゴリズムによる波のシミュレーション(線上の波) |
|
|
媒質をバネと格子点で近似し、力学的なアルゴリズムで動かす事による、波のシミュレーションです。 |
二重振り子のシミュレーション |
|
|
ラグランジュ方程式を用いた、二重振り子のシミュレーションです。 |
ローレンツアトラクタ(GUI版) |
|
|
4次精度ルンゲ=クッタ法により、ローレンツアトラクタを求めるプログラムです。 |