需要一个正态分布的随机数发生器

时间:2018-06-17 21:52:04

标签: c++ normal-distribution

我是C ++的新手,但有数学背景。我正在尝试创建一个随机数生成器,它将在1.00和2.00之间吐出小数(美元金额),大致正态分布(因此平均值为1.50)。

现在,我从来没有尝试过这样的事情,也没有找到一个类似的问题,专门用正常分布的概率从一组数字中吐出一个元素。在这个模型中,1.50最有可能出现,1.00或2.00几乎没有机会出现。

写一篇p.d.f很容易。对于正态分布,其中均值= 1.50且3 * sigma = 0.5 - > sigma = 1/6(因此几乎所有数据都在1.00和2.00之间)。然而,不仅仅是不知道如何在这条曲线下整合101个区域(我不认为可以通过分析解析),它对我来说听起来并不高效。我知道C ++中有一个正常的分布函数。

有人可以帮我写一下这个评论吗?谢谢

3 个答案:

答案 0 :(得分:5)

C ++标准库有normal distribution class - 正是您所要求的。使用它 - 并将值剪切到最小值和最大值之间:

#include <algorithm> // for std::clamp()
#include <iostream>
#include <random>

int main() {
    std::random_device randomness_device{};
    std::mt19937 pseudorandom_generator{randomness_device()};

    auto mean = 1.5;
    auto std_dev = 0.5;
    auto min_allowed = 1.0;
    auto max_allowed = 2.0;
    std::normal_distribution<> distribution{mean, std_dev};
    auto sample = distribution(pseudorandom_generator);
    auto clamped = 
        // C++17 and later
        std::clamp(sample, min_allowed, max_allowed);
        // C++14 or earlier:
        // std::max(min_allowed,std::min(sample, max_allowed));
        // 

    std::cout 
        << "A value from a normal distribution with mean " << mean
        << " and standard deviation " << std_dev << ": "   << sample
        << "; when clamped to [" << min_allowed << ", " 
        << max_allowed << "], we get: " << clamped << "\n";
}

就分布而言 - 这会改变度量,使整个范围(-infinity,1)集中在1,类似地(2,无穷大)集中在2.作为评论者,建议,还有其他方法解释您对“近似正常”分布的请求,例如重新采样,直到您达到所需范围内的值;或者应用将(无穷大,无穷大)映射到(1,2)的连续变换,例如, x - &gt;反正切(X)。但你没有具体说明你究竟是在追求什么。

答案 1 :(得分:3)

添加到其他答案,您可以使用(伪)unform随机数生成器通过Polar method

创建正常的随机数生成器

示例代码:

#include <math.h>
#include <stdlib.h>

double
randn (double mu, double sigma)
{
  double U1, U2, W, mult;
  static double X1, X2;
  static int call = 0;

  if (call == 1)
    {
      call = !call;
      return (mu + sigma * (double) X2);
    }

  do
    {
      U1 = -1 + ((double) rand () / RAND_MAX) * 2;
      U2 = -1 + ((double) rand () / RAND_MAX) * 2;
      W = pow (U1, 2) + pow (U2, 2);
    }
  while (W >= 1 || W == 0);

  mult = sqrt ((-2 * log (W)) / W);
  X1 = U1 * mult;
  X2 = U2 * mult;

  call = !call;

  return (mu + sigma * (double) X1);
}

接下来,您可以使用它来获取应用程序的正态分布随机数。

答案 2 :(得分:2)

您可以使用std::normal_distribution<>

它会从double数据类型的整个范围生成样本,但您可以在您感兴趣的区间之外丢弃样本,并且您仍将接近具有三西格玛的正态分布。

#include <iostream>
#include <random>

using namespace std;

int main()
{
    auto mean   = 1.5,
         stddev = 1.0 / 6;

    // Create a normal distribution to pull samples from
    // The distribution has mean 1.5 and ~1/6 std dev
    random_device rd;
    mt19937_64 generator(rd());
    normal_distribution<> distribution(mean, stddev);

    cout << "some samples:" << endl;
    for (int i = 0; i < 10; ++i)
    {
        // Generate a sample.  The sample will be in (-infinity, infinity), so 
        // we throw away values that are outside of 3 std devs.
        // The distribution will no longer be normal, but close enough.
        double v;
        do
        {
            v = distribution(generator);
        } while (v < mean - 3 * stddev || v >= mean + 3 * stddev);

        cout << v << endl;
    }
    return 0;
}
some samples:
1.70539
1.49569
1.53731
1.42872
1.34029
1.54886
1.66154
1.54685
1.60833
1.36282