【问题标题】:Proper save/restore of RNG state in boost::random behaving unexpectedly在 boost::random 意外行为中正确保存/恢复 RNG 状态
【发布时间】:2013-05-01 06:13:43
【问题描述】:

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

模拟伪随机数生成器的类也应该模拟 Streamable 概念,即实现 operator>。如果是这样,则 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


代码的 cmets 说明了我认为应该发生的事情。此外,一些行包含注释代码,以使用不同的生成器和不同的分布进行相同的测试。任何一个都会出现同样的问题:在状态恢复后从生成器 randgen 获取的下两个值与保存后立即生成的两个值不同,因为它们应该是。

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

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

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

【问题讨论】:

    标签: boost random


    【解决方案1】:

    我正在查看/保存的生成器不是“真实”的生成器还是什么?

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2016-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-14
      相关资源
      最近更新 更多