2

boost::random を使用して乱数ジェネレーターの状態を保存および復元しようとする小さなテスト プログラムがありますが、ドキュメントが示すように動作していません。ブーストドキュメントから:

疑似乱数ジェネレーターをモデル化するクラスは、Streamable の概念もモデル化する必要があります。つまり、operator<< および operator>> を実装します。その場合、operator<< は疑似乱数ジェネレーターのすべての現在の状態を指定された ostream に書き込み、operator>> は後で状態を復元できるようにします。状態はプラットフォームに依存しない方法で書き込まれますが、書き込みと読み取りに使用されるロケールは同じであると想定されます。復元された状態の疑似乱数生成器と、書き込まれたばかりの状態の元の疑似乱数生成器は等価でなければなりません。

私が理解しているように、RNG 状態が保存され、そこから数値が引き出されると、状態が変わるはずです。後で状態が復元された場合、ジェネレーターがロールバックされたときとまったく同じ数を生成できるはずです。これを調べるテストプログラムを作ったのですが、一見状態が復元されていないように見えます。次のコードを検討してください。

 unsigned int s = static_cast<unsigned int>(std::time(0));

//typedef boost::minstd_rand base_generator_type;
typedef boost::mt19937 base_generator_type;
base_generator_type randgen(s);
boost::uniform_01<base_generator_type> getrand(randgen);
//boost::normal_distribution<float> noise(0,1);
//boost::variate_generator<base_generator_type,
//boost::normal_distribution<float> > getrand(randgen, noise);

double numsbefore[2], numsrightafter[2], numsnew[4];

//generate a short sequence, save it, and display
numsbefore[0] = getrand();
numsbefore[1] = getrand();

cout << "First Sequence, before save: " 
     << numsbefore[0] << " "
     << numsbefore[1]  << endl; 

//save the current RNG state to a file using the stream interface
std::ofstream rngfileout("test_rngfile.txt");
rngfileout << randgen;
rngfileout.close();

//generate the next two numbers and display
numsrightafter[0] = getrand();
numsrightafter[1] = getrand();
cout << "Next, right after save: " 
   << numsrightafter[0] << " "
   << numsrightafter[1] << endl;

 //read in the RNG state that was saved, back into the RNG, restoring the state
 //to be such as it was prior to the most recent two calls to randgen()
 std::ifstream rngfilein("test_rngfile.txt", ifstream::in);

 if(!rngfilein.good())
 {
  cout << "Couldn't read from file\n";
  return 0;
 }
rngfilein >> randgen;
rngfilein.close();

//declare and initialize a new variate generator to the newly-restored generator
boost::uniform_01<base_generator_type> getrand2(randgen);
//   boost::variate_generator<base_generator_type, 
//     boost::normal_distribution<float> > getrand2(randgen, noise);

//copy the new variate function into the old one, to allow us to use
//the old one on the restored generator   
getrand = getrand2;

//generate the next sequence
//The first two should be the same as the most recent two called
//The next two should be new random numbers
numsnew[0] = getrand();
numsnew[1] = getrand();
numsnew[2] = getrand();
numsnew[3] = getrand();

cout << "Restored, Next: " 
     << numsnew[0] << " "
     << numsnew[1] << " "
     << numsnew[2] << " "
     << numsnew[3] << endl; 

指定された時間シードの出力は次のとおりです。

最初のシーケンス、保存前: 0.970021 0.266862
次に、保存直後: 0.110485 0.267466
復元、次: 0.970021 0.266862 0.110485 0.267466


コードのコメントは、何が起こるべきだと私が考えているかを示しています。また、一部の行には、別のジェネレーターと別のディストリビューションで同じテストを実行するためのコメント付きコードが含まれています。これらのいずれでも同じ問題が発生します。状態が復元された後に発生器 randgen から取得される次の 2 つの値は、保存直後に生成される 2 つの値と同じではありません。

詳細な検査 (デバッグ) では、変量ジェネレーター getrand() への呼び出しは、randgengetrand() を何度呼び出してもジェネレーターの状態をまったく変更しないように見えるため、保存するとそのままになります。作成したばかりの場合と同じであるため、復元後に再度プルすると、最初から開始されます。

ジェネレーターを呼び出すたびに状態が進むべきではありませんか? RNG の状態が変わらない場合、同じ番号ではないシーケンスを取得するにはどうすればよいですか? 私が表示/保存しているジェネレーターは「本物の」ジェネレーターではありませんか?

また、 の代入演算はgetrand = getrand2大雑把に見えるかもしれませんが、それらに対して = 演算子が定義されており、最後の 4 つの呼び出しを に置き換えてgetrand2()も違いはありません。

4

1 に答える 1