高斯随机数发生器

时间:2011-03-13 02:31:09

标签: c random gaussian taocp

我正在尝试在区间[0,1]中实现高斯分布式随机数生成器。

float rand_gauss (void) {
  float v1,v2,s;

  do {
    v1 = 2.0 * ((float) rand()/RAND_MAX) - 1;
    v2 = 2.0 * ((float) rand()/RAND_MAX) - 1;

    s = v1*v1 + v2*v2;
  } while ( s >= 1.0 );

  if (s == 0.0)
    return 0.0;
  else
    return (v1*sqrt(-2.0 * log(s) / s));
}

这是Knuth第二卷TAOCP第3版第122页中算法的直接实现。

问题是 rand_gauss()有时会返回区间[0,1]之外的值。

2 个答案:

答案 0 :(得分:8)

Knuth描述了TAOCP第二卷第122页的极性方法。该算法生成正态分布, mean = 0 标准差= 1 。但您可以通过乘以所需的标准偏差并添加所需的平均值来调整它。

您可能会觉得将代码与the polar method in the C-FAQ的其他实现进行比较会很有趣。

答案 1 :(得分:4)

将您的if语句更改为(s >= 1.0 || s == 0.0)。更好的是,使用break,如以下示例所示,生成返回复数对(u,v)的SIMD高斯随机数。这使用 Mersenne twister随机数生成器 dsfmt()。如果您只想要一个真实的随机数,则仅返回u并保存v以进行下一次传递。

inline static void randn(double *u, double *v)
{
double s, x, y;    // SIMD Marsaglia polar version for complex u and v
while (1){
   x = dsfmt_genrand_close_open(&dsfmt) - 1.; 
   y = dsfmt_genrand_close_open(&dsfmt) - 1.;
   s = x*x + y*y;
   if (s < 1) break;
}
 s = sqrt(-2.0*log(s)/s);
*u = x*s; *v = y*s;
return;
}

这种算法非常快。计算四个不同高斯随机数发生器的两个随机数(u,v)的执行时间为:

Times for delivering two Gaussian numbers (u + iv)
i7-2600K @ 4GHz, gcc -Wall -Ofast -msse2 ..

gsl_ziggurat                        =       20.3 (ns) 
Box-Muller                          =       78.8 (ns) 
Box-Muller with fast_sin fast_cos   =       28.1 (ns) 
SIMD Marsaglia polar                =       35.0 (ns)

Charles K. Garrett的fast_sin和fast_cos多项式例程使用cos()和sin()的嵌套多项式实现将Box-Muller计算加速2.9倍。 SIMD Box Muller和极地算法肯定具有竞争力。它们也可以轻松并行化。使用gcc -Ofast -S,汇编代码转储显示平方根是SIMD SSE2:sqrt - &gt; sqrtsd%xmm0,%xmm0

评论:使用gcc5获得准确的时间表确实很难和令人沮丧,但我认为这些都没问题:截至2016年2月3日:DLW

[1]相关链接:c malloc array pointer return in cython

[2]比较算法,但不一定适用于SIMD版本:http://www.doc.ic.ac.uk/~wl/papers/07/csur07dt.pdf

[3] Charles K. Garrett:http://krisgarrett.net/papers/l2approx.pdf