【C++11】随机数引擎与随机数类

随机数引擎与伪随机数

C++11新标准中,使用default_random_engine类生成随机数引擎,作为其它随机数类的基础,以生成各种符合要求的随机数。注意:相关操作需引入random头文件。

随机数引擎是一个重载了函数调用运算符的函数对象,工作原理可以理解为(并非实际工作原理):生成一个伪随机序列,调用时依次取出序列中的下一个数字。

可以直接调用随机数引擎对象获取伪随机数:

int t{4};
// 默认初始化引擎并进行4次调用
while(t--) {
	// 获取随机数引擎
    default_random_engine e;
    for (int i = 0; i < 8; ++i) {
        cout << e() << " ";
    }
    cout << "\n---------------\n";
}

之所以称为伪随机数,是因为多次调用间生成的序列是一致的:
在这里插入图片描述

需要通过设置随机数种子等多种方式来逼近"真随机数"。

获取“真随机数”

这里所说的真随机数并非严格意义上的真随机,而是单纯的不希望每一次程序运行的随机数都是相同的。

注意:其它随机数类间接使用随机数引擎生成随机数,保证随机数引擎逼近“真随机”即可保证其它随机数类逼近“真随机”。

静态随机数引擎

可以注意到,同一个随机数引擎生成的随机序列是可以称为“真随机数”的,因为从原理的角度来看:其在调用时依次取出序列中的下一个数字。

因此可以使用static关键字使程序运行期间使用同一个随机数引擎,那么所生成的随机数就是不断从随机序列中取出的。

static default_random_engine e;
for (int i = 0; i < 20; ++i) {
    cout << e() << " ";
}
随机数种子

在初始化随机数引擎时,传入一个无符号整数以达到改变函数对象状态的目的,可以生成不同的随机数(注意:默认初始化时,随机数引擎的随机数种子为1)。但是,随机数种子相同的引擎,仍会生成相同的随机数序列,这也解释了多个默认初始化的引擎生成序列相同。

为了保证传入的无符号整数不同,通常会使用获取系统时间(通常获取秒)来作为随机数种子。注意:获取系统时间需包含ctime头文件。

// 使用系统时间(秒)做随机数种子
default_random_engine e(time(0));
for (int i = 0; i < 20; ++i) {
    cout << e() << " ";
}

但是又有新的问题,不同时刻的秒数是不同的,但同一秒内多次调用此程序所对应的系统秒是相同的。且由于,程序调用时间长短问题,即便获取系统毫秒值也会发生同一毫秒内多次调用的问题。

同一秒内多次调用造成伪随机:

void test() {
    // 使用系统时间(秒)做随机数种子
    default_random_engine e(time(0));
    for (int i = 0; i < 8; ++i) {
        cout << e() << " ";
    }
    cout << "\n---------------\n";
}

int main()
{
    int t{4};
    // 连续调用4次
    while(t--) {
        test();
    }
    return 0;
}

在这里插入图片描述

std::random_device

随机数种子容易出现秒级及秒级以上可用的局面,但是程序的调用常常小于或等于毫秒级,也就是那怕使用系统的毫秒时间也没用。

但是,如果有一个方法可以因不同调用而获取的数字不同,那么以其作为随机数种子就可以打破上述尴尬局面。

聪明的读者一定会注意到,因不同调用而获取的数字不同不就是获取了一个随机数嘛。对,使用一个真随机数作为随机数引擎的随机数种子。

???。。。

为什么?因为获取真随机数是非常耗费系统资源的,需要注意的是,那怕获取一个伪随机数也是异常耗费系统资源的。如此,使用一个真随机数来初始化一个随机数引擎,以使多个引擎获取的随机数逼近真随机数的操作便很好理解了。

标准库提供了random_device类,来生成random_device对象用来获取真随机数。再次强调,非常耗费系统资源。

注意:部分编译器是不支持此操作的,即random_device对象用来所获取的随机数也为伪随机数。因此,在使用前应使用entropy()成员方法进行检验:

random_device rd;
/* 返回bool值true表示当前编译器支持random_device
获取真随机数 */
cout << rd.entropy() << " ";

entropy()方法检验通过就可以生成“真随机数”了:

random_device rd;
// rd()每次调用消耗系统资源返回不同的数字
default_random_engine e(rd());

for (int i = 0; i < 8; ++i) {
    cout << e() << " ";
}
服从均匀分布的整型随机数

uniform_int_distribution为服从均匀分布的整数随机数模板类,可以在构造时传递参数,表示生成随机数的区间,注意区间为左右均闭的闭区间。

static default_random_engine e;

/* 服从均匀分布的整数随机数类,
注意区间为左右均闭的闭区间 */
uniform_int_distribution<int> rint(0, 1);

for (int i = 0; i < 8; ++i) {
    /* 注意:传递的是e而不是e(),
    因为e()是一个数字 */ 
    cout << rint(e) << " ";
}
服从均匀分布的实型随机数

uniform_real_distribution为服从均匀分布的实数随机数模板类,可以在构造时传递参数,表示生成随机数的区间,注意区间为左右均闭的闭区间。

static default_random_engine e;

/* 服从均匀分布的实数随机数类,
注意区间为左右均闭的闭区间 */
uniform_real_distribution<double> real(0, 1);

for (int i = 0; i < 8; ++i) {
    /* 注意:传递的是e而不是e(),
    因为e()是一个数字 */ 
    cout << real(e) << " ";
}
服从标准正态分布的随机数

normal_distribution为服从标准正态分布的实数随机数模板类,其中第一个参数为均值,第二个参数为标准差。

static default_random_engine e;

/* 服从标准正态分布的实数随机数类,
注意第一个参数为均值,第二个参数为标准差 */
normal_distribution<double> norm(4, 1.5);

for (int i = 0; i < 8; ++i) {
    /* 注意:传递的是e而不是e(),
    因为e()是一个数字 */ 
    cout << norm(e) << " ";
}
服从二项分布的随机结果

bernoulli_distribution为服从bernoulli分布(01分布)的类,返回bool值表示本次测试结果为1或0,可以在构造时传参数修改true的概率,01分布默认true概率为0.5。

static default_random_engine e;

/* 服从01分布的类,返回bool值表示本次测试为1或0
可以在构造时传参数修改true的概率,01分布默认
true概率为0.5 */
bernoulli_distribution bern(0.6);

for (int i = 0; i < 8; ++i) {
    /* 注意:传递的是e而不是e(),
    因为e()是一个数字 */ 
    cout << bern(e) << " ";
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sophon、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值