乱数ジェネレータの実装。その1-EngineとDistribution-

boostのvariate_generatorを思い出したので、適当に実装してみます。
C++0xでは使えないそうです。

Mersenne Twisterを使う

Cの標準関数rand()はいろいろ問題があるので、まずはMersenne Twisterを使いましょう。
以下のサイトからダウンロードします。

    init_gen_rand( seed );       //シード値の設定
    uint32_t r = gen_rand32();   //32bitの乱数を取得

XorShiftを使う

以下のサイトに実装例があります。

コピペ。

// シードは4要素の配列。どっかで一度適当に定義すること。
extern unsigned int seed128[4];

// シードを与える関数
inline void init_xorshift(unsigned int s){
	for (unsigned int i=0;i<4;++i) seed128[i]=s=1812433253U*(s^(s>>30))+i;
}

// 32bitの整数乱数を生成
inline unsigned int xorshift128(){
	unsigned int *a = seed128;
	unsigned int  t = (a[0]^(a[0]<<11));
	a[0]=a[1]; a[1]=a[2]; a[2]=a[3];
	return( a[3]=(a[3]^(a[3]>>19))^(t^(t>>8)) );
}

乱数エンジンの実装

インターフェイス
  • シード値の設定
  • 乱数の生成
typedef unsigned int uint32;

class rand_interface
{
public:
    virtual void set_seed( uint32 seed ) = 0;
    virtual uint32 operator()() = 0;
    static uint32 max(){ return 0xffffffff; }
};
mt19937
//整数型[0, 2^32-1]
class mt19937
    : public rand_interface
{
public:
    void set_seed( uint32 seed )
    {
        init_gen_rand( seed );
    }

    uint32 operator()()
    {
        return gen_rand32();
    }

public:
    mt19937( uint32 seed )
    {
        set_seed( seed );
    }
};
xor_shift
//整数型[0, 2^32-1]
class xor_shift
    : public rand_interface
{
public:
    void set_seed( uint32 seed )
    {
        for( uint32 i = 0; i < 4; ++i )
            seed128[ i ] = seed = 1812433253U * ( seed ^ ( seed >> 30 ) ) + i;
    }

    uint32 operator()()
    {
        uint32* a = seed128;
        uint32 t = ( a[ 0 ] ^ ( a[ 0 ] << 11 ) );
        a[ 0 ] = a[1]; a[ 1 ] = a[ 2 ]; a[ 2 ] = a[ 3 ];
        return a[ 3 ] = ( a[ 3 ] ^ ( a[ 3 ] >> 19 ) ) ^ ( t ^ ( t >> 8 ) );
    }

public:
    xor_shift( uint32 seed )
    {
        set_seed( seed );
    }

private:
    uint32 seed128[ 4 ];
};
使い方
int main()
{
    {
        mt19937 rand( seed );
        std::cout << rand();
    }

    {
        xor_shift rand( seed );
        std::cout << rand();
    }

    return 0;
}

Distribution(分布クラス)の実装

乱数エンジンから取得した値を加工するためのクラスを実装します。

[min,max]の一様分布
//整数型の[min, max]の一様分布
class uniform_int
{
public:
    template< typename EngineType >
    uint32 operator()( EngineType& engine )
    {
        int range = max - min + 1;
        return engine() % range + min;
    }

public:
    uniform_int( int min, int max )
        : min( min ), max( max )
    {}

private:
    int min;
    int max;
};

//実数型の[min, max]の一様分布
template< typename RealType >
class uniform_real
{
public:
    template< typename EngineType >
    RealType operator()( EngineType& engine )
    {
        RealType factor = max - min;
        RealType r = engine() / ( RealType )EngineType::max();
        return r * factor + min;
    }

public:
    uniform_real( RealType min, RealType max )
        : min( min ), max( max )
    {}

private:
    RealType min;
    RealType max;
};
使い方
int main()
{
    {
        mt19937 engine( seed );
        uniform_int dist( 1, 6 );
        std::cout << dist( engine ) << std::endl;   //[1, 6]の乱数
    }

    {
        xor_shift engine( seed );
        uniform_real< float > dist( 0.0f, 2.0f );
        std::cout << dist( engine ) << std::endl;   //[0.0, 2.0]の乱数
    }
    return 0;
}