読者です 読者をやめる 読者になる 読者になる

Boost.Random ジェネレータの状態を保存・復旧する

C++

Boost.RandomやC++標準の乱数ジェネレータは、シードと乱数生成された回数さえ覚えておけば、状態を復旧できます。
乱数生成は分布クラス内部で行われるので、そういうことがしたい場合は、分布処理内での乱数生成回数を覚えておくために、ジェネレータをラップして乱数が生成される度にカウントアップするクラスを用意する必要があります。

#include <iostream>
#include <boost/random.hpp>

class logged_generator {
    std::size_t& generate_count_;
    boost::random::mt19937& gen_;
public:
    typedef boost::random::mt19937 engine_type;
    typedef engine_type::result_type result_type;

    logged_generator(std::size_t& c, engine_type& gen)
        : generate_count_(c), gen_(gen) {}

    static result_type min() { return engine_type::min(); }
    static result_type max() { return engine_type::max(); }

    result_type operator()()
    {
        const result_type result = gen_();
        ++generate_count_; // 生成回数をカウントする
        return result;
    }
};

template <class Generator, class Distribution>
void generate(Generator& gen, Distribution& dist)
{
    for (int i = 0; i < 3; ++i) {
        std::cout << dist(gen) << " ";
    }
    std::cout << std::endl;
}

int main()
{
    std::size_t seed = 3;
    std::size_t generate_count = 0; // 乱数生成した回数

    boost::random::mt19937 mt_gen(seed);

    logged_generator gen {generate_count, mt_gen};
    boost::random::uniform_int_distribution<> dist {0, 10};

    generate(gen, dist);

    // シードと乱数生成回数を設定して、ジェネレータの状態を復旧
    boost::random::mt19937 restored_mt_gen {seed};
    restored_mt_gen.discard(generate_count);


    // 前まで使ってたジェネレータオブジェクトと、結果を比べてみる
    generate(restored_mt_gen, dist);
    generate(gen, dist);
}
6 0 7 
9 3 1 
9 3 1 

ジェネレータの状態復旧には、ジェネレータのコンストラクタもしくはseed()メンバ関数でシード値を設定し、discard()メンバ関数に生成回数を設定します。
実行結果の後ろ2行は、ジェネレータを復旧したあとに生成したものと、以前まで使っていたジェネレータオブジェクトで引き続き生成したものです。正しく復旧できていることがわかります。