ブラウン運動の可視化プログラムをC++とHTML5でそれぞれ作ってみた
実行結果
とりあえず実行結果を載せます。
C++
まずは二次元のブラウン運動から。要素数は100で、10000ステップまで50ステップごとに表示しています。周期境界条件を採用しており、端まで行った点は反対の端から出てきます。
gifアニメとYouTubeでどうぞ。最初は中心に集まっていた点が、ステップが進むごとに領域全体に広がっていくのが確認できます。
http://cdn-ak.f.st-hatena.com/images/fotolife/t/takahirox/20110821/20110821171748.gif
続いて三次元。角度が悪くて、z方向への散らばりがわかりづらいかも。
http://cdn-ak.f.st-hatena.com/images/fotolife/t/takahirox/20110821/20110821171700.gif
HTML5
http://gachapin.jp/brown2d.html
canvasに対応したブラウザでご覧ください。環境によっては動作が重いかもしれません。
要素数の変更やx方向へのバイアスがかけられます。
ブラウン運動とは
後で書く
今回は結晶中の電子(電子が粒子であるとしています)の動きを想定しています。なのでHTML5版では「x方向にバイアス=X方向に電界を与える」ことができるようにし、電子が流れる様(=電流が流れる様)を再現してみました。
C++での実装
最初に二次元の実装をしました。
「C++の正しい書き方」をあまり知らないので、変なところがあればご指摘ください。
#include <iostream> #include <fstream> #include <cstdlib> #include <ctime> using namespace std; #define CONSTRAINT 100 class Electron { protected: int x ; int y ; virtual void checkConstraint( ) ; virtual unsigned int getRandom( ) { return rand( ) % 4 ; } public: Electron( ) { x = 0; y = 0 ; } virtual void move( ) ; virtual void display( ostream &os ) ; static void initSeed( ) { srand( ( unsigned ) time( NULL ) ) ; } static void initSeed( unsigned int seed ) { srand( seed ) ; } } ; void Electron::move( ) { switch( getRandom( ) ) { case 0: x++ ; break ; case 1: x-- ; break ; case 2: y++ ; break ; case 3: y-- ; break ; default: break ; } checkConstraint( ) ; } void Electron::checkConstraint( ) { if( x > CONSTRAINT ) x = - CONSTRAINT ; if( x < - CONSTRAINT ) x = CONSTRAINT ; if( y > CONSTRAINT ) y = - CONSTRAINT ; if( y < - CONSTRAINT ) y = CONSTRAINT ; } void Electron::display( ostream &os ) { os << x << "," << y << endl ; } void output( Electron *e[], unsigned int element_num, unsigned int step ) { char output_file[20] ; sprintf( output_file, "%06d.csv", step ) ; ofstream ofs( output_file ) ; if( ofs ) { for( unsigned int i = 0; i < element_num; i++ ) e[ i ]->display( ofs ) ; cout << "[info] " << output_file << " was generated." << endl ; } else { cout << "[error] " << output_file << " can't open." << endl ; } } int main( int argc, char **argv ) { if( argc != 4 ) { cout << "usage : " << argv[ 0 ] << " <element#> <loop#> <step>" << endl ; return -1 ; } char output_file[20] ; // need to validate arguments. unsigned int element_num = atoi( argv[ 1 ] ) ; unsigned int loop = atoi( argv[ 2 ] ) ; unsigned int step = atoi( argv[ 3 ] ) ; Electron *e[ element_num ] ; unsigned int i, j ; Electron::initSeed( ) ; for( i = 0; i < element_num; i++ ) e[ i ] = new Electron[ element_num ] ; output( e, element_num, 0 ) ; for( i = 0; i < loop + 1; i++ ) { for( j = 0; j < element_num; j++ ) { e[ j ]->move( ) ; } if( i % step == step - 1 ) { output( e, element_num, i + 1 ) ; } } for( i = 0; i < element_num; i++ ) delete e[ i ] ; return 0 ; }
つぎにこんなスクリプトを書いて、gifアニメとFLVファイルを出力しました。
#!/bin/bash ELEMENT_NUM=100 COUNT=10000 STEP=50 make brown2d ./brown2d $ELEMENT_NUM $COUNT $STEP i=1 for step in $( seq 0 $STEP $COUNT ) do echo set datafile separator \",\" > .tmp echo set xrange[-100:100] >> .tmp echo set yrange[-100:100] >> .tmp echo set term png >> .tmp echo set output \"$( printf %06d $i ).png\" >> .tmp echo plot \"$( printf %06d $step ).csv\" >> .tmp echo set output >> .tmp gnuplot .tmp echo [log] $step step done. i=$(( i + 1 )) rm -f .tmp rm -f $( printf %06d $step ).csv done convert *.png brown2d.gif echo [log] brown2d.gif was generated. ffmpeg -r 6 -i %06d.png brown2d.flv echo [log] brown2d.flv was generated. rm *.png
このスクリプトを実行して出来上がったのが、上で貼ったgifアニメとYouTubeの動画です。
続いて三次元バージョンの実装をしました。二次元のものを継承して作成しました。
#include <iostream> #include <fstream> #include <cstdlib> #include <ctime> using namespace std; #define CONSTRAINT 100 class Electron { ... snip ... class Electron3D : public Electron { protected: int z ; virtual void checkConstraint( ) ; virtual unsigned int getRandom( ) { return rand( ) % 6 ; } public: Electron3D( ) { z = 0; } virtual void move( ) ; virtual void display( ostream &os ) ; } ; void Electron3D::move( ) { switch( getRandom( ) ) { case 0: x++ ; break ; case 1: x-- ; break ; case 2: y++ ; break ; case 3: y-- ; break ; case 4: z++ ; break ; case 5: z-- ; break ; default: break ; } checkConstraint( ) ; } void Electron3D::checkConstraint( ) { Electron::checkConstraint( ) ; if( z > CONSTRAINT ) z = - CONSTRAINT ; if( z < - CONSTRAINT ) z = CONSTRAINT ; } void Electron3D::display( ostream &os ) { os << x << "," << y << "," << z << endl ; } ... snip ... int main( int argc, char **argv ) { ... snip ... Electron *e[ element_num ] ; unsigned int i, j ; Electron3D::initSeed( ) ; for( i = 0; i < element_num; i++ ) e[ i ] = new Electron3D ; ... snip ... }
二次元のElectronクラスをもっとうまく書けば、Electron3Dの実装量は減るはずです。
二次元のものと同様にこんなスクリプトを書いてgifアニメとFLVファイルを作成しました。
#!/bin/bash ELEMENT_NUM=100 COUNT=10000 STEP=50 make brown3d ./brown3d $ELEMENT_NUM $COUNT $STEP i=1 for step in $( seq 0 $STEP $COUNT ) do ... snip ... echo set zrange[-100:100] >> .tmp ... snip ... done convert *.png brown3d.gif echo [log] brown3d.gif was generated. ffmpeg -r 6 -i %06d.png brown3d.flv echo [log] brown3d.flv was generated. rm *.png
HTML5での実装
続いてHTML5で実装。cavnvasの三次元対応が面倒そうだったので、とりあえず二次元のみ実装。
C++からjavascripへの移植に加えて、パラメータを動的に設定できるようにしました。さらにx方向へバイアスを加えられるようにしました。
<html> <head> <script type="text/javascript"> var Width = 300, Height = 300 ; var element_num ; var surface ; var img ; var electrons ; var interval ; var bias ; function object( o ) { var f = object.f, i, len, n, prop ; f.prototype = o ; n = new f ; for( i = 1, len = arguments.length; i < len; i++ ) for( prop in arguments[ i ] ) n[ prop ] = arguments[ i ][ prop ] ; return n ; } object.f = function( ){ } ; var Electron = { x: Width / 2, y: Height / 2, checkConstraint: function( ) { if( this.x > Width ) this.x = 0 ; if( this.x < 0 ) this.x = Width ; if( this.y > Height ) this.y = 0 ; if( this.y < 0 ) this.y = Height ; }, getRandom: function( ) { return parseInt( Math.random( ) * 100 ) ; }, move: function( ) { var r = this.getRandom( ) ; if( r < 24 - bias ) this.x-- ; else if( r < 49 ) this.x++ ; else if( r < 74 ) this.y-- ; else this.y++ ; this.checkConstraint( ) ; }, beRandom: function( ) { this.x = parseInt( Math.random( ) * Width ) ; this.y = parseInt( Math.random( ) * Height ) ; }, display: function( ) { surface.drawImage( img, this.x - 1 , this.y - 1) ; } } ; function init( ) { surface = document.getElementById( "canvas" ).getContext( "2d" ) ; initBias( ) ; initImage( ) ; initElectrons( ) ; } function initImage( ) { stop( ) ; if( ! img ) img = new Image( ) ; img.src = document.getElementById( "color" ).value ; start( ) ; } function update( ) { surface.clearRect( 0, 0, Width, Height ) ; for( var i = 0; i < element_num; i++ ) { var e = electrons[ i ] ; e.move( ) ; e.display( ) } } function initBias( ) { bias = document.getElementById( "bias" ).value ; } function initElectrons( ) { stop( ) ; element_num = document.getElementById( "element_num" ).value ; electrons = new Array( ) ; for( var i = 0; i < element_num; i++ ) { var e = object( Electron ) ; electrons.push( e ) ; } start( ) ; } function stop( ) { if( interval ) clearInterval( interval ) ; } function start( ) { interval = setInterval( update, 1000 / 60 ) ; } function beRandom( ) { stop( ) ; for( var i = 0; i < element_num; i++ ) electrons[ i ].beRandom( ) ; start( ) ; } </script> </head> <body onLoad="init( )"> <p> <label>Electrons#</label> <select id="element_num" name="element_num" onChange="initElectrons( )"> <option value="1">1</option> <option value="5">5</option> <option value="10">10</option> <option value="50">50</option> <option value="100" selected>100</option> <option value="500">500</option> <option value="1000">1000</option> <option value="5000">5000</option> <option value="10000">10000</option> </select> </p> <p> <label>Color</label> <select id="color" name="color" onChange="initImage( )"> <option value="black.png" selected>black</option> <option value="red.png">red</option> <option value="blue.png">blue</option> </select> </p> <p> <button type=button onClick="beRandom( )">beRandom</button> </p> <p> <label>Bias</label> <select id="bias" name="bias" onChange="initBias( )"> <option value="25">+25</option> <option value="20">+20</option> <option value="15">+15</option> <option value="10">+10</option> <option value="5">+5</option> <option value="1">+1</option> <option value="0" selected>0</option> <option value="-1">-1</option> <option value="-5">-5</option> <option value="-10">-10</option> <option value="-15">-15</option> <option value="-20">-20</option> <option value="-25">-25</option> </select> </p> <canvas id="canvas" width="300" height="300" style="border:medium solid #000000" /> </body> </html>