首页 文章

按照C / C中的正态分布生成随机数

提问于
浏览
105

有谁知道如何在C / C中正常分布后轻松生成随机数?

http://www.mathworks.com/access/helpdesk/help/toolbox/stats/normrnd.html

我不想使用任何Boost .

我知道Knuth详细谈论了这个问题,但我现在还没有他的书 .

17 回答

  • -1

    我创建了一个C++ open source project for normally distributed random number generation benchmark .

    它比较了几种算法,包括

    • 中心极限定理方法

    • Box-Muller变换

    • Marsaglia极地方法

    • Ziggurat算法

    • 逆变换采样方法 .

    • cpp11random 使用C 11 std::normal_distributionstd::minstd_rand (它实际上是clang中的Box-Muller变换) .

    iMac Corei5-3330S@2.70GHz,clang 6.1,64位的单精度( float )版本的结果:

    normaldistf

    为了正确,程序验证样品的平均值,标准偏差,偏度和峰度 . 结果发现,通过求和4,8或16个均匀数的CLT方法与其他方法一样没有良好的峰度 .

    Ziggurat算法比其他算法具有更好的性能 . 但是,它不适合SIMD并行,因为它需要表查找和分支 . 具有SSE2 / AVX指令集的Box-Muller比非SIMD版本的ziggurat算法快得多(x1.79,x2.99) .

    因此,我建议将Box-Muller用于具有SIMD指令集的架构,否则可能是ziggurat .


    附:基准测试使用最简单的LCG PRNG来生成均匀分布的随机数 . 因此,对某些应用程序来说可能还不够 . 但性能比较应该是公平的,因为所有实现都使用相同的PRNG,因此基准测试主要测试转换的性能 .

  • 42

    Box-Muller变换是常用的 . 这正确地生成具有正态分布的值 .

    http://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution

    http://en.wikipedia.org/wiki/Box_Muller_transform

    数学很容易 . 你生成两个统一的数字,从那些你得到两个正态分布的数字 . 返回一个,保存另一个以获取随机数的下一个请求 .

  • 20

    您可以使用GSL . 一些complete examples are given来演示如何使用它 .

  • -3

    我已经按照http://www.mathworks.com/help/stats/normal-distribution.html中给出的PDF的定义,并想出了这个:

    const double DBL_EPS_COMP = 1 - DBL_EPSILON; // DBL_EPSILON is defined in <limits.h>.
    inline double RandU() {
        return DBL_EPSILON + ((double) rand()/RAND_MAX);
    }
    inline double RandN2(double mu, double sigma) {
        return mu + (rand()%2 ? -1.0 : 1.0)*sigma*pow(-log(DBL_EPS_COMP*RandU()), 0.5);
    }
    inline double RandN() {
        return RandN2(0, 1.0);
    }
    

    这可能不是最好的方法,但它很简单 .

  • 11

    存在用于逆累积正态分布的各种算法 . 量化金融中最受欢迎的是http://chasethedevil.github.io/post/monte-carlo--inverse-cumulative-normal-distribution/

    IMO,没有太多的动机使用Wichura算法AS241以外的东西:它是机器精度,可靠和快速 . 高斯随机数生成中很少出现瓶颈 .

    此外,它显示了Ziggurat之类的方法的缺点 .

    这里的最佳答案主张Box-Müller,你应该知道它已经知道缺陷 . 我引用https://www.sciencedirect.com/science/article/pii/S0895717710005935

    在文献中,Box-Muller有时被认为略逊一筹,主要有两个原因 . 首先,如果将Box-Muller方法应用于来自坏线性同余生成器的数字,则转换后的数字会提供极差的空间覆盖 . 在许多书中都可以找到具有螺旋尾巴的变形数字图,最值得注意的是里普利的经典着作,他可能是第一个进行这种观察的书 .

  • 3

    Box-Muller实施:

    #include <cstdlib>
    #include <cmath>
    #include <ctime>
    #include <iostream>
    using namespace std;
     // return a uniformly distributed random number
    double RandomGenerator()
    {
      return ( (double)(rand()) + 1. )/( (double)(RAND_MAX) + 1. );
    }
     // return a normally distributed random number
    double normalRandom()
    {
      double y1=RandomGenerator();
      double y2=RandomGenerator();
      return cos(2*3.14*y2)*sqrt(-2.*log(y1));
    }
    
    int main(){
    double sigma = 82.;
    double Mi = 40.;
      for(int i=0;i<100;i++){
    double x = normalRandom()*sigma+Mi;
        cout << " x = " << x << endl;
      }
      return 0;
    }
    
  • 4

    看看:http://www.cplusplus.com/reference/random/normal_distribution/ . 这是产生正态分布的最简单方法 .

  • 3

    1)通过使用类似于蒙特卡罗方法的东西,您可以生成高斯随机数的图形直观方式 . 您可以使用C中的伪随机数生成器在高斯曲线周围的框中生成随机点 . 您可以使用分布公式计算该点是在高斯分布的内部还是下部 . 如果该点在高斯分布内,则您将高斯随机数作为该点的x值 .

    这种方法并不完美,因为从技术上讲,高斯曲线朝着无穷大方向前进,而你无法创建一个在x维度上接近无穷大的方框 . 但是高斯曲线在y维度上接近0非常快,所以我不担心 . C中变量大小的约束可能更多地限制了您的准确性 .

    2)另一种方法是使用中心极限定理,该定理指出当添加独立随机变量时,它们形成正态分布 . 记住这个定理,你可以通过添加大量的独立随机变量来近似高斯随机数 .

    这些方法并不是最实用的方法,但是当您不想使用预先存在的库时,这是可以预期的 . 请记住,这个答案来自于很少或没有微积分或统计经验的人 .

  • 26

    计算机是确定性设备 . 计算中没有随机性 . 此外,CPU中的算术设备可以在一些有限的整数数集上评估summ(在有限的整数中进行评估)字段)和有限的实数有理数 . 并且还执行按位操作 . 数学与[0.0,1.0]等无数个点的更好的集合达成协议 .

    您可以使用某些控制器在计算机内部监听某些电线,但是它会有均匀的分布吗?我不知道 . 但是如果假设它的信号是累积值大量独立随机变量的结果那么你会得到近似正态的分布随机变量(在概率论中证明了这一点)

    存在称为伪随机生成器的算法 . 当我试探伪随机生成器的目的是模仿随机性 . 和善的标准是: - 经验分布从理论上收敛(在某种意义上 - 逐点,统一,L2) - 从随机生成器得到的值似乎是相互依赖的 . 当然,从“真实的观点”来看并不是这样,但我们认为这是真的 .

    一种流行的方法 - 你可以用统一的分布来总结12 i.r.v ......但是在推导中心极限定理的过程中,在傅立叶变换,泰勒级数的帮助下,它需要连续n-> inf假设 . So for example theoreticaly - Personally I don't undersand how people perform summ of 12 i.r.v. with uniform distribution.

    我在大学里有可操作性理论 . 对我而言,这只是一个数学问题 . 在大学里,我看到了以下模型:


    double generateUniform(double a, double b)
    {
      return uniformGen.generateReal(a, b);
    }
    
    double generateRelei(double sigma)
    {
      return sigma * sqrt(-2 * log(1.0 - uniformGen.generateReal(0.0, 1.0 -kEps)));
    }
    double generateNorm(double m, double sigma)
    {
      double y2 = generateUniform(0.0, 2 * kPi);
      double y1 = generateRelei(1.0);
      double x1 = y1 * cos(y2);
      return sigma*x1 + m;
    }
    

    这样的方式只是一个例子,我想它存在另一种实现方式 .

    正确的证据可以在"Moscow, BMSTU, 2004: XVI Probability Theory, Example 6.12, p.246-247"中找到"Moscow, BMSTU, 2004: XVI Probability Theory, Example 6.12, p.246-247",ISBN 5-7038-2485-0

    不幸的是,我不知道这本书的翻译是否存在英文 .

  • 0

    如果您使用的是C 11,则可以使用std::normal_distribution

    #include <random>
    
    std::default_random_engine generator;
    std::normal_distribution<double> distribution(/*mean=*/0.0, /*stddev=*/1.0);
    
    double randomNumber = distribution(generator);
    

    您可以使用许多其他分布来转换随机数引擎的输出 .

  • -1

    使用 std::tr1::normal_distribution .

    std :: tr1命名空间不是boost的一部分 . 它是包含C技术报告1中的库添加的命名空间,可以在最新的Microsoft编译器和gcc中使用,与boost无关 .

  • 84

    看看我发现了什么 .

    library使用Ziggurat算法 .

  • 12

    这是在现代C编译器上生成样本的方法 .

    #include <random>
    ...
    std::mt19937 generator;
    double mean = 0.0;
    double stddev  = 1.0;
    std::normal_distribution<double> normal(mean, stddev);
    cerr << "Normal: " << normal(generator) << endl;
    
  • 0

    这是一个C示例,基于一些参考 . 这很快又很脏,你最好不要重新发明和使用升级库 .

    #include "math.h" // for RAND, and rand
    double sampleNormal() {
        double u = ((double) rand() / (RAND_MAX)) * 2 - 1;
        double v = ((double) rand() / (RAND_MAX)) * 2 - 1;
        double r = u * u + v * v;
        if (r == 0 || r > 1) return sampleNormal();
        double c = sqrt(-2 * log(r) / r);
        return u * c;
    }
    

    您可以使用QQ图来检查结果并查看其与实际正态分布的近似程度(对样本进行排序1..x,将等级转换为x总数的比例,即多少样本,得到z值并绘制它们 . 向上的直线是期望的结果) .

  • -1

    comp.lang.c FAQ列表共享三种不同的方法来轻松生成具有高斯分布的随机数 .

    你可以看看它:http://c-faq.com/lib/gaussian.html

  • 4

    编辑:自2011年8月12日起,我们有C++11直接提供std::normal_distribution,这就是我今天的方式 .

    这是最初的答案:

    以下是按复杂程度递增排序的一些解决方案 .

    • Add 12 uniform numbers 从0到1并减去6.这将匹配正常变量的均值和标准差 . 一个明显的缺点是范围限制在/ -6 - 与真正的正态分布不同 .

    • Box-Muller transform - 已在上面列出,并且实施起来相对简单 . 但是,如果您需要非常精确的样本,请注意Box-Muller变换与一些统一的生成器相结合会遇到称为Neave Effect的异常现象 .

    H. R. Neave,“关于使用具有乘法同余伪随机数发生器的Box-Muller变换”,Applied Statistics,22,92-97,1973

    • 对于 best precision 我建议 drawing uniforms and applying the inverse cumulative normal distribution 到达正态分布的变量 . 你可以找到一个非常好的逆累积正态分布算法

    https://web.archive.org/web/20151030215612/http://home.online.no/~pjacklam/notes/invnorm/

    希望有所帮助

    彼得

  • 14

    一种快速简便的方法是将一些均匀分布的随机数相加并取其平均值 . 有关其工作原理的完整说明,请参见Central Limit Theorem .

相关问题