prev up next   top/contents search

comp.lang.c FAQ 列表· 问题 13.20

我如何生成具有正态或高斯分布的随机数?


有许多方法可以做到这一点。

  1. 利用中心极限定理(“大数定律”)并累加几个均匀分布的随机数
    #include <stdlib.h>
    #include <math.h>
    
    #define NSUM 25
    
    double gaussrand()
    {
    	double x = 0;
    	int i;
    	for(i = 0; i < NSUM; i++)
    		x += (double)rand() / RAND_MAX;
    
    	x -= NSUM / 2.0;
    	x /= sqrt(NSUM / 12.0);
    
    	return x;
    }
    
    (但请注意sqrt(NSUM / 12.)修正,尽管很容易无意中忽略,尤其是在NSUM为 12 时。)
  2. 使用 Abramowitz 和 Stegun 描述的方法
    #include <stdlib.h>
    #include <math.h>
    
    #define PI 3.141592654
    
    double gaussrand()
    {
    	static double U, V;
    	static int phase = 0;
    	double Z;
    
    	if(phase == 0) {
    		U = (rand() + 1.) / (RAND_MAX + 2.);
    		V = rand() / (RAND_MAX + 1.);
    		Z = sqrt(-2 * log(U)) * sin(2 * PI * V);
    	} else
    		Z = sqrt(-2 * log(U)) * cos(2 * PI * V);
    
    	phase = 1 - phase;
    
    	return Z;
    }
    
  3. 使用 Knuth 讨论的方法,该方法最初归功于 Marsaglia
    #include <stdlib.h>
    #include <math.h>
    
    double gaussrand()
    {
    	static double V1, V2, S;
    	static int phase = 0;
    	double X;
    
    	if(phase == 0) {
    		do {
    			double U1 = (double)rand() / RAND_MAX;
    			double U2 = (double)rand() / RAND_MAX;
    
    			V1 = 2 * U1 - 1;
    			V2 = 2 * U2 - 1;
    			S = V1 * V1 + V2 * V2;
    			} while(S >= 1 || S == 0);
    
    		X = V1 * sqrt(-2 * log(S) / S);
    	} else
    		X = V2 * sqrt(-2 * log(S) / S);
    
    	phase = 1 - phase;
    
    	return X;
    }
    
所有这些方法都生成均值为 0、标准差为 1 的数字。(要调整到其他分布,请乘以标准差并加上均值。)方法 1 在“尾部”表现不佳(尤其是在NSUM较小的时候),但方法 2 和 3 的表现相当好。有关更多信息,请参阅参考文献。

额外链接

参考文献:Knuth 第 3.4.1 节 p. 117
Box 和 Muller,《关于生成随机正态偏差的注记》
Marsaglia 和 Bray,“生成正态变量的便捷方法”
Abramowitz 和 Stegun,《数学函数手册》
Press 等,《C 语言数值导论》第 7.2 节 pp. 288-290


prev up next   contents search
关于此 FAQ 列表   关于 Eskimo   搜索   反馈   版权

Eskimo North 托管