乱数生成のアンチパターン - Faith and Brave - C++で遊ぼう
今まで特に気にせず剰余を使ってました…乱数の扱いって難しいんですよね…
というわけで、どれくらい偏るのか検証してみました。
コード(ideone)
#include <random> #include <iostream> const int N=6; const int X=1000000; int main(int, char**) { int n1[N]={},n2[N]={}; ::std::random_device rd; unsigned int s = rd(); ::std::mt19937 g1(s),g2(s); for( int i=0; i < X; ++i ) { n1[ g1()%N ]++; n2[ ::std::uniform_int_distribution<unsigned int>(0, N-1)(g2) ]++; } for( int i=0; i < N; ++i ) { ::std::cout << n1[i] << ", " << n2[i] << ::std::endl; } return 0; }
結果
偏ってる?よくわかりませんね。
少しコードを変えてみます。
コード2(ideone)
#include <random> #include <iostream> #include <algorithm> const int N=6; const int X=10000; const int C=10000; void test(::std::vector<int>& c, unsigned int seed, unsigned int (*f)(::std::mt19937&)) { ::std::mt19937 g(seed); int n[N] ={}; for( int i=0; i < X; ++i ) { n[ f(g) ]++; } int r[N] = {}; for( int i=0; i < N; ++i ) r[i] = i; ::std::sort(r, r+N, [&](int a, int b){ return n[a] > n[b]; }); for( int i=0; i < N; ++i ) { c[r[i]] += N/2 - i; } } int main(int, char**) { ::std::random_device rd; ::std::vector<int> c1(N), c2(N); for( int j=0; j < C; ++j ) { unsigned int s = rd(); test(c1, s, [](::std::mt19937& g){ return g()%N; }); test(c2, s, [](::std::mt19937& g){ return ::std::uniform_int_distribution<unsigned int>(0, N-1)(g); }); } for( int i=0; i < N; ++i ) { ::std::cout << i << ": " << c1[i] << ", " << c2[i] << ::std::endl; } return 0; }
結果
確かに、偏っている…ような気がします。(ホントは全然わかってない。)
ただ、この偏りは逆に利用できるのかもしれません。
例えば、テトリスを考えてみてください。
縦棒のミノを大きい数値に割り当てれば、なんとなくやりごたえが上がるかもしれません。
もしくは、
レアカードを大きい数値に割り当てれば、自然と引きにくくなるかもしれません。
…かも
まとめ
乱数生成器を作るのは(検索すればすぐに情報が出てきますし)、そんなに難しいことではないと思います。ただ、乱数生成器が作れたからといって、乱数を正しく扱えるわけではありません。
というわけで、C++11 の distribution はホントありがたいです。
0 件のコメント:
コメントを投稿