今日の「それC++11/Boostでできるよ!」
http://blog.livedoor.jp/dankogai/archives/51761113.html経由で
http://hirobanex.net/article/2011/12/1324792864を知ったわけですが、
こういうのって需要多いんですね。
これC++11なら標準ライブラリにあります。
C++11にまだ移行できない場合はBoostにあるのでそちらで。
こんな感じで使えます。
#include<iostream> #include<vector> #include<random> int main() { std::mt19937 rng; std::discrete_distribution<> dist{ 20, 10, 10, 30, 30}; std::vector<std::string> items{"foo","bar","baz","hoge","moge"}; std::cout << items[dist(rng)] << std::endl; return 0; }
C++11/boostのrandomは乱数生成器と分布を組み合わせて使えるので、
ものすごく便利です。分布はそのままで、乱数生成アルゴリズムだけ切り替えるとかも簡単にできますし、逆に分布だけ別のものに切り替えることも可。
今回の例のような、重み付き乱択っていうんですか?こういう乱数は結局分布が違うだけなので、分布を作ってやれば十分ということ。
あ、これC++11 Advent Calendarのネタにすればよかったorz
ついでに、本当にこれで出来てるのか確かめてみる。
検証コード:
#include<iostream> #include<vector> #include<random> int main() { std::mt19937 rng; std::discrete_distribution<> dist{ 20, 10, 10, 30, 30}; std::vector<std::string> items{"foo ","bar ","baz ","hoge","moge"}; const int N = dist.probabilities().size(); const int total=1e6; std::vector<int> counts(N); for(int i=0;i<total;++i) ++counts[dist(rng)]; for(int i=0;i<N;++i) std::cout << "item: " << items[i] << ",probability: " << dist.probabilities().at(i) << ", generated: " << 1.*counts[i]/total << std::endl; }
結果:
item: foo ,probability: 0.2, generated: 0.199779 item: bar ,probability: 0.1, generated: 0.100002 item: baz ,probability: 0.1, generated: 0.100087 item: hoge,probability: 0.3, generated: 0.300024 item: moge,probability: 0.3, generated: 0.300108
1e6回も回してこの精度がいいか悪いかは知りませんが
ちゃんと出来てますね。