web-dev-qa-db-ja.com

std :: mt19937はウォームアップを必要としますか?

多くの疑似乱数ジェネレーターは、「ウォームアップ」するために多くのサンプルを必要とすることを読みました。 std :: random_deviceを使用してstd :: mt19937をシードする場合、それはそうですか、それとも構築後に準備ができていると期待できますか?問題のコード:

#include <random>
std::random_device rd;
std::mt19937 gen(rd());
46
Brent

メルセンヌツイスターはシフトレジスタベースのpRNG(疑似乱数ジェネレーター)であるため、0または1の長いランで不良シードの影響を受け、内部状態が十分に混ざるまで比較的予測可能な結果が得られます。

ただし、単一の値を取るコンストラクタは、そのような「悪い」状態を生成する可能性を最小限に抑えるように設計されたシード値に対して複雑な関数を使用します。 SeedSequenceの概念に準拠したオブジェクトを介して内部状態を直接設定するmt19937を初期化する2つ目の方法があります。これは、「良好な」状態の選択またはウォームアップの実行について心配する必要がある場合がある、この2番目の初期化方法です。


標準には、seed_seqと呼ばれる、SeedSequenceコンセプトに準拠したオブジェクトが含まれています。 seed_seqは、任意の数の入力シード値を取り、pRNGの内部状態を直接設定するのに適した一連の異なる値を生成するために、これらの値に対して特定の操作を実行します。

以下は、std::mt19937状態全体を満たすのに十分なランダムデータを含むシードシーケンスをロードする例です。

std::array<int, 624> seed_data;
std::random_device r;
std::generate_n(seed_data.data(), seed_data.size(), std::ref(r));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));

std::mt19937 eng(seq);

これにより、状態全体がランダム化されます。また、各エンジンは、seed_sequenceから読み取るデータの量を指定するので、ドキュメントを読んで、使用するエンジンの情報を見つけることができます。

ここでは、seed_seqをstd::random_deviceから完全にロードしていますが、seed_seqは、特にランダムではない数個の数値が適切に機能するように指定されています。例えば:

std::seed_seq seq{1, 2, 3, 4, 5};
std::mt19937 eng(seq);

以下のコメントでは、Cubbiはseed_seqがウォームアップシーケンスを実行することで機能することを示しています。

シードの「デフォルト」は次のとおりです。

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 rng(seed);
57
bames53

32ビット値を1つだけシードした場合、得られるのは、状態空間を通る同じ2 ^ 32軌道の1つだけです。 PRNG状態のKiBを使用する場合は、おそらくすべてをシードする必要があります。@ bames63 'の回答へのコメントで説明されているように、_std::seed_seq_の使用はおそらく適切ではありません状態全体を乱数で初期化する場合のアイデア悲しいことに、_std::random_device_はSeedSequenceの概念に準拠していませんが、次のようなラッパーを作成できます。

_#include <random>
#include <iostream>
#include <algorithm>
#include <functional>

class random_device_wrapper {
    std::random_device *m_dev;
public:
    using result_type = std::random_device::result_type;
    explicit random_device_wrapper(std::random_device &dev) : m_dev(&dev) {}
    template <typename RandomAccessIterator>
    void generate(RandomAccessIterator first, RandomAccessIterator last) {
        std::generate(first, last, std::ref(*m_dev));
  }
};

int main() {

    auto rd = std::random_device{};
    auto seedseq = random_device_wrapper{rd};
    auto mt = std::mt19937{seedseq};
    for (auto i = 100; i; --i)
        std::cout << mt() << std::endl;

}
_

これは、少なくとも概念を有効にするまで機能します。コンパイラがSeedSequenceをC++ 20 conceptとして認識しているかどうかに応じて、不足しているgenerate()メソッドのみを提供しているため、機能しない場合があります。 。ただし、PRNGはシードシーケンスオブジェクトを格納しないため、アヒル型テンプレートプログラミングでは、そのコードで十分です。

4

MTが「不十分」にシードされて、最適でないシーケンスになる可能性がある状況があると思います。正しく覚えていれば、すべてゼロのシードはそのようなケースの1つです。これがあなたにとって深刻な問題である場合は、WELLジェネレーターを使用してください。それらはより柔軟であると思います-シードの品質はそれほど重要ではありません。シードが不十分で、一連のサンプルを生成してジェネレーターを最適な状態にすることとは対照的です。)

2
Mayur Patel