Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save and Load Random Number Generator State in C++11

This question has been asked before (stackoverflow) but the (accepted) answer is not satisfactory.

The following example saves and loads the state but depending on the number of generated values it works or it doesn't:

#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;

}    

With these parameters it works like expected. However, if I set preN=3 the output looks like:

0.13453 -0.146382 0.46065 
Saving...

Loading...
Generator1  Generator2
-1.87138    0.163712

Why did the assert not apply? Now I set preN=3 and middleN=1 and the output is

0.13453 -0.146382 0.46065 
Saving...
-1.87138 
Loading...
Generator1  Generator2
0.163712    0.163712

If I set middleN to anything larger than 1 the assert applies. Can anyone explain what is going on? What am I doing wrong or not understanding?

Tested with GCC5.4.0 and CLANG3.8.0 on Linux

like image 960
Pieter Bruegel the Elder Avatar asked Feb 21 '17 20:02

Pieter Bruegel the Elder


People also ask

What is random number generator state?

Pseudo Random Number Generator(PRNG) refers to an algorithm that uses mathematical formulas to produce sequences of random numbers. PRNGs generate a sequence of numbers approximating the properties of random numbers. A PRNG starts from an arbitrary starting state using a seed state.

How do you generate a random number between 1 to 100 in C++?

How to Generate Random Numbers in C++ Within a Range. Similar to 1 and 10, you can generate random numbers within any range using the modulus operator. For instance, to generate numbers between 1 and 100, you can write int random = 1+ (rand() % 100).

How do you generate a random number between 1 and 9 in C++?

int x = int(floor(rand() / (RAND_MAX + 1.0) * (high-low) + low));


2 Answers

The problem is not your random number generator's state. The problem is your distribution's state. Yes, distributions can have state too.

You can get the same values by resetting the normal distribution's state with reset. Alternatively, you can preserve and reconstitute the distribution's state too, using << and >>.

like image 182
Nicol Bolas Avatar answered Sep 22 '22 02:09

Nicol Bolas


Thanks to the answer from Nicol Bolas above, I can add the corrected code below:

#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;
}    
like image 41
Pieter Bruegel the Elder Avatar answered Sep 19 '22 02:09

Pieter Bruegel the Elder