《白话C++》第10章 Page20 10.2.2 随机数

rand():

计算机编程很需要随机功能,但计算机能够提供真实的随机概率吗?通过C语言的函数rand(),可以方便地获得“伪随机”数,比如:

int a = rand();

在调用rand()之前,还需要先调用srand(int seed),作用是“种下一颗随机种子”,“种子”就是入参seed,也是一个整数。播种之后,C运行库就会依据该种子的指,生成一系列的数值,而后程序第一次调用rand(),就得到该系列中的第一个数,第二次调用就得到系列中的第二个数……想象一下,如果程序存在并发,自然容易发生分不清谁是第几次的情况,也就会存在某两个并发几乎“同时”调用了rand(),于是得到了相同的两个随机数的情况。

更“伪”的事情时:如果两次调用srand(rand)中下的种子是一样的,则所产生两个系列数,将一一对应,完全相同。

连续运行连词,可以看到种子相同,得到随机数完全相同,这很不随机呀。种子不同,得到的随机数系列也不同,原来一开始,这一切变化来自最开始的那个种子。

能不能让“种子数”本身也随机呢?这除非有个硬件设备,比如可以采集周边光线、噪声、WIFI强度、微博头条等等随机数据,以便在每次运行时,生成一个真正的随机数,否则软件模拟的结果,“伪随机”就是“伪随机”。常见的做法是,取当前时间作为作为随机数种子,毕竟时间一直在变,前例的代码改变如下:

再运行两次,两次的结果不一样了。看起来很完美,但相当多的C/C++程序员被这个方法坑过,因为在并发时,很可能会有两个或更多线程在同一秒内调用time()函数得到的时间是一样的,可机器的CPU的计算能力随便就死3GHz,还多核,理论值上,一毫秒之内调用一百万次time()得到的时间全相同。我们用单一线程就可以模拟出类似情况,比如循环100次调用time():

得到的时间是距离1970年1月1日零点零分零秒的秒数。

random_device:

C++标准委员会在新标准里提供了一个名为“random_device”的类。

注意这次有两层循环,内层循环输出了5个随机数,外层循环负责同样的事情做两遍,所以测试时不需要运行程序两次了。

外层循环中,定义了random_device类的一个变量r,内层循环就用它产生随机数,一个变量直接挂一对括号,这是“函数对象”,random_device应该是针对“()”做了操作符重载,连续调5次,产生5个随机数。在Windows下,两次调用果真产生了相同的系列,

在Ubuntu Linux下,效果大不一样,

速度比较快,不是重点,重点是在Linux下,两个随机系列完美的不相同!Linux提供了良好的随机数设备模拟,其原理是利用系统当前的“熵”进行模拟。“熵”指系统整体熵的信息混乱度,比如内存剩余多少,当前有几个进程,磁盘上最后一次读出的内容是什么,键盘刚刚哪个键被按下等等的变化数据,以某种算法计算出一个随机种子。

Windows其实也提供类似功能,却没有绑定到当前使用的C/C++运行库。结果是“random_device”仍然在取时间。

在Linux下,每次都需要直接访问操作系统计算熵值,会比较慢(甚至可能堵塞),为此我们可以只向“random_device”要一次数据,然后以此为种子,再使用各种数学算法计算出随机数系列:

注意,random_device变量更名为“rd”,用它所产生的随机数,被用于mt19937对象“r”的构造入参“r”将被作为种子以代号“mt19937”为代表的算法,生成一系列随机数。

【小提示】:mt19937随机算法

mt19937伪随机数产生算法,具有实现简易、占用内存少、速度高、所产生的随机数分布均衡等优点,因此被广泛使用。更多解释请上网查阅“MersenseTwister”百科

以上例子中产生的随机数都或大或小,如果希望所产生的随机数值被控制再一定范围内,可以使用“求余”操作。比如一个“摇骰子”的游戏,每个骰子所产生的数必须在1到6之间:

int v = (rand() % 6) + 1;

【课堂作业】:使用C++11标准,写一段猜随机数

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值