【问题标题】:Need a normally-distributed random number generator需要一个正态分布的随机数生成器
【发布时间】:2018-06-17 21:52:03
【问题描述】:

我是 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 之间)。然而,不仅仅是不知道如何用 C++ 将这条曲线下的 101 个区域(我认为无法解析解决)集成,这对我来说听起来并不高效。我知道 C++ 中有一个正态分布函数。

有人可以用 cmets 帮我写出来吗?谢谢

【问题讨论】:

    标签: c++ normal-distribution


    【解决方案1】:

    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,infinity) 集中在 2。正如评论者所建议的那样,还有其他方法解释您对“近似正态”分布的要求,例如重新采样,直到您达到所需范围内的值;或应用将(无穷大,无穷大)映射到(1,2)的连续变换,例如x -> arctan(x)。但你没有具体说明你追求的是什么。

    【讨论】:

    • 如果您修改分布以在 [1,2] 上产生实际概率度量,此答案会更有用。
    • en.cppreference.com/w/cpp/algorithm/clamp 在这里很有用。但是,将所有内容折叠到边界中可能是一个不太理想的近似值,而不是重新滚动直到您开始时达到正确的间隔。
    • @BaummitAugen:哦,我还没有意识到 C++17 中出现了“clamp”。至于分布,这取决于 OP 实际想要实现的目标。
    • 当然。这只是我对“近似正态分布”的解释,我怀疑不应将正概率分配给点。不过,我同意这个问题的定义不明确。
    • 应该是std::mt19937 pseudorandom_generator{rd()};
    【解决方案2】:

    除了其他答案之外,您可以使用(伪)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);
    }
    

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

    【讨论】:

      【解决方案3】:

      您可以为此使用std::normal_distribution&lt;&gt;

      它从double 数据类型的全部范围内生成样本,但您可以丢弃您感兴趣的区间之外的样本,您仍将接近具有 3 sigma 的正态分布。

      #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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-06
        • 1970-01-01
        • 2016-07-07
        • 2011-01-16
        • 2014-07-26
        • 2016-09-21
        • 2018-06-22
        • 1970-01-01
        相关资源
        最近更新 更多