在boost :: random中意外保存/恢复RNG状态意外行为

时间:2012-07-06 21:58:41

标签: boost random

我有一个小型测试程序试图使用boost :: random保存和恢复随机数生成器的状态,但它的行为与文档指示的不同。来自提升文档:

  

对伪随机数生成器建模的类也应该对Streamable概念建模,即实现运算符<<和运算符>>。如果是,则运营商<<将伪随机数生成器的所有当前状态写入给定的ostream,以便运算符>>可以在以后恢复状态。状态应以与平台无关的方式编写,但假定用于写入和读取的语言环境相同。具有恢复状态的伪随机数发生器和刚写入状态的原始数据应该是等效的。

据我了解,如果保存了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获取的下两个值与保存后立即生成的两个值不同,应该是这样。

仔细检查(调试)后,无论我多少次调用getrand(),调用variate生成器getrand()似乎都不会改变生成器randgen的状态,并且因此,当我保存它时,它仍然与刚刚创建时相同,因此,当我在恢复后再次从中拉出它时,它只是从头开始。

每次调用发生器都不应该导致状态进展吗?如果RNG状态永远不变,我怎么能得到不同数字的序列?我正在查看/保存的发电机不是&#34;真正的&#34;一个什么的?

此外,getrand = getrand2的分配操作可能看起来很粗略,但是为这些操作定义了=运算符,用getrand2()替换最后4个调用并没有什么区别。

1 个答案:

答案 0 :(得分:1)

  

我正在查看/保存的发电机不是“真正的”发电机吗?

情况确实如此。您正在使用的uniform_01构造函数实际上是提供引擎的副本,而不是参考。

如果你正在使用Boost 1.39或更高版本,你可以这样使用它:

boost::uniform_01<> getrand;
getrand(randgen);

如果您遇到较旧的Boost并且不需要getrand可复制,则将uniform_01的类型参数从base_generator_type更改为base_generator_type&应该也工作。