【问题标题】:Implementing the ratio of uniforms for normal distribution in C++在 C++ 中实现正态分布的均匀比例
【发布时间】:2012-10-11 16:13:01
【问题描述】:

我知道这可以通过定义泪珠形状并接受落在该区域内的点(来自统一生成器)来完成。

我试图在 C++ 中通过生成两个统一的随机数 x 和 y 来定位点 (x,y),然后检查该点是否在该区域内。

我的代码本身没有问题,但我的逻辑是否存在缺陷?我还没有找到合适的图形方式来检查这是否是真正的正态分布。

这是应该工作的代码:

typedef unsigned long long int Ullong;
typedef double Doub;

struct Normaldev : Ran {
    Doub mu,sig;

    Normaldev (Doub mmu, Doub ssig, Ullong i)
        : Ran (i), mu(mmu), sig(ssig){}

    Doub dev() {

      Doub u, v, x, y, q;
        do {
          u=Doub();
          v=1.7156*(Doub()-0.5);
          x=u-0.449871;
          y=abs(v)+0.386595;
          q=x*x+y*(0.19600*y-0.25472*x);
        } while(q>0.27597 && (q>0.27846 || v*v>-4*log(u)*u*u));

        return mu+sig*v/u;
     }
};

我用我对 C++ 的基本知识尽可能多地更改了数字食谱书中建议的代码,但 Ran 到底应该是什么?

【问题讨论】:

  • 通过夏皮罗测试检查分布是否正常。对于正态分布的 RoU 方法,在 Numerical Recipes 3rd edition 中有代码和数学。基本上,您使用边界椭圆来快速测试点 (x, y) 是否在区域内。
  • 我已经阅读了那本书中的相关页面tinyurl.com/9oh2nqg,但我不确定我是否很好地掌握了它背后的理论。代码在 C 中?
  • 代码是(坏的)C++。你不确定什么?从您最初的问题来看,在我看来您理解了该方法:只要 (x, y) 是泪滴区域的均匀样本,y/x 就是正态分布的。
  • 我想编码的方式是:启动时间种子,将两个随机数滚动为 x 和 y,并根据它们是否符合 x=cos(t) 和y=sin(t)*sin(t/2) 泪滴参数。

标签: c++ random normal-distribution


【解决方案1】:

请看第 3 版“C 中的数值食谱”,第 364-369 页。您会发现 Box-Muller 返回两个正态分布的随机变量,即“u”和“v”。所以第一次调用函数时,你计算两个变量但只返回'u'。该函数的第二次调用除了返回 'v' 什么都不做。

double  u, v;
double  sigma = 1.0
double  mean  = 0.0;
int     flag  = 0;

double boxMuller()
{
    if (flag == 1) {
        flag  = 0;
        return v * sigma + mean;
    }
    double help;
    do {
        u = Doub() - 0.5;
        v = Doub() - 0.5;
        help      = u * u + v * v;
    } while (help >= 0.25);
    help = sqrt( log( help * 4.0 ) / help * -2.0 );
    u   *= help;
    v   *= help;
    flag = 1;
    return u * sigma + mean;
}

相比之下,Ratio-of-Uniforms 方法必须在每次调用时(而不是每秒)计算一个新的随机变量。

所以我测量了时间,并更喜欢使用上面的 Box-Muller 代码。

【讨论】:

  • 速度真的取决于你的底层统一生成器。我每天都使用制服比例,并用 Xor-Shift 制服生成器对其进行基准测试,使其比 B-M 快得多。此外,当您绘制许多法线(例如从 10^9 开始)时,Box-Muller 会产生偏差,因为它会生成 ~ -6 到 6 之间的数字(如果您使用“标准”统一生成器)。
  • 我认为从 -6 到 +6 的范围限制是因为正方形。由于制服的比例也包含正方形,因此限制不应少。这里最好的解决方案可能是 Ziggurat 版本。
  • 顺便说一句,您可以通过使用例如改进该方法。在循环中查找 'u' 和 'v' 的 64 位无符号整数。要计算“帮助”,您可以将两个完全乘法的上半部分相加(128 位结果)。
【解决方案2】:

我用我对 C++ 的基本知识尽可能多地更改了数字食谱书中建议的代码,但 Ran 到底应该是什么?

Ran 是 NormalDev 的父类。它没有在您提供的代码中定义。根据代码,它似乎是一个非常通用的随机数类,在其构造函数中采用 unsigned long long int 种子。

【讨论】:

  • randd这里生成一个0到1的随机数?
  • 如帖子中所述。是的。
  • Box muller 在计算方面并不比 Ratio of Uniforms 好:它拒绝更一致的数字,每次都使用对数和平方根。
  • @AlexandreC。你是对的。我删除了帖子的后半部分。
  • 好吧,我现在使用 Box Muller 变换进行分布。它帮助我了解了更多关于正态分布和 C++ 的知识,但是当需要更多点时,它会明显变慢。
猜你喜欢
  • 2013-03-25
  • 2015-08-14
  • 2010-09-09
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
  • 2016-02-05
  • 2018-02-06
  • 1970-01-01
相关资源
最近更新 更多