我最近了解到了一种在C++11中生成随机数的新方法,但是我无法理解我读到的关于它的文章(什么是那个引擎,类似于分布的数学术语,“产生的所有整数都是等可能的”)。
那么,有人可以解释一下:
- 它们究竟是什么?
- 它们的含义是什么?
- 如何生成它们?
- 它们是如何工作的?
- 等等
你可以把这些信息放在一个关于生成随机数的常见问题解答中。
这个问题太广泛了,无法给出完整的答案,但让我挑选一些有趣的点:
假设您有一个简单的随机数生成器,生成0、1、…、10这些数字时每个数字的概率相等(把它看作是经典的rand()
)。现在您想要的是一个在0、1、2范围内的随机数,且每个数字出现的概率相等。您的第一反应可能是使用 rand() % 3
。但是请稍等,余数为0和1出现的频率比余数为2的频率更高,因此这种方法是不正确的!
这就是为什么我们需要适当的分布,将一组均匀分布的随机整数转换成我们想要的分布,例如上面例子中的 Uniform[0,2]
。最好交给好的库去处理!
因此,在所有随机性的核心都是一个良好的伪随机数生成器,它生成一系列数字,这些数字在某个区间上服从均匀分布,并且理想情况下具有非常长的周期。标准实现的 rand()
并不经常是最好的,因此有多个选择很好。线性同余法和梅森旋转算法是两个很好的选择(LG实际上通常也被 rand()
使用);同样,最好让库来处理。
很简单:首先,设置一个引擎并对其进行种子化。种子完全确定了“随机”数字的整个序列,因此a)每次使用不同的种子(例如从/ dev / urandom
中获取),b)如果您希望重新创建一系列随机选择,则需要存储种子。
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
现在我们可以创建分布:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
...使用引擎生成随机数!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
更重要的一点是,与传统的 rand()
相比,更倾向于使用 <random>
的原因在于,现在很清楚和明显如何使随机数生成成为线程安全的:要么为每个线程提供其自己的、线程本地的 engine,种子为线程本地的种子; 要么同步访问 engine 对象。
result_type
,这是用于种子的正确整数类型。 我想我曾经有过一个有 bug 的实现,强制我在 x64 上将 std::mt19937
的种子强制为 uint32_t
,最终应该修复这个问题,你可以说 MyRNG::result_type seed_val
,从而使 engine 可以非常容易地替换。std::random_device
而非/dev/urandom
。 - Cubbistd::random_device
的示例:http://en.cppreference.com/w/cpp/numeric/random/random_device。 - W.K.S
rand
生成的平坦分布更高级的人群,您应该快速查看维基百科上的一些基本统计和RNG概念,否则很难向您解释<random>
的合理性以及其各种组件的使用方法。 - Matteo Italia