Irreproducible random numbers using `<random>`
I am trying to create a class that creates random numbers for several distributions, keeping them reproducible (by setting the initial seed).
The code seems to work until I start using the normal distribution surface and strange errors. This is basically:
- If I uncomment
double a = rnd.rnorm(0.0, 1.0);-line (line 40) (that is, if I callrnormbefore setting the seed), the first random number of the normal distribution no longer matches, the random numbers after that again match - If I get an odd number of random numbers from the normal distribution, the normal random numbers are shifted by one (for example, setting line 39 to
int n = 3;) - If I do two things together, random numbers are shifted one in the other direction (leading)
Now my question is: what causes this strange behavior? Did I implement RNG incorrectly? And most importantly, how can I fix this?
The code
If you want to check the results yourself, you can use this http://cpp.sh/9phre
or
#include <stdio.h> #include <random> // Class to create random numbers // Main functions to set the seed: setseed() // create uniformly distributed values: runif() // and normally distributed values: rnorm() class RNG { public: RNG(int seed = (int) time(0)) { setseed(seed); }; ~RNG() {}; void setseed(int newSeed) { re.seed(newSeed); }; double runif(double minNum, double maxNum) { return dud(re, distUnifDbl::param_type{minNum, maxNum}); }; double rnorm(double mu, double sd) { return dnd(re, distNormDbl::param_type{mu, sd}); }; private: // take the Mersenne-Twister Engine std::mt19937 re {}; // create the uniform distribution using distUnifDbl = std::uniform_real_distribution<double>; distUnifDbl dud {}; // create the normal distribution using distNormDbl = std::normal_distribution<double>; distNormDbl dnd {}; }; int main(int argc, char const *argv[]) { RNG rnd; int n = 4; // setting n to an odd number, makes _all_ normal numbers non-reproducible //double a = rnd.rnorm(0.0, 1.0); // uncommenting this, makes the _first_ normal number non-reproducible printf("Testing some Uniform Numbers\n"); rnd.setseed(123); for (int i = 0; i < n; ++i) { printf("% 13.10f ", rnd.runif(0.0, 1.0)); } rnd.setseed(123); printf("\n"); for (int i = 0; i < n; ++i) { printf("% 13.10f ", rnd.runif(0.0, 1.0)); } printf("\n"); printf("\nTesting some Normal Numbers\n"); rnd.setseed(123); for (int i = 0; i < n; ++i) { printf("% 13.10f ", rnd.rnorm(0.0, 1.0)); } rnd.setseed(123); printf("\n"); for (int i = 0; i < n; ++i) { printf("% 13.10f ", rnd.rnorm(0.0, 1.0)); } printf("\n"); return 0; } results
Base case
When setting n = 4 and comments a , I get the following (this is exactly what I need / need: reproducible "random" numbers):
Testing some Uniform Numbers 0.7129553216 0.4284709250 0.6908848514 0.7191503089 0.7129553216 0.4284709250 0.6908848514 0.7191503089 Testing some Normal Numbers -0.5696096995 1.6958337120 1.1108714913 0.9675940713 -0.5696096995 1.6958337120 1.1108714913 0.9675940713 Error 1
Now for the mistakes. By setting n = 5 (or any odd number), I get:
Testing some Uniform Numbers 0.7129553216 0.4284709250 0.6908848514 0.7191503089 0.4911189328 0.7129553216 0.4284709250 0.6908848514 0.7191503089 0.4911189328 Testing some Normal Numbers -0.5696096995 1.6958337120 1.1108714913 0.9675940713 1.5213608069 -0.0482498863 -0.5696096995 1.6958337120 1.1108714913 0.9675940713 It looks like it shifts all normal numbers by 1. The single numbers remain unchanged (which is good, I think).
Error 2
Uncommenting one line (i.e. calling rnd.rnorm(0.0, 1.0) once before setting the seeds) leads to the following output (with n = 4 or any other even number)
Testing some Uniform Numbers 0.7129553216 0.4284709250 0.6908848514 0.7191503089 0.7129553216 0.4284709250 0.6908848514 0.7191503089 Testing some Normal Numbers 0.9761557076 -0.5696096995 1.6958337120 1.1108714913 0.9675940713 -0.5696096995 1.6958337120 1.1108714913 Which, apparently, violates only the first normal random number, again leaving the uniform numbers in order.
Error 3
Using two points together (leaving the line uncommented and setting n to an odd number), I get this
Testing some Uniform Numbers 0.7129553216 0.4284709250 0.6908848514 0.7191503089 0.4911189328 0.7129553216 0.4284709250 0.6908848514 0.7191503089 0.4911189328 Testing some Normal Numbers -0.4553400276 -0.5696096995 1.6958337120 1.1108714913 0.9675940713 -0.5696096995 1.6958337120 1.1108714913 0.9675940713 1.5213608069 Now the second number of normal random numbers is shifted by one direction in another direction (lead).
System specification
I use this on Ubuntu 16.04 and g++ --version g++(Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Updates
It seems that it is not associated with a specific generator, i.e. replaces std::mt19937 re {}; on std:: linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647> re {}; , or using std::subtract_with_carry_engine<std::uint_fast64_t, 48, 5, 12> re{}; leads to the same behavior (but obviously with different numbers).
void setseed(int newSeed) { re.seed(newSeed); dud.reset(); // <---- dnd.reset(); }; Distributions have an internal state. You need to reset it in order to get the same sequence again.