ゆき社長

シーゲンガーのお勉強 ゲームプログラマ、ゲーマー、色々!

std::random 事始め

とある事情があり std::random を使うことに

libcのrandもあるんだけど、C++11には新しく乱数ライブラリが入りました

通常のrandは、線形合同法等の古いアルゴリズムを使っているため、乱数に偏りが出たり規則性が出る、循環が短い

等々問題点が多いので

ちょっと意識の高い人は メルセンヌツイスター法(MT法) を使ってたと思います

MT法は、乱数アルゴリズムの中でも、偏りが少なく計算も速く、非常に強力です

C++11では、MT法等が抽象化され標準で使えるようになりました

これは一様乱数を作成するものですが、他にも正規分布、ベルヌーイ分布、ポワソン分布等

色々な分布を作成する事が可能ですし

今まで (rand() % 6)+1 等と余りを使ったりして乱数を取得していたのが

乱数の範囲も指定できるようになります

(しかも 上記の場合においては rand()を使うより速くなる事が多い)

つまりは とっても強力

とりあえず、簡単に MT法で乱数を出すなら

#include

int main(){

std::mt19937 r(static_cast(time(nullptr)));

for (int i = 0; i < 10; ++i) {

std::cout << std::hex << r() << std::endl;

}

return(1);

}

std::mt19937 というのが乱数生成機で、乱数シードに timeを入れる

わかりやすいですね。

これで十分だと思うのですが、random_deviceやseed_seq を使う事で

より良い乱数を作れるらしい

(この辺の詳しい所は 数学得意な人に教えてもらいたい)

ので 定型文として

下記のようにすると より良い乱数が得られる

#include

int main(){

std::random_device rnd;

std::vector< std::uint_least32_t> v(10) ;

std::generate( v.begin(), v.end(), std::ref(rnd) ) ;

std::seed_seq q( v.begin(), v.end());

std::mt19937 mt{ q } ;

for (int i = 0; i < 10; ++i) {

std::cout << std::hex << mt() << std::endl;

}

return(1);

}

random_deviceというのが、素晴らしい乱数を作るらしいが、速度の関係か

これを毎回呼ぶのは好ましくないっぽく、シードとして使う

vectorを使い、乱数シードを複数持つ??

ちょっと 細かく追ってないので想定の範囲でゴメンナサイ。

今回は10個シードを持ちます

注目は std::ref() を使ってる所かな。

参照で渡さなければ

calling a private constructor of class

コンパイルエラー。

2つ目の注目は mt19937の引数 seed_seqは rvalue不可能という事

// OK

std::seed_seq q( v.begin(), v.end());

std::mt19937 mt{ q } ;

// NG

std::mt19937 mt( std::seed_seq( v.begin(), v.end()) );

このへんは、理由を調べるのはコードを追っかけないとよくわからないので

定型文として 何も考えずに覚える事にします

これで 整数の乱数が作成出来たので さいころを実装するのに

(mt() % 6) +1

でも可能ですが、uniform_int_distribution を使うとより高速で便利に出来ます

さらに std::bind を使い、dice() という関数オブジェクトにしてしまいます

#include

int main(){

std::random_device rnd;

std::vector< std::uint_least32_t> v(10) ;

std::generate( v.begin(), v.end(), std::ref(rnd) ) ;

std::seed_seq q( v.begin(), v.end());

std::uniform_int_distribution dist( 1, 6 ) ;

auto dice=std::bind(std::uniform_int_distribution(1,6), std::mt19937{q});

for (int i = 0; i < 10; ++i) {

std::cout << dice() << std::endl;

}

return(1);

}

んー いんじゃないですかー!