How do C ++ 11 distributions of the <random> class convert a base generator?
The following code does not look intuitive:
#include <random> #include <iostream> using namespace std; int main() { mt19937 MyGenerator(40); auto gauss = normal_distribution<double>(0,1); auto linear = uniform_real_distribution<double>(0,1); cout << gauss(MyGenerator) << endl; //line a cout << linear(MyGenerator) << endl; //line b cout << gauss(MyGenerator) << endl; } Running this code gives the exit
-0.816097 0.705030 0.303032. If now the order of lines a and b is replaced, the output changes to
0.644008 0.338080 -0.639501. It is clear that the first two numbers are different from each other, since they are produced by different distributions. However, why is the third number different? In my intuition, the distribution should capture the number c = MyGenerator (), which then maps to a random number in a certain range. The random number generator will indicate the next number in the sequence of numbers after the distribution is called. So, should the result of the third call be the same in both cases?
Another observation: Adding a fourth call to any of the distributions really looks like playing the same numbers.
The libstdc ++ implementation of normal_distribution uses the polar Marsaglia method . The interesting thing about this method is that each pass uses two random numbers from URNG to generate two results.
Thus, the first call to the distribution calls URNG twice (perhaps more times, since it uses sampling, but an even number of times) and returns one result; the next distribution call will not call URNG, but will return the stored second result.
Here's the extract from the source code , slightly reformatted:
if (_M_saved_available) { _M_saved_available = false; ret = _M_saved; } else { result_type x, y, r2; do { x = result_type(2.0) * aurng() - 1.0; y = result_type(2.0) * aurng() - 1.0; r2 = x * x + y * y; } while (r2 > 1.0 || r2 == 0.0); const result_type mult = std::sqrt(-2 * std::log(r2) / r2); _M_saved = x * mult; _M_saved_available = true; ret = y * mult; } There is no requirement that the distribution call the base generator once for each value. Some distributions are best calculated by combining several random values.
For example, in the GNU implementation, the implementation of uniform distribution
return (__aurng() * (__p.b() - __p.a())) + __p.a(); __aurng generator __aurng once; while the kernel of the normal distribution:
do { __x = result_type(2.0) * __aurng() - 1.0; __y = result_type(2.0) * __aurng() - 1.0; __r2 = __x * __x + __y * __y; } while (__r2 > 1.0 || __r2 == 0.0); calling him at least twice.