1、介绍
在C++11新标准之前, 我们程序都是依赖于一个简单的C库函数rand来生成随机数。此函数生成均匀分布的伪随机数,每个随机数的范围在0和一个系统相关的最大值之间。
C++11的头文件random中有一组随机数库通过一组协作的类来解决这些问题:随机数引擎类和随机数分布类。
2、随机数引擎和分布的使用
随机数引擎的操作:
Engine e; //默认构造函数,使用该引擎类型默认的种子
Engine e(s); //使用整型值s作为种子
e.seed(s); //使用种子s重置引擎的状态
e.min(); //此引擎可生成的最小值
e.max(); //此引擎可生成的最大值
Engine::result_type; //此引擎生成的unsigned整型类型
e.discard(u); //将引擎推进u步; u的类型为unsigned long long
调用随机数引擎的原始随机数:
default_random_engine dre;
for (int i = 0; i < 10; ++i)
cout << dre() << endl;
大多数场合下随机数引擎输出的原始随机数是不能直接使用的,需要进行转换。
使用分布类型和引擎,生成0到9之间均匀分布的随机数
default_random_engine dre;
uniform_int_distribution uidu(0, 9);
for (int i = 0; i < 10; ++i)
cout << uidu(dre) << endl;
上面的uniform_int_distribution
随机数引擎有一个序列不变的特性,在调试的时候非常有用,但是要是不注意的话代码的结果便会出错。看下面的例子:
vector bad_randvec()
{
default_random_engine dre;
uniform_int_distribution uidu(0,9);
vector ret;
for (size_t i = 0; i < 10; ++i)
ret.push_back(uidu(dre));
return ret;
}
上面的代码看起来是返回一个10个随机数的vector,但是实际上在每次调用的时候,返回值都是一样的。正确写法应该是将分布和引擎设置为static。
vector bad_randvec()
{
static default_random_engine dre;
static uniform_int_distribution uidu(0,9);
vector ret;
for (size_t i = 0; i < 10; ++i)
ret.push_back(uidu(dre));
return ret;
}
这样每次输出的结果便是随机的了。
设置随机数发生器的种子
default_random_engine dre1(123);
default_random_engine dre2;
dre2.seed(time(0));
for (int i = 0; i < 10; ++i)
cout << dre1() << ' ';
cout << endl;
for (int i = 0; i < 10; ++i)
cout << dre2() << ' ';
cout << endl;
其中dre1使用123作为随机数种子,dre2使用默认的随机数种子,而dre2.seed(time(0))是使用时间为随机数种子,使得随机数种子不是固定的。
生成随机实数
很多时候我们需要生成0到1之间的随机浮点数,我们可以使用生成实数的分布。
default_random_engine dre3;
uniform_real_distribution urdd(0, 1); //<>中可以是空,即使用默认模板参数,默认为double
for (int i = 0; i < 10; ++i)
cout << urdd(dre3) << ' ';
cout << endl;
分布类型d的常用函数还有以下几个:
d.min(); //返回d(e)能生成的最小值
d.max(); //返回d(e)能生成的最大值
d.reset(); //重建d的状态,使得随后对d的使用不依赖于d已经生成的值
生成非均匀分布的随机数
除了均匀分布之外,我们往往还需要用到一些非均匀分布的随机数,如正态分布。
default_random_engine dre4;
normal_distribution<> nd(4, 1.5);//均值为4,标准差为1.5
vector vals(9);
for (size_t i = 0; i < 200; ++i)
{
unsigned val = lround(nd(dre4));
if (val < vals.size())
++vals[val];
}
for (auto e : vals)
{
cout << string(e, '*') << endl;
}
程序输出如下:
***
***********
******************
********************************************
*******************************************************
********************************
******************************
*****
*
可以看到输出的结果是大致符合正态分布,但是并不是完全对称的,如果是完全对称反倒证明这个随机数生成器的性能应该不好。
伯努利分布
随机数中还支持生成伯努利分布的随机数。声明和调用的形式与前述的分布和引擎相似。
default_random_engine e;
bernoulli_distribution b1; //默认是50 / 50 的机会,
/*TODO*/
伯努利分布还可以调整概率:
default_random_engine e;
bernoulli_distribution b2(.60); //概率为60 / 40 的机会,
/*TODO*/
总结
C++11新标准中的定义的随机数的分布有20种,本文只是介绍了比较常用的几种,新标准引入的不同分布的随机数,为程序的编写带来了更多的方便,可以舍弃原有C的rand函数了。