简体   繁体   English

如何分发C ++ 11类 <random> 转换基础发电机?

[英]How do distributions of C++11 class <random> transform the underlying generator?

The following code does not seem to behave intuitively: 以下代码似乎没有直观的行为:

#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 output 运行此代码可提供输出

-0.816097
 0.705030
 0.303032.

If now the order of lines a and b is swapped, the output changes to 如果现在交换行a和b的顺序,则输出变为

 0.644008
 0.338080
-0.639501.

It is completely clear that the first two numbers are different now, as they are produced by different distributions. 很明显,前两个数字现在是不同的,因为它们是由不同的分布产生的。 Nevertheless, why is the third number different? 不过,为什么第三个数字不同? In my intuition, the distribution should grab a number c = MyGenerator() which is then mapped to the random number in the specific range. 在我的直觉中,分布应该获取一个数字c = MyGenerator(),然后将其映射到特定范围内的随机数。 The random number generator would point to the next number in the sequence of numbers after the distribution call. 随机数生成器将指向分发调用之后的数字序列中的下一个数字。 So, shouldn't the outcome of the third call be the same in both cases? 那么,在这两种情况下,第三次调用的结果不应该相同吗?

Another observation: Adding a forth call to either of the distributions does in fact seem to reproduce the same numbers. 另一个观察结果:对其中一个发行版添加第四个调用实际上似乎会重现相同的数字。

libstdc++'s implementation of normal_distribution uses the Marsaglia polar method . libstdc ++的normal_distribution实现使用Marsaglia极坐标方法 The interesting thing about this method is that each pass uses two random numbers from the URNG to generate two results. 关于这种方法的有趣之处在于每次传递使用来自URNG的两个随机数来生成两个结果。

That is, the first call to the distribution calls the URNG twice (possibly more times, as it uses rejection sampling, but an even number of times) and returns one result; 也就是说,对分配的第一次调用会调用URNG两次(可能更多次,因为它使用拒绝采样,但偶数次)并返回一个结果; the following call to the distribution will not call the URNG but will return the saved second result. 以下对分配的调用不会调用URNG,但会返回保存的第二个结果。

Here's an 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's no requirement that the distribution call the underlying generator once for each value. 对于每个值,分配不需要为基础生成器调用一次。 Some distributions are best calculated by combining multiple random values. 一些分布最好通过组合多个随机值来计算。

For example, in the GNU implementation, the implemantation of the uniform distribution is 例如,在GNU实现中,统一分布的实现是

return (__aurng() * (__p.b() - __p.a())) + __p.a();

calling the generator __aurng once; 调用生成器__aurng一次; while the core of the normal distribution is: 而正态分布的核心是:

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 it at least twice. 至少召唤两次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM