C ++中正态分布的随机数

Posted

技术标签:

【中文标题】C ++中正态分布的随机数【英文标题】:Random number from normal distribution in C++ 【发布时间】:2020-03-17 11:05:31 【问题描述】:

作为一个完整的 C++ 初学者,我想从正态分布中生成一个随机数。

使用以下代码(源自 post),我可以这样做:

#include <iostream>   
#include <boost/random.hpp>
#include <boost/random/normal_distribution.hpp>

using namespace std;

int main()

    boost::mt19937 rng(std::time(0)+getpid());
    boost::normal_distribution<> nd(0.0, 1.0);
    boost::variate_generator<boost::mt19937&,
                             boost::normal_distribution<> > rnorm(rng, nd);

    cout<< rnorm();
  return 0;

由于代码非常复杂(在我看来),我认为可能有更直接的解决方案:

#include <iostream>
#include <random>

using namespace std;

int main()
   
    default_random_engine generator;
    normal_distribution<double> distribution(0.0,1.0);

    cout << distribution(generator);
    return 0;

虽然我可以生成一个随机数,但它始终是相同的数字。 这就引出了两个问题:

(1) 为什么会这样,我该如何解决?

(2) 还有其他更简单的方法来生成随机数吗?

【问题讨论】:

好吧,我不熟悉这些函数,但是在 C 中,如果你不给 rand 函数提供种子值,它也会生成相同的值,因为算法不是随机的而是确定性的。因此,如果您从相同的种子值开始(如果没有它可能是 0),它将是相同的。 感谢所有快速回复!我确实打算了解会发生什么,而不仅仅是从其他地方复制和粘贴代码。例如,我不知道我必须向生成器提供种子。 在第一个版本中添加种子“std::time(0)+getpid())”,在第二个版本中不添加。无论哪种方式,它都是伪随机的,所以对于相同的种子,你会得到相同的“随机数”。在第二个你也可以调用 generator.seed(std::time(0)+getpid());效果与第一版类似 @user213544 你有 C++11 编译器吗?如果是这样,请使用标准头 &lt;random&gt; 而不是 &lt;boost/random.hpp&gt; 旁注:Why is “using namespace std;” considered bad practice? 【参考方案1】:

使用种子来初始化您的generator。这里我使用的是基于时间的种子。

#include <iostream>
#include <random>
#include <chrono>

using namespace std;

int main()

    unsigned seed = chrono::system_clock::now().time_since_epoch().count();
    default_random_engine generator(seed);
    normal_distribution<double> distribution(0.0, 1.0);

    cout << distribution(generator);
    return 0;

【讨论】:

为什么建议使用基于时间的种子而不是基于random_device 的种子? @TedLyngmo 极端案例 - MinGW 的 random_device 实现产生相同的确定性值,这使得它在这种情况下无用。因此上面的解决方案是优越的,但我不知道它是否适用于其他地方。 @Fureeish "一个值得注意的 std::random_device 是确定性的实现是 MinGW 的旧版本(错误 338,自 GCC 9.2 起已修复),尽管存在替代实现,例如 mingw-std- random_device。可以使用 MCF 线程模型从 GCC 下载最新的 MinGW 版本。" - 因此,与其调整代码以支持不符合标准的实现,不如尝试获得符合标准的实现,并且使用 MinGW,这是可能的。 :-) @user213544 default_random_engine 是实现定义的,所以很难给出一个通用的答案。如果我没记错的话,标准中的mt19937 被锁定为Mersenne Twister 的某个版本。我不确定boost 是否使用相同的实现。不过,PRNG:s 比标准中包含的更快更好(就生成的数据而言)。 @user213544 在您有性能要求之前,您不应该担心性能。【参考方案2】:

(1) 为什么会这样,我该如何解决?

发生这种情况是因为您默认构建 PRNG(伪随机数生成器)并且不为其播种。 PRNG 生成一个确定的数字序列。该序列通常很长,然后重新开始。种子用于设置 PRNG 的内部状态——可以说是它的起点。没有种子,它每次都会以相同的状态开始。

(2) 还有其他更简单的方法来生成随机数吗?

不,不使用现代标准 C++(C++11 及更高版本)。

一些注意事项:

使用基于单次时钟样本的基于时间的种子被认为是不好的,因为您冒着为两个 PRNG:s 设置相同值的风险。 您的程序中只需要一个 PRNG(每个线程需要生成随机数) - 播种 PRNG 被认为成本很高(在速度方面)。因此,您可以使生成器成为全局的,以便它可以在程序中的任何地方使用。下面是一个线程安全的版本,初始化应该是一个 True(但速度很慢)RNG,从熵池std::random_device 生成数字。
std::mt19937& prng()            // extern declared in a header file
    static thread_local std::mt19937 gen(std::random_device());
    return gen;

如果您的 random_device 缺少 entropy 或有问题(旧版本的 MinGW 有一个有问题的实现),您可以将 random_device 输出与一些基于时间的数字(间隔一段时间采样)结合起来创建一个 std::seed_seq用于初始化 PRNG。下面应该与有缺陷的和一致的实现一起工作,以创建一个难以预测的种子:

#include <chrono>
#include <thread>

// Create a seed_seq with 2 time based numbers and 2 random_device numbers.
// The two sleeps are done to ensure some diff in the clock counts.
static std::seed_seq get_seed() 
    static constexpr auto min = std::chrono::steady_clock::duration::min();
    std::random_device rd;
    std::uint_least32_t si[4];
    for(size_t s = 0; s < std::size(si);) 
        si[s++] = rd();
        std::this_thread::sleep_for(min);
        si[s++] = static_cast<std::uint_least32_t>(
            std::chrono::steady_clock::now().time_since_epoch().count());
        std::this_thread::sleep_for(min);
    
    return si[0], si[1], si[2], si[3];


std::mt19937& prng()            // extern declared in a header file
    static thread_local std::seed_seq seed = get_seed();
    static thread_local std::mt19937 gen(seed);
    return gen;

【讨论】:

嘿泰德。你能看看我关于正态分布的问题吗? ***.com/questions/70900624 @WhoCares “好”正态分布比我能轻松谈论的要复杂一些。 Lemire 有一种 uniform 分布公式,它的表现很好。我建议查看该领域人员的出版物以获得正确答案。它至少应该形成一个坚实的基础。 对不起,Lemire 是谁?

以上是关于C ++中正态分布的随机数的主要内容,如果未能解决你的问题,请参考以下文章

将 Z 分数(Z 值,标准分数)转换为 Python 中正态分布的 p 值

python:5种正态性检验方法

下侧分位数和上侧分位数是啥关系

复现文章所用链接。

从Blob中正确保存C#中的.mp4文件[关闭]

C内存共享进程通信范例