日常业务中,经常会生成随机数,这里以C++为例,常见的随机数生成方法如下:原理很简单,先生成一个随机数生成器,指定种子,然后生成随机数;
#include <iostream> #include <windows.h> int main() { printf("time(0)=%d",time(0)); srand(0); for(int i=0;i<=10;i++){ printf("%d ",rand()); } system("pause"); }
根据随机数的定义,每次生成的数不一样才能叫随机数。然而上面这段代码真是这样的么?我们看看效果了:生产的10个数确实不一样啊,这不就是我们想要的效果么? 感兴趣的读者可以自行尝试多生成一些数,看看每次生成的数是不是都不一样!
然而这样就结束了么?图样图森破!同样的代码,我们多运行几次实例看看了,效果如下:不同的时间点我生成了4个不同的实例,然后每次生成实例产生的随机数都是一样的,这是不是很“毁三观”啊!
我明明想生成随机数,为啥每次生成实例产生的数据都是一样的了?换句话说:srand生成随机数的原理是啥了?
计算机的随机数都是由伪随机数,即是由小M多项式序列生成的,其中产生每个小序列都有一个初始值,即随机种子。(注意: 小M多项式序列的周期是65535,即每次利用一个随机种子生成的随机数的周期是65535,当你取得65535个随机数后它们又重复出现了。)我们知道 rand() 函数可以用来产生随机数,但是这不是真正意义上的随机数,是一个伪随机数,是根据一个数(我们可以称它为种子)为基准以某个递推公式推算出来的一系列数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,所以我们运行不同实例时产生的“随机数”居然是一样的,都是相同种子惹的祸!所以我们把种子用当前时间来代替,重新更改后的代码如下:
#include <iostream> #include <windows.h> int main() { int seed = time(0); printf("seed=%d", seed); srand(seed); for(int i=0;i<=10;i++){ printf("%d ",rand()); } system("pause"); }
多运行几个实例,每次产生的数确实不一样了:这样就万事大吉、高枕无忧了?
前面说了:相同的种子,产生的伪随机数是一样的,是不是也可以反过来猜想: 根据伪随机数倒推种子了?比如我先在看到的数字是22843、6380、25403、7962,是不是能倒推出种子是16210637719041了?一旦倒推出种子,我是不是也能成功预测第5个数字是13894了?穷举找seed的代码如下:
int num[] = { 22843,6380,25403,7962 }; int seed = time(0); bool bfind = false; while (seed--) { srand(seed); for (int i=0;i<4;i++) { if (num[i]!=rand()) { bfind = false; break; } bfind = true; } printf("%d is not the seed,continue! ", seed); if (bfind) { printf("find the seed:%d ", seed); break; } }
一旦找到seed,就能用这个seed继续生成剩下的数字,准确预测了!
注意:实战时,因为不知道菠菜站点启动服务器的时间,所以这个计算量较大,一般都是用服务器+多线程跑的,这里只是介绍最核心的原理,感兴趣的小伙伴可以自行尝试!
1、https://www.runoob.com/w3cnote/cpp-rand-srand.html C++ rand 与 srand 的用法