C ++ 11类<random>的分布如何转换基础生成器?</random>

时间:2014-09-05 10:56:50

标签: c++ c++11 random

以下代码似乎没有直观的行为:

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

运行此代码会提供输出

-0.816097
 0.705030
 0.303032.

如果现在交换行a和b的顺序,则输出变为

 0.644008
 0.338080
-0.639501.

完全清楚的是,前两个数字现在是不同的,因为它们是由不同的分布产生的。不过,为什么第三个数字不同? 在我的直觉中,分布应该获取一个数字c = MyGenerator(),然后将其映射到特定范围内的随机数。随机数生成器将指向分发调用之后的数字序列中的下一个数字。那么,在这两种情况下,第三次调用的结果是否应该相同?

另一个观察: 事实上,向任一发行版添加第四次调用似乎都会重现相同的数字。

2 个答案:

答案 0 :(得分:13)

libstdc ++ normal_distribution的实现使用Marsaglia polar method。这个方法的有趣之处在于每个传递使用来自URNG的两个随机数来生成两个结果。

也就是说,对分配的第一次调用会调用URNG两次(可能更多次,因为它使用拒绝采样,但偶数次)并返回一个结果;以下对分配的调用不会调用URNG,但会返回保存的第二个结果。

这是一个extract from the source code,略有重新格式化:

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

答案 1 :(得分:9)

没有要求分发为每个值调用基础生成器一次。一些分布最好通过组合多个随机值来计算。

例如,在GNU实现中,统一分布的实现是

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

调用生成器__aurng一次;而正态分布的核心是:

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

至少调用两次。