rand, arc4random, random
C++での乱数の使い方をまとめとく。
rand, srand
Cの標準ライブラリ
0以上, RAND_MAX以下の整数乱数を発生させる。
良くやるのは
time_t time(time_t *timer);
を使うやり方。引数のtimerには戻り値と同じ現在の暦時刻が代入される。
必要なけりゃぬるぽをいれときゃおk。
ただし、グローバル関数を使ってるので、プログラム全体で一つの乱数生成器しか利用できない。
シミュレーション用途だと、再現性を保証するための状態管理がむずかしくなる。
RAND_MAX: 2147483647 までの一様乱数しか生成できない。
乱数の質もあまり良くないらしい。
乱数の範囲は、剰余演算で制御できる。
srand(static_cast<unsigned>(time(nullptr))); //srand呼ばないと毎回同じ結果になる std::cout << "max: " << RAND_MAX << std::endl; REP(i, LOOP) std::cout << rand()%(MAX+1) << " "; std::cout << std::endl;
arc4random
ANSI Cにはないので、使えない場合もある。
iOS/Macでは使えてます。
randと違って、シードを設定する必要がない。
arc4random_stir 関数が、 /dev/urandom からデータを読み取り、
それを使用して Fn arc4random_addrandom によって S-Box の順序を変える。
なるほど、わからん。
とにかく、
arc4randomは自分自身で初期化するため、arc4random_stirを呼ぶ必要はない。
/dev/urandomからデータを読み取るって書いてあるから、boost::random_device()と同じようなもんなのかな?
非決定性、初期化の必要はない。
REP(i, LOOP) std::cout << arc4random()%(MAX+1) << " "; std::cout << std::endl;
random
C++11だと使えるはずだけど、Boost Randomと少し違う点がある。
seedの設定にはseed_seqを使う。
seed_seqのコンストラクタは初期化用ベクタのイテレータをとる。
初期化用のベクタの長さはいくらでもいいみたい。長くても、全要素が使われるとは限らない。
別にシードはseed_seqでなくてもいいみたいなので、std::random_deviceとかarc4random()とかtime(nullptr)とかでも良い気もする。
std::random_device rnd; //std::mt19937_64 rnd; std::vector< std::uint_least32_t > v(10); //初期用ベクタ std::generate( v.begin(), v.end(), std::ref(rnd) ); //algorithm generateを使って、初期用ベクタを埋める std::seed_seq seed(v.begin(), v.end()); std::mt19937 engine( seed ); //std::mt19937 engine(rnd()); std::uniform_real_distribution<> distribution(0, 1.0); std::cout << "min: " << engine.min() << " max: " << engine.max() << std::endl; REP(i,10) std::cout << engine() << std::endl; std::cout << std::endl; std::cout << "min: " << rnd.min() << " max: " << rnd.max() << std::endl; REP(i,10) std::cout << distribution(engine) << std::endl; std::cout << std::endl;
Boost.Randomとの大きな違いは、variate_generator
なので、こんな感じにbindを使って書いたりする。
auto myrnd = std::bind( std::uniform_real_distribution<double>(0.0, 1.0), std::mt19937(arc4random()) ); REP(i,10) std::cout << myrnd() << std::endl;