11

I have a simple code sample:

#include <iostream>
#include <random>
using namespace std;
int main() {
    minstd_rand0 gen(1);
    uniform_real_distribution<double> dist(0.0, 1.0);
    for(int i = 0; i < 10; ++i) {
        cout << "1 " << dist(gen) << endl;
    }

    normal_distribution<double> dist2(0.0, 1.0);
    minstd_rand0 gen2(1);
    for(int i = 0; i < 10; ++i) {
        cout << "2 " << dist2(gen2) << endl;
    }

    return 0;
}

Which I compile on gcc and msvc. I get diferent results on std code!( enter image description here

So why GCC and MSVC std::normal_distribution results are diferent for the same seed and generator, and, most importantly, how to force them to be same?

Cœur
  • 37,241
  • 25
  • 195
  • 267
DuckQueen
  • 772
  • 10
  • 62
  • 134
  • 13
    Fascinating. I would have expected them to have 100% different or 100% identical numbers. The fact they're the same but rearranged blows my mind. – Mooing Duck Jul 22 '16 at 18:16
  • 2
    And `uniform_real_distribution` results are as expected the same! – DuckQueen Jul 22 '16 at 18:17
  • 3
    @MooingDuck: The standard algorithm here produces two values each iteration. It looks like they both use the same implementation of the algorithm, but just differ on which of the two values is returned and which is cached. –  Jul 22 '16 at 18:32
  • 2
    They probably both use Knuth's algorithm (vol2, page 122, algo P). Which repeatedly generates 2 random numbers until the constraints are met. So you have two good numbers, you return one of them and keep the other for the next call. You can guess the rest. – Hans Passant Jul 22 '16 at 18:37
  • 1
    Interestingly enough, clang++ follows MSVC – pyCthon Jul 22 '16 at 18:40

2 Answers2

6

Unlike the PRN generators defined by the standard that must produce the same output for the same seed the standard does not keep that mandate for distrobutions. From [rand.dist.general]/3

The algorithms for producing each of the specified distributions are implementation-defined.

So In this case even though the distribution has to have a density function in the form of

enter image description here

How the implementation does that is up to them.

The only way to get a portable distribution would be to write one yourself or use a third party library.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
5

It's problematic, but the standard unfortunately does not specify in detail what algorithm to use when constructing (many) of the randomly distributed numbers, and there are several valid alternatives, with different benefits.

26.6.8.5 Normal distributions [rand.dist.norm] 26.6.8.5.1 Class template normal_distribution [rand.dist.norm.normal]

A normal_distribution random number distribution produces random numbers x distributed according to the probability density function

enter image description here

parameters μ and are also known as this distribution’s mean and standard deviation .

The most common algorithm for generating normally distributed numbers is Box-Muller, but even with that algorithm there are options and variations.

The freedom is even explicitly mentioned in the standard:

26.6.8 Random number distribution class templates [rand.dist] . . .

3 The algorithms for producing each of the specified distributions are implementation-defined.

A goto option for this is boost random

By the way, as @Hurkyl points out: It seems that the two implementations are actually the same: For example box-muller generates pairs of values, of which one is returned and one is cached. The two implementations differ only in which of the values is returned.

Further, the random number engines are completely specified and will give the same sequence between implementations, but care does need to be taken since the different distributions can also consume different amounts of random data in order to produce their results, which will put the engines out of sync.

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97