本文共 3951 字,大约阅读时间需要 13 分钟。
之前在敲代码的时候用过rand函数,当时只是知道了rand函数要配合srand函数一起使用,才能达到产生一个随机数的目的,具体原因是什么则一知半解,后来闲着无事,查找了一下资料,差不多弄懂了。不过碍于本人水平有限,可能会有些地方理解有误,敬请给读者批评指正,并提出宝贵意见。
rand函数是用来产生一个随机数,返回值为0~RAND_MAX,RAND_MAX不得小于32767,在编译器中可以通过查看定义来查看这个数具体是多少。不过这数字并不是真正的随机,而是一种伪随机数。
至于为什么说rand函数产生的数字是伪随机数,我们用一串代码来解释
#include#include int main(){ int random = 0; for (int i = 0; i < 10; i++) { random = rand(); //用rand产生一个随机数 printf("%d\n", random); } return 0;}
程序运行结果:
产生这样结果的原因是什么呢?正如前面我们所说,rand函数所产生的数是基于一个依赖于种子的算法来实现的,而这个种子在你每次启动电脑时就确定了。因此每次rand产生的数字也当然是一样的。
至于rand函数所使用的算法是什么,在(C语言之父)所著的《C程序设计语言》一书中给出了一种算法:
unsigned long int next = 1;int rand(void){ next = next * 1103515245 + 12345; return (unsigned int)(next / 65536) % 32768;}
next 就是我们前面所说的种子,由此大家应该知道为什么我们运行上面的代码时,产生的10个数字每次都是一样的。
在前言中我们提到,rand要配合srand来使用,那么srand又是什么呢?
同样的在中我们可以查到srand函数的解释:unsigned long int next = 1;void srand(unsigned int seed){ next = seed;}
很明显,srand可以初始化我们的种子,但是要怎么进行初始化呢,我们先自己输入一个参数,手动对srand进行初始化。
#include#include int main(){ int random = 0; int n = 0; scanf("%d", &n); srand(n); //用srand初始化种子 for (int i = 0; i < 10; i++) { random = rand(); //用rand产生一个随机数 printf("%d\n", random); } return 0;}
当我们输入 2,3,4…时结果如下:
当我们输入不同的n时,rand函数都给出了不同的值,但是当我们输入两组相同的n时,rand的结果都是一样的。也就是说,我们手动地让srand初始化种子,所以此时的随机数可以说成是在认为控制下的随机数,因此它也不是真正意义上的随机数。为了解决这一问题,我们引入time()函数。
同样的,我们先看中给出的解释:
因此我们可以将time函数的返回值作为srand的参数来初始化种子。
代码如下:#include#include #include int main(){ int random = 0; srand((unsigned int )time(NULL)); //用srand初始化种子 for (int i = 0; i < 10; i++) { random = rand(); //用rand产生一个随机数 printf("%d\n", random); } return 0;}
运行结果:
在代码3中,如果我们将srand()放入 for循环中,运行的结果会是什么呢?
#include#include #include int main(){ int random = 0; //srand((unsigned int )time(NULL)); //用srand初始化种子 for (int i = 0; i < 10; i++) { srand((unsigned int )time(NULL)); //用srand初始化种子 random = rand(); //用rand产生一个随机数 printf("%d\n", random); } return 0;}
因此当我们想要随机产生一组数字时,要将srand放在循环体外,每次运行程序时,只重置一次种子。
当我们使用了time函数后,rand函数便会随机产生一个0~RAND_MAX之间的数,但是在大多数情况下,我们只需要某一个区间内的随机数就可以了,那么如何做到呢?
例如我们想要得到[a,b]区间内的随机数,我们只需要将rand生成的数模上(b-a+1)再加上a即可,即: random = rand()%(b-a+1)+a。 因为对于任意两个正整数x,n,x%n所得到的结果一定位于区间[0,n-1], 因此random%(b - a+1)∈[0,b-a],random%(b - a+1)+ a∈[a,b]#include#include #include int main(){ int random = 0; srand((unsigned int )time(NULL)); //用srand初始化种子 for (int i = 0; i < 10; i++) { a = rand()%51+50; //用rand产生一个[50,100]的数字 rand()%(100-50+1)+50∈[50,100] printf("%d\n", a); } return 0;}
对于代码3,当我们多次运行后,可以发现,产生的第一个数字总是在慢慢的增加,并且每两次运行之间的差值都很小
unsigned long int next = 1;int rand(void){ next = next * 1103515245 + 12345; return (unsigned int)(next / 65536) % 32768;}
以Dennis M.Ritchie所给的代码为例,逐渐增加则是因为,第二次运行时,种子的值肯定比第一次运行的值大,因此(next / 65536) % 32768的值,第二次通常会比第一次大(当然这也不是绝对的),当结果逐渐增大到超过32768后,又会变成一个接近0的正整数,然后再次逐渐增大。
猜想:在同一台电脑上,同一款IDE的不同版本(例如VS2019和VS2010)下同时运行代码3,最后产生的随机数是一样的。
碍于本人水平有限,最后的疑问,也是我不太明白的地方。如果有读者能够理解的,也希望能帮忙解释一下。如果文中有错误和不妥之处,还请各位读者批评指正,谢谢。
转载地址:http://xdef.baihongyu.com/