今日の「それ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回も回してこの精度がいいか悪いかは知りませんが
ちゃんと出来てますね。