C++ Primer第五版笔记——随机数

本文介绍了C++中如何使用新的随机数库生成不同分布的随机数。从随机数引擎到分布对象,再到如何生成均匀分布及非均匀分布的随机数,文章详细解释了这些过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

新标准前,C和C++都依赖与一个简单的C库函数rand来生成随机数。此函数生成均匀分布的伪随机整数,每个随机数在0和一个系统相关的最大值之间。rand函数有一些问题:有许多程序需要不同范围的随机数;一些应用需要随机生成浮点数;一些程序需要非均匀分布的数。
定义在头文件random中的随机数库通过一组协作的类来解决这些问题:随机数引擎类和随机数分步类

随机数引擎和分布
随机数引擎类是函数对象类(重载函数调用符的那种类),他们定义了一个调用运算符,该运算符不接受参数并返回一个随机unsigned整数。可以通过调用一个随机数引擎对象来生成原始随机数:

default_random_engine e;    
for(size_t i = 0;i<10;i++){
    cout<<e()<<endl;         //"调用"e()来生成随机数
}

标准库定义了多个随机数引擎类,区别在于性能和随机性质量的不同。每个编译器都会指定一个座位default_random_engine类型,此类型一般具有最常用的特性。
这里写图片描述
大多数场合,随机数引擎的输出是不能直接使用的,因为通常生成随机数的范围与需要的不符,而正确转换随机数的范围是很困难的。

分布类型和引擎
为得到一个指定范围内的数,我们使用一个分布类型的对象:

//生成0-9之间(包含)均匀分布的随机数
uniform_int_distribution<unsigned> u(0,9);
default_random_engine e;
for(size_t i = 0;i<10;i++){
    cout<<u(e)<<endl;    //没次调用返回指定范围内并均匀分布的值
}

类似随机数引擎类,分布类型也是函数对象类,分布类型定义一个调用运算符,接受一个引擎类做为参数,分布对象使用它的引擎参数生成随机数,并将其映射到指定的分布。

引擎生成一个数值序列
随机数发生器有一个特性:即使生成的数看起来是随机的,但对于一个给定的发生器,每次运行程序它都会返回一个相同的数值序列。因此如果希望能在多次调用的时候生成不同的序列,应该将其(包括引擎和分布对象)定义为static的。

设置随机数发生器种子
随机数发生器会生成相同的随机数序列这个特性其实在调试中很有用,但是一旦调试完成,通常还是希望每次运行程序能有不同的随机结果,可以通过提供一个种子来达到这一目的。种子是一个数值,引擎可以利用它在序列中一个新位置重新开始生成随机数。
为引擎设置种子有两种方式:在创建引擎对象时提供种子;调用引擎的seed成员:

default_random_engine e(12345);     //使用给定的种子值
e.seed(54321);                      //设置一个新种子值

选择一个好的种子也是十分困难的事情,可能最常用的方法是调用系统函数time,这个函数定义在头文件ctime中,time接受一个指针作为参数,它指向用于写入时间的数据结构,如果该指针为空,则函数简单的返回时间:

default_random_engine e(time(0));       //稍微随机些的种子

由于time返回以秒计的时间,因此这种方式只适合生成种子的间隔为秒级或更长的应用。


其他随机数分布
随机数引擎生成unsigned数,范围内的每个数被生成的概率都是相同的,而应用程序常常需要不同类型或是不同分布的随机数。标准库通过定义不同随机数分布对象来满足这两方面的需求。
生成随机实数
程序常需要一个随机浮点数的源,比如程序经常需要一个0-1之间的随机数。
一种方法是将调用rand()的结果除以RAND_MAX,即系统定义的rand可以生成的最大随机数的上界。这种方法的问题是随机整数的精度通常低于随机浮点数,这样,一些浮点值将永远不会被生成。
新标准下,可以更容易的获得随机浮点数。通过定义一个uniform_real_distribution类型的对象,并让标准库来处理随机整数得到随机浮点数的映射。与处理uniform_int_distribution一样,在定义对象时,指定最小值和最大值:

default_random_engine e;
//不同的分布类型,使得生成不同的结果     
uniform_real_distribution<double> u(0,1);
for(size_t i = 0;i<10;i++){
    cout<<u(e)<<endl;
}

使用分布的默认结果类型
每个分布模板都有一个默认模板实参,生成浮点值的分布类型默认生成double值,而生成整数的分布类型默认生成int值,由于分布类型只有一个模板参数,因此当希望使用默认随机数分布类型时要在模板名之后使用空尖括号。

生成非均匀分布的随机数
新报标准库的另一个优势就是可以生成非均匀分布的随机数。
例如想要生成一个正态分布的值的序列:

default_random_engine e;
//normal_distribution用于正态分布
normal_distribution<> n(4,1.5);     //均值4,标准差1.5
vector<unsigned> vals(9);           //初始化9个元素为0
for(size_t i = 0;i != 200;i++){
    unsigned v = lround(n(e));      //lround函数舍入到最近的整数
    if(v < vals.size()){
        ++vals[v];                  //统计每个数出现的次数
    }
}

以上函数尝试使用正态分布的随机分布类来生成200个数,如果正常的话,生成的数大约99%都在0-8(包含),而出现的次数与正太分布的图形也是对应的。

bernoulli_distribution类
有一个分布是接受模板参数,即bernoulli_distribution,因为他是一个普通类,而非模板。此分布总是返回一个bool值,它返回true的概率是一个固定值,默认为0.5:

default_random_engine e;
bernoulli_distribution b;   //默认是1/2的机会
bool answer = b(e);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值