C++ C++;11级<;随机>;转换底层生成器?
以下代码的行为似乎并不直观:C++ C++;11级<;随机>;转换底层生成器?,c++,c++11,random,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); cou
#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;
}
如果现在交换行a和b的顺序,则输出更改为
0.644008
0.338080
-0.639501.
很明显,前两个数字现在不同了,因为它们是由不同的分布产生的。然而,为什么第三个数字不同呢?
根据我的直觉,分布应该获取一个数字c=MyGenerator(),然后映射到特定范围内的随机数。随机数生成器将指向分发调用之后的数字序列中的下一个数字。那么,在这两种情况下,第三次通话的结果不应该是相同的吗
另一项意见:
向这两个发行版中的任何一个添加第四个调用实际上似乎复制了相同的数字。libstdc++的
normal\u发行版的实现使用了。这种方法的有趣之处在于,每次传递都使用来自URNG的两个随机数来生成两个结果
也就是说,对分发的第一个调用调用URNG两次(可能多次,因为它使用拒绝采样,但次数为偶数),并返回一个结果;以下对分发的调用不会调用URNG,但会返回保存的第二个结果
下面是一个稍微重新格式化的示例:
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;
}
不要求发行版为每个值调用一次底层生成器。某些分布最好通过组合多个随机值来计算
例如,在GNU实现中,统一分布的实现是
return (__aurng() * (__p.b() - __p.a())) + __p.a();
调用生成器一次;而正态分布的核心是:
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);
至少打两次电话。。。。而且呼叫的数量是可变的。所以可能是normal+uniform对底层生成器进行了三次调用,而uniform+normal进行了五到七次调用。我发现aurng()
总是返回0,这导致GCC 6.1.0中的循环无限。@LiDong,这不应该发生;这表明你的骨灰盒坏了。我问了另一个问题
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);