随机数在测试、游戏、仿真和安全等很多问题中都非常有用。为了适应各种各样的应用需求,标准库在<random>中提供了很多种不同的随机数生成器。随机数生成器包括两部分:
【1】一个引擎( engine),负责生成一组随机值或者伪随机值。
【2】一种分布( distribution),负责把引擎产生的值映射到某个数学分布上。
常用的分布包括 uniform_int_distribution(生成的所有整数概率相等)、 normal_distribution
(正态分布,又名“铃铛曲线”)和 exponential_distribution(指数增长),它们的范围各不相同。例如:
using my_engine default_random_engine; //引擎类型using my_distribution= uniform_int_distribution<>; //分布类型my_engine re{}; //默认引擎my_distribution one_to_six {1,6}; //该分布把随机数映射到1~6的范围auto die=bind(one_to_six,re); //得到一个随机数生成器int x= die(); //掷骰子:x得到的值位于1~6之间
标准库函数bind()生成一个函数对象,它会把第2个参数(re)作为实参绑定到第一个参数( one_to_six函数对象)的调用中(见33.5.1节)。因此,调用die()等价于调用one_to_six(``re``).
在设计和实现标准库随机数组件时,我们非常注重它的泛化能力和性能,因此曾经有位专家把它评价为“实现随机数库的榜样和标杆”。不过它对于入门级的程序员来说稍显繁杂且不够友好。在上面的代码中,我们用 using语句稍微解释了一下生成随机数的过程,当然也可以写成下面的形式
auto die= bind(uniform_int_distribution<>(1,6), default_random_engine{})
至于说哪个版本更易读完全取决于代码上下文和读者自己的感觉。
对于初学者来说,完整版本的随机数生成器显得有些过于正式且不易使用,有一个简易版本可作为替代。例如:
Rand_int rnd{1,10}; //构建一个随机数生成器,生成1~10之间的随机数int x=rnd(); //x是1~10之间的随机数
(推荐使用)我们该如何得到这个新版本的随机数生成器呢?显然必须在 Rand int类的内部实现与die()类似的功能才行:
class Rand_int{public:Rand_int(int low, int high):dist(low, high){}int operator()(return dist{re};)private:default_random_engine re;uniform_int_distribution<> dist;};
Rand_int()的定义仍然是“专家级的”,不过使用起来已经变得很容易了,甚至初学者在C++课程的第一周就能学会使用它。例如:
int main(){Rand_int rnd{0,4}; //创建一个随机数生成器vector<int> histogram(5); //构建一个相应尺寸的vectorfor(int i=0;i!=200;++i){++histogram[rnd()]; //用[0:4]之间的每个数字出现的次数填充histogram}for(int i=0;i!=mn.size();++i){cout<<i<<'\t';for(int j=0;j!=mn[i];++j)cout<<'*';cout<<endl;}}
输出结果是如下所示的一个均匀分布(统计差异在合理范围之内):
0 ****************************************1 ************************************2 *********************************************3 *************************************4 ******************************************
因为C++没有标准图形库,所以我们使用了“ASCI图形”。众所周知,有很多为C++设计的开源或者商业的GUI库,但是在本书中我们尽量只使用ISO标准之内的功能。
关于随机数的更多内容请参阅40.7节。
