在C++11中保存和加载随机数生成器状态

7
这个问题之前提出过(stackoverflow),但是(被接受的)回答并不令人满意。
以下示例保存和加载状态,但是根据生成的值数量,它可能有效也可能无效:
#include <fstream>
#include <iostream>
#include <random>
#include <cassert>

int main()
{
  const int preN = 4;
  const int middleN = 0;

  // initialize randGen
  std::mt19937 randGen1;
  std::normal_distribution<double> distribution1;


  // print some initial random numbers
  for (int i=0;i<preN;++i)
    std::cout << distribution1(randGen1)<<" ";

  // save state
  std::cout << std::endl << "Saving...\n";
  {
    std::ofstream fout("seed.dat");
    fout << randGen1;
  }

  // maybe advance randGen1
  for (int i=0;i<middleN;++i)
    std::cout << distribution1(randGen1)<<" ";

  // load saved state into randGen2 
  std::cout << std::endl << "Loading...\n";
  std::ifstream fin("seed.dat");
  std::mt19937 randGen2;
  fin >> randGen2;
  std::normal_distribution<double> distribution2;

  // are both randGen equal?
  assert(randGen1 == randGen2);

  // print numbers from both generators
  std::cout << "Generator1\tGenerator2\n";
  std::cout << distribution1(randGen1) << "\t"
            << distribution2(randGen2) << "\n";

  return 0;

}    

使用这些参数,它的运行方式就像预期的那样。然而,如果我将preN=3设置,输出看起来像这样:
0.13453 -0.146382 0.46065 
Saving...

Loading...
Generator1  Generator2
-1.87138    0.163712

为什么断言没有生效?现在我设置了preN=3middleN=1,输出结果为:
0.13453 -0.146382 0.46065 
Saving...
-1.87138 
Loading...
Generator1  Generator2
0.163712    0.163712

如果我将 middleN 设为大于1的任何值,则会应用断言。有人能解释一下发生了什么吗?我做错了什么或者没有理解到位吗?
在Linux上通过GCC5.4.0和CLANG3.8.0进行测试。
3个回答

8
问题不在于您的随机数生成器状态,而在于您的分布状态。是的,分布也可以有状态。
您可以通过使用reset重置正态分布的状态来获得相同的值。或者,您也可以使用<<>>保留和重建分布的状态。详情请参见此处

我也尝试了一下(因为那个行为看起来很有趣,而且我很久没有尝试C++了),可以确定的是,均匀分布的行为是不同的! - sascha
它可能没有内部状态很重要(比正常分布简单得多),但这只是经验观察。这只是我的第一次尝试。也许它也有。请忽略我的评论。好答案! - sascha
@sascha:一个分布是否具有影响其输出的状态取决于其实现。 - Nicol Bolas
你有任何关于这种自由规范的原因吗?我想不出任何好的理由不去假设它们是无状态的,除了缓存(构建一个随机池)。我也不认为scipy会这样做(即使是GSL在第一次看起来也不是)。 - sascha
normal_distribution 可能有状态,也可能没有状态。这完全取决于实现算法的选择。 - T.C.
显示剩余2条评论

4
感谢上面来自 Nicol Bolas 的答案,下面是我修改后的代码:
#include <fstream>
#include <iostream>
#include <random>
#include <cassert>

int main()
{
  const int preN = 7;
  const int middleN = 0;

  // initialize another randGen
  std::mt19937 randGen1;
  std::normal_distribution<double> distribution1;

  // print some initial random numbers
  for (int i=0;i<preN;++i)
    std::cout << distribution1(randGen1)<<" ";

  // save state
  std::cout << std::endl << "Saving...\n";
  {
    std::ofstream fout("seed.dat");
    fout << randGen1;
    fout.close();
    std::ofstream fout2("distribution.dat");
    fout2 << distribution1;
    fout2.close();
  }

  // maybe advance randGen
  for (int i=0;i<middleN;++i)
    std::cout << distribution1(randGen1)<<" ";

  // load saved state into randGen2
  std::cout << std::endl << "Loading...\n";
  std::mt19937 randGen2;
  std::normal_distribution<double> distribution2;
  {
    std::ifstream fin("seed.dat");
    fin >> randGen2;
    fin.close();
    std::ifstream fin2("distribution.dat");
    fin2 >> distribution2;
    fin2.close();
  }

  // are both randGen equal?
  assert(randGen1 == randGen2);
  assert(distribution1 == distribution2);

  // print numbers from both generators
  std::cout << "Generator1\tGenerator2\n";
  std::cout << distribution1(randGen1) << "\t"
            << distribution2(randGen2) << "\n";

  return 0;
}    

1

以下是如何保存和恢复双精度随机数种子的方法。如果是整数,使用jrand48而不是erand48。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{

     unsigned short savedseed[3];
     unsigned short currentseed[3];
     double x;

     /*-- initialize ramdom seed to whatever --*/
     currentseed[0]= 23;
     currentseed[1]= 45;
     currentseed[2]= 67;

     printf("\n");

     /*-- generate three random numbers  --*/
     x =  erand48(currentseed);     printf("%g\n", x);
     x =  erand48(currentseed);     printf("%g\n", x);
     x =  erand48(currentseed);     printf("%g\n", x);
     printf("\n");

     /*-- save seed  --*/
     memcpy(savedseed, currentseed, 3*sizeof(unsigned short));

     /*-- generate next three random numbers  --*/     
     x =  erand48(currentseed);     printf("%g\n", x);
     x =  erand48(currentseed);     printf("%g\n", x);
     x =  erand48(currentseed);     printf("%g\n", x);
     printf("\n", x);

     /*-- restore seed  --*/
     memcpy(currentseed, savedseed,  3*sizeof(unsigned short));

     /*-- generate the same three random numbers again --*/
     x =  erand48(currentseed);     printf("%g\n", x);
     x =  erand48(currentseed);     printf("%g\n", x);
     x =  erand48(currentseed);     printf("%g\n", x);
     printf("\n");  
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接