2

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

模拟伪随机数生成器的类也应该模拟 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 获取的下两个值与保存后立即生成的两个值不同,因为它们应该是。

经过仔细检查(调试),似乎对变量生成器 getrand() 的调用根本不会改变生成器的状态,randgen无论我在其上调用 getrand() 多少次,所以当我保存它时,它仍然存在就好像它刚刚创建一样,因此,当我在恢复后再次从中拉出时,它只是从头开始。

不应该每次调用生成器都会导致状态前进吗?如果 RNG 状态从不改变,我怎么能得到不同数字的序列?我正在查看/保存的发电机不是“真实”的发电机,还是什么?

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

4

1 回答 1

1

我正在查看/保存的发电机不是“真实”的发电机,还是什么?

确实如此。uniform_01您正在使用的构造函数实际上会复制所提供的引擎,而不是获取引用。

如果您使用的是 Boost 1.39 或更高版本,则可以像这样使用它:

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

如果您卡在较旧的 Boost 上并且不需要getrand可复制,则将uniform_01's 类型参数从base_generator_typeto更改base_generator_type&也应该有效。

于 2013-04-30T22:13:43.317 回答