補足メモ 2-1: C# 用 TinyMT の作り方



TinyMT を C# で使用するために、以下の作業を行います。 一つは TinyMT ライブラリの DLL 化と、もうひとつは この DLL 化した TinyMT を呼び出すための C# 用の宣言に関してです。

TinyMT の DLL 化
C# 用のインタフェイス


ビルド済み TinyMT.dll およびソースファイル一式は以下のリンクより ダウンロードして下さい。

ソースコード一式:TinyMT_DLL_Src,zip
バイナリ: TinyMT.dll

メルセンヌ・ツイスタ法を用いると、高品質な乱数を生成することができます。 メルセンヌ・ツイスタ法はその 考案者のページ ( http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html ) で公開されていますが、 今回はその中でもメモリ使用量が少ない TinyMT を C# で使えるようにします。

TinyMT は C で書かれている為、C# で使用する場合には DLL 化する必要があります。 DLL 化する場合は、tinymt32.h の先頭付近にある tinymt32_t の定義後あたりに、 次のようなコードを追加しておきます。

また dllexport する関係上、tinymt32.h にて inline 定義されている関数の static キーワードを削除します。
// tinymt32.h の先頭部分
// typedef struct TINYMT32_T tinymt32_t; の直後に以下のコードを
// 追加します。

extern "C" {
  __declspec(dllexport)
  void tinymt32_init(tinymt32_t * random, uint32_t seed);

  __declspec(dllexport)
  void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[],
                              int key_length);

  __declspec(dllexport)
  uint32_t tinymt32_generate_uint32(tinymt32_t * random);

  __declspec(dllexport)
  float tinymt32_generate_float(tinymt32_t * random);

  __declspec(dllexport)
  float tinymt32_generate_floatOO(tinymt32_t * random);

  __declspec(dllexport)
  float tinymt32_generate_floatOC(tinymt32_t * random);

  __declspec(dllexport)
  float tinymt32_generate_float01(tinymt32_t * random);

  __declspec(dllexport)
  float tinymt32_generate_float12(tinymt32_t * random);

  __declspec(dllexport)
  double tinymt32_generate_32double(tinymt32_t * random);
}

// static キーワードの削除および C リンケージの追加の例
// inline static uint32_t tinymt32_generate_uint32(...) から
// 次の様に変更(extern "C" を追加して static を削除)
extern "C"
inline uint32_t tinymt32_generate_uint32(tinymt32_t * random) {
...
}

それぞれの関数を C リンケージ指定したので、 実際の定義を行なっている tinymt32.cpp (*1) の定義部分でも C リンケージ指定をしておきます:
// tinymt32.cpp におけるリンケージ指定の例
extern "C"
void tinymt32_init(tinymt32_t * random, uint32_t seed) {
...
}
*1 配布されている TinyMT のパッケージでは ファイル名は tinymt32.c となっています。 しかしこのままでは Visual C++ でコンパイルすることができませんので tinymt32.cpp として C++ としてコンパイルを行なっています。 このような状況の為、上記の修正が必要となります。 gcc 等ではこのような修正は必要ないのかもしれません (もともとのファイル名 tinymt32.c にて 問題なくコンパイルできるのかもしれません)。

tinymt64.h および jump32.h にも同様の修正を施します。
// tinymt64.h の typedef struct TINYMT64_T tinymt64_t; の
// 直後に以下のコードを追加。

extern "C" {
  __declspec(dllexport)
  void tinymt64_init(tinymt64_t * random, uint64_t seed);

  __declspec(dllexport)
  void tinymt64_init_by_array(tinymt64_t * random,
                              const uint64_t init_key[],
                              int key_length);

  __declspec(dllexport)
  uint64_t tinymt64_generate_uint64(tinymt64_t * random);

  __declspec(dllexport)
  double tinymt64_generate_double(tinymt64_t * random);

  __declspec(dllexport)
  double tinymt64_generate_double01(tinymt64_t * random);

  __declspec(dllexport)
  double tinymt64_generate_double12(tinymt64_t * random);

  __declspec(dllexport)
  double tinymt64_generate_doubleOC(tinymt64_t * random);

  __declspec(dllexport)
  double tinymt64_generate_doubleOO(tinymt64_t * random);
}

// static キーワードの削除および C リンケージの追加の例
// inline uint64_t tinymt64_generate_uint64(tinymt64_t * random) {
// ...
// }
//  から
// 次の様に変更(extern "C" を追加して static を削除)
extern "C"
inline uint64_t tinymt64_generate_uint64(tinymt64_t * random) {
...
}

// jump32.h のプロトタイプ宣言も次のように修正

extern "C" __declspec(dllexport)
void tinymt32_jump(tinymt32_t *tiny,
                   uint64_t lower_step,uint64_t upper_step,
                   const char * poly_str);
TinyMT の C# 用 DLL 作成がこの文章の主な目的ではありませんが、 上記のような修正を施せば C# から呼び出すことのできる TinyMT.dll を作成することができます。


TinyMT.dll が出来たら、次はその DLL の中にある関数を呼び出すための 準備をします。 具体的には、呼び出し可能にする関数を DllImport アトリビュートを指定して 宣言していきます。

併せて内部に tinymt32_t 型および tinymt64_t 型の状態変数を保持しておき、 気軽に乱数が使えるようなクラスとして定義しておきます。
[入力用プログラムリスト TinyMT.pdf は こちら]
// TinyMT.cs

using System;
using System.Runtime.InteropServices;

namespace ToyBox {
  public class TinyMT {
    public struct tinymt32_t {
      uint status1,status2,status3,status4;	// C だと uint status[4]
      uint mat1;
      uint mat2;
      uint tmat;

      public tinymt32_t(uint inMat1,uint inMat2,uint inTMat) {
        status1=status2=status3=status4=4;
        mat1=inMat1;
        mat2=inMat2;
        tmat=inTMat;
      }
    };

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public
    void tinymt32_init(ref tinymt32_t outRandStatus, int inSeed);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public unsafe
    void tinymt32_init_by_array(ref tinymt32_t outRandStatus,
                                UInt32* inInitKey,int inKeyLength);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public
    uint tinymt32_generate_uint32(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public
    float tinymt32_generate_float(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    float tinymt32_generate_floatOO(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public
    float tinymt32_generate_floatOC(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    float tinymt32_generate_float01(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    float tinymt32_generate_float12(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    double tinymt32_generate_32double(ref tinymt32_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    void tinymt32_jump(ref tinymt32_t inRandStatus,
                       UInt64 inLowerStep,UInt64 inUpperStep,
                       string inPolyStr);

    struct tinymt64_t {
      ulong status1,status2;	// uint64_t status[2];
      uint mat1;
      uint mat2;
      ulong tmat;

      public tinymt64_t(uint inMat1,uint inMat2,ulong inTMat) {
        status1=status2=0;
        mat1=inMat1;
        mat2=inMat2;
        tmat=inTMat;
      }
    };

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public
    void tinymt64_init(ref tinymt64_t outRandStatus, int inSeed);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public unsafe 
    void tinymt64_init_by_array(ref tinymt64_t outRandStatus,
                                UInt64* inInitKey,int inKeyLength);
	
    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    UInt64 tinymt64_generate_uint64(ref tinymt64_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public
    double tinymt64_generate_double(ref tinymt64_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    double tinymt64_generate_doubleOO(ref tinymt64_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    double tinymt64_generate_doubleOC(ref tinymt64_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    double tinymt64_generate_double01(ref tinymt64_t inRandStatus);

    [DllImport("TinyMT.dll",CallingConvention=CallingConvention.Cdecl)]
    extern static public 
    double tinymt64_generate_double12(ref tinymt64_t inRandStatus);

    // mRand32Status, mRand64Status および kJumpPolyStr の初期値は
    // TinyMT のサンプルプログラムのものをそのまま使用。
    tinymt32_t mRand32Status=new tinymt32_t(0x8f7011ee,
                                            0xfc78ff1f,
                                            0x3793fdff);

    tinymt64_t mRand64Status=new tinymt64_t(0xfa051f40,
                                            0xffd0fff4,
                                            0x58d02ffeffbfffbc);

    string kJumpPolyStr="d8524022ed8dff4a8dcc50c798faba43";

    public TinyMT() {
      tinymt32_init(ref mRand32Status, 1);
      tinymt64_init(ref mRand64Status, 1);
    }

    public uint GenerateUInt() {
      return tinymt32_generate_uint32(ref mRand32Status);
    }
	
    public float GenerateFloat() {
      return tinymt32_generate_float(ref mRand32Status);
    }

    public float GenerateFloatOO() {
      return tinymt32_generate_floatOO(ref mRand32Status);
    }

    public float GenerateFloatOC() {
      return tinymt32_generate_floatOC(ref mRand32Status);
    }

    public float GenerateFloat01() {
      return tinymt32_generate_float01(ref mRand32Status);
    }

    public float GenerateFloat12() {
      return tinymt32_generate_float12(ref mRand32Status);
    }

    public double Generate32Double() {
      return tinymt32_generate_32double(ref mRand32Status);
    }

    public void Jump32(ulong inStep) {
      tinymt32_jump(ref mRand32Status, inStep,0,kJumpPolyStr);
    }

    public void Jump32(ulong inHighStep,ulong inLowStep) {
      tinymt32_jump(ref mRand32Status,inLowStep,inHighStep,kJumpPolyStr);
    }

    // [0,2^64)
    public ulong GenerateUInt64() {
      return tinymt64_generate_uint64(ref mRand64Status);
    }

    // [0,1)
    public double GenerateDouble() {
      return tinymt64_generate_double(ref mRand64Status);
    }

    // (0,1)
    public double GenerateDoubleOO() {
      return tinymt64_generate_doubleOO(ref mRand64Status);
    }

    // (0,1]
    public double GenerateDoubleOC() {
      return tinymt64_generate_doubleOC(ref mRand64Status);
    }

    // [0,1)
    public double GenerateDouble01() {
      return tinymt64_generate_double01(ref mRand64Status);
    }

    // [1,2)
    public double GenerateDouble12() {
      return tinymt64_generate_double12(ref mRand64Status);
    }
  }
}

[入力用プログラムリスト Program002_MT.pdf は こちら]
// Program002_MT.cs
// OK と表示されれば成功
using System;
using ToyBox;

namespace No002_MT {
	class Program002_MT {
		static void Main(string[] args) {
			TinyMT tinyMT1=new TinyMT();
			TinyMT tinyMT2=new TinyMT();

			// ショートカット
			const int kJumpStep=10000;
			tinyMT2.Jump32(kJumpStep);

			for(int i=0; i<kJumpStep; i++) {
				float t=tinyMT1.GenerateFloat();
				//Console.WriteLine(t);
			}
			float r1=tinyMT1.GenerateFloat();
			float r2=tinyMT2.GenerateFloat();
			if(r1==r2) {
				Console.WriteLine("r1="+r1);
				Console.WriteLine("r2="+r2);
				Console.WriteLine("OK");
			} else {
				Console.WriteLine("NG");
			}
		}
	}
}