前言
之前看到过一篇博客,讲的是如何使用Java中的类写一个“女朋友”出来。
看完整篇博客之后自己的第一反应肯定是我上我也行。
不过如果是当时的自己,是肯定不敢写成博客发出来供其他人观看的。现在想想,自己一开始写博客的初衷就是想用幽默有趣的语言搞定枯燥无趣的知识,并希望可以帮到其他人。
为什么这么简单的事自己却退缩了?虽然不是很明白,但是咸鱼了很久的博主也是准备翻身了。
=================================================
废柴日记1:咸鱼了很久之后终于有了一些觉悟于是翻了个身
=================================================
--------------------------------------------------------------------------------------
日期:2021年9月11日 天气:小雨转多云
--------------------------------------------------------------------------------------
其实,我每天都还好。
最近慢慢开始刷力扣,也算是一种康复训练,刷题的时候发现了一道很有趣的题,一道和随机数与概率论有关的编程题,这对于概率论卷面考75分的高(di)材(neng)生(er)的博主来说,当然就是小case(并不是)。
借此机会来学习一下Python与C++中产生随机数的几个函数。
对于随机数最重要的一点:对于所谓的「真随机数」,现阶段的计算机是无法产生的。(这里的「真随机数」我们定义为:产生「真随机数」X不受到任何因素的影响,就跟你突然心血来潮去蹦极一样,突然且随机。)
现阶段计算机产生随机数的过程实质上是一个伪随机的过程。
对于计算机而言不存在「随机」这一说法,你所看到的「随机数」也是计算机通过给定的公式和一些参数求出的数字。这个数字对计算机而言是必然结果,对我们而言是一个随机结果。
对我而言,计算机就像是一个笨孩子,你给它什么,它就还给你什么。这个孩子现在还没能产生自己的思维方式与理智,只要你不问它,就算它饿死,也不会对你说一句话。
对于这样的一个笨孩子,它能够根据你所制定的一些规则进行一些“简单”的操作,已经很不错了。
结果有一天它还在愉快地充电,你突然跑了过来把它逼到墙角,然后大喊着:“快,给我康康你的「随机数」!”计算机直接当场懵B,再三思考之后选择死机[doge]。
所以说你所看到的「随机数」,并不是「真随机数」,而是计算机根据事先写好的函数产生的。
我们先来看一下C++中可以产生「随机数」的两个函数:Rand()与Srand()。
C++:Rand()和Srand()的爱情故事❤
首先是rand()函数(头文件:stdlib.h)的底层代码:
//C++中rand()函数的底层实现
int __cdecl rand (void)
{
_ptiddata ptd = _getptd();
return( ((ptd->_holdrand = ptd->_holdrand * 214013L + 2531011L) >> 16) & 0x7fff );
}
实际上rand() 的内部实现是用线性同余法做的,它不是「真随机数」,因其周期特别长,故在一定的范围里可看成是随机的。
如果我们事先不做处理,纯引用,rand()会生成一个0-0x7fff的随机数,即最大是32767的一个数。
/*
* RAND_MAX is the maximum value that may be returned by rand.
* The minimum is zero.
*/
#define RAND_MAX 0x7FFF
rand()函数每次运行时其实是根据一个「seed」来生成一个随机数,这个「seed」可以理解为一个种子,rand()会根据「seed」的值来生成一个随机数。
「seed」的值默认为1。
这其实也在间接说明:当实验次数过多时,未经过处理的rand()函数产生的随机数会逐渐趋近于稳定,逐渐地由「随机数」变成一些固定的数字。
既然如此,我们为了让rand()产生的数字能够一直保持着 "随机性" ,我们需要想办法改变这个「seed」的值,而且最好是在rand()每一次运行的时候,这个「seed」的值都会发生改变。这样我们至少会在心灵上得到些许的慰藉。
C++中提供了一个可以修改「seed」的函数——srand()。
srand()的函数定义(From stdlib.h):
_CRTIMP void __cdecl __MINGW_NOTHROW srand (unsigned int);
rand()每次运行时会先检查程序是否运行过srand(int seed)函数。如果运行过,rand()函数将会以「seed」作为随机数种子生成一次随机数;否则以默认的1作为种子生成随机数。
好了,我们现在有可以改变「seed」的工具了,现在只需要想办法造出一个 "永动机" ,"永动机"随时都会产生一个和之前不同的值,这样rand()每次都会以一个和之前完全不同的种子生成随机数。
问题来了:什么东西永远都会在发生改变,永远不会回到过去式,一直是现在式与将来式呢?
时间。
时间是不会倒流的,每一刻的时间都是不同的,如果我们让rand()函数每次都以时间产生的某种数字来产生数字,这就会使结果的「随机性」大大提高。
这时,我们把邪恶的目光放在了处于time.h头文件的time()函数:
_CRTIMP time_t __cdecl __MINGW_NOTHROW time (time_t*);
对于time.h头文件中的函数详解可以参考一下大佬的博客:
时间函数 time.h 详解 - 草滩小恪 - 博客园C++对时间的操作也有许多值得大家注意的地方。最近,在技术群中有很多网友也多次问到过C++语言中对时间的操作、获取和显示等等的问题。下面,在这篇文章中,笔者将主要介绍在C/C++中时间和日期的使用方法https://www.cnblogs.com/acm1314/p/4568141.html这里大致总结一下:time(NULL)会返回一个long类型的数字,这个数字描述的是当前的「日历时间」。紧接着我们把这个数字强转为unsigned int类型,让rand()根据这个「seed」产生随机数:
int main()
{
srand(unsigned(time(NULL)));
for(int i=1;i<=10;i++)
cout<<rand()<<endl;
return 0;
}
这样的话,每次rand()运行的时候,rand()首先会寻找之前是否出现过srand()函数,发现程序的第三行出现了,这个时候:
rand()羞答答地靠近srand(),十分害羞地问道:"你喜欢吃什么东西啊?"
srand()的闺蜜time()十分担心srand()遇到渣男,于是为srand()出谋划策,每次都给srand()一个新的菜系,然后time()把这个菜系从自己的本子上划掉,再重新写一个新的菜系,每次都不重样。
可怜的rand()每次都要辛辛苦苦根据time()给定的范围给它的srand()准备一份浪漫的晚餐。
讲到这里大家应该对rand()与srand()有了新的认识,接下来我们要转移战场,去看一下博主眼中的「语言毒瘤」Python在随机数的产生一方面,又有着怎样的奇妙说法。
Python:快用你无敌的「Third Party」想想办法啊!
如果非要我用一幅图来形容python语言,我的答案将会是JOJO第三部中「Star Platinum」的无敌面板:
属性 | 能力评价 |
执行力 | A |
精密度 | A |
拓展性 | A |
成长力 | A |
功能性 | A |
运行时间 | C |
在python中如果我们想要得到一个随机数,首先需要调用random模块:
import random
在random模块中有着大量关于生成「伪随机数」的相关函数,我们这里只介绍一些常用的函数:
random.random() #生成一个范围在[0,1)的实数。
random.uniform(a,b) #生成一个介于max(a,b)与min(a,b)的实数。
"""注意:这里的a,b大小关系并没有要求,系统会根据a,b大小情况调节结果的范围"""
"""例如:random.uniform(1,10)与random.uniform(10,1)都是生成一个[1,10]的实数"""
random.randint(a,b) #生成一个[a,b]的随机整数,a<=b
random.choice(sequence) #从序列sequence中随机选取一个元素
"""序列sequence是泛指此类,如list,tuple等"""
我们可以看到,python语言十分的简单粗暴,不给你留机会。
这就像是你去男/女朋友家里吃饭,原本想的是和男/女朋友一起准备饭菜,感情加强,羁绊加深。
结果刚到家门口,发现ta家是一个大家族,从下车到坐在餐桌旁都不需要你动一下,吃饭的时候甚至你只需要张开嘴。
🐂吗?🐂的,但是就是不知道为啥心里有一点点失落。
啊?没有男/女朋友?那没事了。
对于产生随机数的方式感兴趣的朋友可以看一下大佬的博客,弱鸡的博主就不做过多解释了:
总结一下:
- 现阶段计算机生成的随机数为「伪随机数」,还没有可以产生「真随机数」的算法出现。
- C++中的rand()函数会根据系统中的「seed」产生随机数,srand()可以改变「seed」的值,time(NULL)函数可以返回当前的系统时间。
- 可以用srand()+time(NULL)+rand()产生比较随机的随机数。
- Python中的random模块具有产生伪随机数的大量函数。
罗翔老师说过:基于趣味而走向某种学科学习的同学,需要了解到这种学科厚重的意义,来支撑你日复一日看似枯燥的求学生涯。看到这里的朋友,希望你能从这篇博客中获得些许的收获,哪怕是一份快乐。
博主实力不济,写下这个系列的原因只是希望分享自己获得知识那一瞬间的喜悦,也是在写给内心深处的自己,给自己留下热情的火种。
吾日三省吾身:日更否?刷题否?快乐否?
已更,已刷,快乐滴很!
吾心满意足。