技術ブログとか

C/C++/C#/DirectX/Effect

random

数日お泊りしてたので書けませんでした、傍から見るとまさに3日坊主。

twitterで某先輩が「srand/randを教えるぐらいならC++11のrandomを教えろ」と申されていたので、まとめてみる。

 

C++11でrandomという標準ライブラリが追加された。

この中には様々な(疑似)乱数生成器、それらの分布を変更する分布生成器などが格納されている。

乱数生成器はtemplate classとして実装されているが、それらのパラメータを既に設定してある乱数生成器もtypedefで定義されている。

大抵の場合はこちらを使用すればことは足りるため、今回書いていくのも後者の設定済み生成器である。

とりあえず、それらの基本的なものをいくつか取り上げていく。

 

まずは非決定的な乱数の生成エンジン、random_device

int main()
{
    // 返ってくる結果の最小値と最大値
    cout << "min:" << random_device::min() << endl;
    cout << "max:" << random_device::max() << endl;

    // 適当に5回ほど乱数を出力
    random_device rd;
    for(int i = 0; i < 5; ++i)
    {
        cout << rd() << endl;
    }
}

非決定的な乱数というのは、つまり予測ができない乱数である。

ソフトウェアでは基本的に疑似乱数しか生成できないため、ハードウェアのノイズなどを用いて乱数を生成する。

これを使えばシード値に影響されない、本当の意味での乱数を生成できるが、欠点として速度が遅いことが挙げられる。

 

次にメルセンヌツイスタ、mt19937及び64bit長のmt19937_64

int main()
{
    // 返ってくる結果の最小値と最大値
    cout << "min:" << mt19937::min() << endl;
    cout << "max:" << mt19937::max() << endl;

    // seedにrandom_deviceで生成した乱数を設定する
    random_device rd;
    mt19937 mt(rd());

    // 適当に5回ほど乱数を出力
    for(int i = 0; i < 5; ++i)
    {
        cout << mt() << endl;
    }
}

vs2010ではminとmaxがstaticではなかったので注意、vs2012では動作確認済み。

メルセンヌツイスタの特徴は、高品質の乱数を高速に生成できるという点である。迷ったらこれ使っておけばいい。

コンストラクタでシード値が指定できるが、何も指定しなかった場合はデフォルト引数に5489uが指定されている。

シード値はコンストラクタのほか、seedメンバ関数でも変更することができる。

また、指定した回数だけ乱数を生成して破棄し、強制的に内部状態を進めるdiscardというメンバ関数も持っている。

mt19937_64も同様。

 

ほかにもrand関数でお馴染みの線形合同法などもあるが、わざわざ使う理由も今のところないので省略する。

 

次は乱数を一様の確率で指定の範囲に分布させるuniform distribution

int main()
{
    // seedにrandom_deviceで生成した乱数を設定する
    random_device rd;
    mt19937 mt(rd());

    // 0以上10以下の間に一様分布させる乱数生成器
    uniform_int_distribution<int>     uid(0, 10);
    uniform_real_distribution<double> urd(0.0, 10.0);

    // それぞれ5回ずつ乱数を出力
    for(int i = 0; i < 5; ++i)
    {
        cout << uid(mt) << endl;
    }
    for(int i = 0; i < 5; ++i)
    {
        cout << urd(mt) << endl;
    }
}

uniform_int_distribution<char>を指定すれば、文字のランダム生成もできる。

分布の範囲をあとから取得する場合はmin/maxあるいはa/bがそれぞれに対応している。

分布のパラメータをあとから取得/設定する場合はparamを使えばよい。

 

ほかにはnormal_distribution正規分布や、poisson_distributionでポワソン分布など、様々な分布生成器が利用できる。

今日は眠いのでここら辺で。