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 编译器吗?如果是这样,请使用标准头<random>
而不是 <boost/random.hpp>
。
旁注: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 ++中正态分布的随机数的主要内容,如果未能解决你的问题,请参考以下文章