高速アニメーション HOME > ブラウン運動  > キャンパスの作成 > 高速アニメーション

07.05 高速化

今までのプログラムが正しくコンパイルされると次の動画にあるような動きとなります。

【ブラウン運動v0.00】


この円は極めて原始的な動きではありますが、自分で距離と方向を決めて「自立歩行」しています。



出来たばかりですが、少し改良してみます。 改良個所は「複数の円を動かす」こと。
では、順に変更箇所を説明していきます。


★プリコンパイル #define

以前説明したコンパイラーがコンパイルする前に行うための記号””です。”#define”は、「コンパイル前にデータを置き換える」ことができる宣言です。

    #define CELLS_COUNT        50000
         ①            ②

この場合プログラム上に登場する①"CELLS_COUNT"という文字を②50000という数字に置き換えるということになります。 これの長所は今回の改良のように「複数」の場合、具体的に「いくつ」に増やすのか書いてないじゃないですか。例えば最初に100個にしようと作成し最後になって1000個でも行ける、次に10000個にしようとなると、様々な個所に「個数」となる数字が書かれていると、その都度探して全部修正しないといけないことになり、修正し忘れの原因にもなります。上記の宣言を使用すると、この一か所さえ修正すればすべてが変更になります。短所は個数がすべてCELLS_COUNTとなってしまっているため、設定個数を忘れてしまった場合必ず最初に戻って確認しないといけなくなることです。


★配列

GGEもNatureCしか知らない昔ながらの人間であるため、配列といえば”A[50]”や”A(50)”のように宣言していました。ところがvc++ .Netの場合、この宣言も勿論できるのですが、宣言できる箇所がnamespace ProtoTypeの上しかできません。どうやらC++マネージ拡張された.Net仕様のプログラムではArrayクラスを使用しないといけないいけないようです。従来のものも同じように使用できるために無理して使用する必要はありませんが、今回は試しにArrayクラスを使用してみました。

この仕様を対比させてみていくと
【宣言部
通常C++        int        p[10];
C++/CLI        array<int>^ p= gcnew array<int>(10);

通常C++        int        p[10][2];
C++/CLI        array<int,2>^ p= gcnew array<int,2>(10,2);


【アクセス部】
通常C++        p[1] = 1;
C++/CLI        p[1] = 1;

通常C++        p[1][0] = 1;
C++/CLI        p[1 , 0] = 1; 


★C++マネージ拡張

「C++マネージ拡張」 ウィキペディア 引用

C++マネージ拡張 (Managed Extensions for C++, Managed C++) は、C++.NET Frameworkアプリケーションを記述するための、マイクロソフトによるC++の拡張である。これによって、C++でネイティブコードだけでなく共通言語ランタイム (CLR) に向けたアプリケーションを記述できる。この拡張は、2002年にリリースされたVisual Studio .NET (2002) に含まれるVisual C++ .NET (2002) に初めて搭載された。

なお、2005年後半にリリースされたVisual Studio 2005では、より洗練されたC++/CLIという独立した後継言語が登場し、C++マネージ拡張は非推奨となった。さらにVisual Studio 2015では廃止され、C++/CLIへの移行が促されている[1][2]

マネージドC++およびC++/CLIで記述されたアプリケーションは、C#など他の.NET言語同様に共通中間言語 (CIL) と呼ばれる中間言語にコンパイルされる。「マネージ (Managed)」とは、.NET仮想マシンによって管理されながら動作するという意味である。このため、ガベージコレクタなどのCLRの機能を利用することができ、C#VB.NETなどといった.NET言語のコードを呼び出したり呼び出されたりといた相互運用ができる。

しかし、必要に応じて1つのアセンブリ(EXE/DLL)にネイティブコードも混在できる点が.NET言語の中でも特殊である。このような言語はマネージドC++およびC++/CLIのほかにはない。一般の.NET言語はP/InvokeCOMを通してC++コードとやりとりする必要がある。このため、マネージドC++およびC++/CLIはマネージコードとネイティブコードの橋渡しとしてしばしば利用される。すなわち、C/C++あるいはその他の言語で書かれたライブラリを.NET用で利用するラッパーライブラリを作ったり、その逆を作ったりするために用いられるのである。」



#pragma once
#include <math.h>
#define CELLS_COUNT       50000

namespace ProtoType {

  using namespace System;
  using namespace System::ComponentModel;
  using namespace System::Collections;
  using namespace System::Windows::Forms;
  using namespace System::Data;
  using namespace System::Drawing;

  /// 
  /// MyForm の概要
  /// 
  public ref class MyForm : public System::Windows::Forms::Form
  {
  public:
    Graphics^      gf;
    Bitmap^       canvas;
    int         cellMax;
    int         LSize;
    double       PI = 3.141592653;
    array<int,2>^  CellAddress = gcnew array<int,2>(CELLS_COUNT,2);
    array<double>^ sin_table= gcnew array<double>(360);
    array<double>^ cos_table= gcnew array<double>(360);

    MyForm(void)
    {
      InitializeComponent();
      //pictureBoxを対象とするビットマップImageオブジェクトを作成する
      canvas = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
      //ImageオブジェクトのGraphicsオブジェクトを作成する
      gf = Graphics::FromImage(canvas);

      Random^     rr = gcnew Random();
      int       rx,ry;
      int       hx,hy;

      hx = (pictureBox1->Width / 2);
      hy = (pictureBox1->Width / 2);
      for (int  i = 0; i<CELLS_COUNT; i++)
      {
        rx = rr->Next(-hx,hx);
        ry = rr->Next(-hy,hy);

        CellAddress[i,0] = hx + rx;
        CellAddress[i,1] = hy + ry;
      }


      for (int  i = 0; i<360; i++)
      {
        cos_table[i] = cos((double)i / 180.0 * PI);
        sin_table[i] = sin((double)i / 180.0 * PI);
      }
    }



★高速化
さて! 皆さんのパソコンは速いですか?
という質問を投げかけた時に、どうお答えになるでしょうか?


それが顕著に分かるのが、同じ処理を複数回実行し描画した場合です。
結果は動画見ていただいているのでわかりませんが、実際プログラムを動作させると全て計算し定期的に動いているためタイマー初期値が10ミリ秒ですから、それ以内に処理が終了しないと周期は必然的に遅延してきます。しかも人間が目で見ていますので最初は「違和感がある」で済みますが、次第に「おかしい」ということになってきます。

従って処理は出来るだけ速く終了に作らなくてはいけません。これは速いPCをお持ちでも同じことで個数を増やしていけば次第に同じことになります。

では、高速化のちょっとしたテクニックをやってみます。

下記プログラムを見ていただくと分かるようにsin(),cos()がsin_table[]とcos_table[]になっています。
これはRandomクラスから貰っている角度は0~359度の整数値です。っということはsin()、cos()に入る値も同様に0~359度のラジアン値360個しかありません。このため、毎回sin,cosを計算しなくても予め360個分の正弦・余弦値を計算しておいて、使用する際は角度の値を配列内から読んでこればよいはずです。この中で最も処理時間がかかるのは、そうなんです!算術関数なのです。 まずこれを実施し個体が1000個体の場合と10000個体の場合をやってみましょう。


void  draw_pair(void)
{
  double     r;
  int       th;
  Random^     rr;

  gf->Clear(Color::White);

  rr = gcnew Random();
  for (int i = 0; i<cellMax; i++)
  {
    r = rr->Next(0,10);
    th = rr->Next(0, 360);
    CellAddress[i,0] += (int)(r * cos_table[th]);
    CellAddress[i,1] += (int)(r * sin_table[th]);

    gf->DrawEllipse(Pens::Black, CellAddress[i,0], CellAddress[i,1], LSize, LSize);
  }
  pictureBox1->Image = canvas;


}



【ブラウン運動 1000個体】



ブラウン運動 10000個体】


07.06 拡散

これで複数の微粒子をランダムウォークされることができるようになりました。
ウィキペディアでブラウン運動は、それズバリの記載以外に「拡散」の項目にも載っています。下記の記述にもあるように気体中のブラウン運動は気体の乱れにより観測しずらいようです。そこで、このシミュレータを使用して気体中のブラウン運動の様子をみてみましょう。

【拡散→ブラウン運動 ウィキペディア引用】
ブラウン運動は不連続的な粒子が液体中で拡散するときに起きる。熱エネルギーによるものであるから、運動が観測できる()ためには、対象粒子の質量は非常に小さいものでなければならない。運動の方向はランダムで常に変化している。ブラウン運動は原理的には気体中でも起きるが、気体中の微粒子の運動はふつう拡散のほか乱流に支配されているため観測しにくい。」

どうなっていくか観測しやすいように、ウィンドウの中心から一気に1000個体を放出します。

さあ! どうなるか!!

ほらほら! 
全くランダムな動作させているのに時間がたってくると拡散していく様子が見て取れます!


【ブラウン運動v0.01】


HOMEa> > ブラウン運動キャンパスの作成 > 高速アニメーション