1.srand和rand函数
c语言中最常用的产生随机数的方式就是srand和rand函数的组合。其中srand函数用于设置随机数起点,rand函数用于产生随机数。rand函数包含在头文件stdlib.h中。
1.1.为什么要设置随机数起点
当我们不设置随机数起点时,我们会发现每次得到的随机数都为同一组。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char ch = 0;
while((ch = getchar()) == '\n')//每次按下回车就输出一次随机数
{
printf("%d", rand());
}
return 0;
}
这是因为rand函数产生的随机数为伪随机数,是以一个数为起点,根据某个公式推算出来的。而这个数就被称之为“种子” ,如果不进行设置,那么种子在系统启动之后就是一个固定的值。这也就意味着你的程序每次启动之后,得到的随机值总是同一组。
这个特性使我联想到沙盒游戏中的地图种子。众所周知,沙盒游戏的地图都是随机生成的,为什么一串数字就可以保证生成的地图是完全相同的呢?我认为是相同的道理,地图的随机生成也是依靠某种算法模拟实现的,种子相同,算法相同,那么生成的地图自然是完全相同的。
所以,我们需要设置一个起点来保证每次运行程序得到的随机数不是同一组。当然,要实现完全的随机,种子必然得是随机的,并且每次运行程序时的种子都应该不同。
1.2.随机数起点的设置
我们的目的是得到一个随机的数,而起点的设置又要求一个随机数,这不是矛盾了吗?所以,绝对的随机是难以实现的,我们优先考虑使每次的种子不同。
那么,什么数据是一直在变化的呢?没错,那就是时间。我们可以通过time函数来获得一个时间戳(从北京时间1970年01月01日08时00分00秒起到现在的总秒数),并将其设置为随机数起点。考虑到启动程序时机的随机性,这样的一个起点几乎可以认为是随机的,再经过rand函数的推算,规律性更是可以忽略不计。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
char ch = 0;
srand((unsigned)time(NULL));//以时间戳来设置随机数起点
while((ch = getchar()) == '\n')
{
printf("%d", rand());
}
return 0;
}
1.3.备注
一个函数中设置一个随机数起点即可 ,否则就会出现以下这种情况。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
char ch = 0;
while((ch = getchar()) == '\n')
{
srand((unsigned)time(NULL));//以时间戳来设置随机数起点
printf("%d", rand());
}
return 0;
}
每次设置完起点后,rand的推算公式就从头开始了,这使得被降低的规律性又升高。同时,程序运行的时间不会很长,那么每次获得的时间戳也会十分相近,也使规律性变得更加明显。
2.自己的一些想法和探究
2.1.免责声明
博主是新手,很多东西都不知道,以下只是一些个人的片面想法。我写的不对的内容,各位大佬想要补充的,请务必在评论区中指出。
2.2.尝试利用未初始化的变量或地址来获得随机数
在学习c语言的过程中,各路老师和大佬都说:“如果变量不初始化,那么这个变量的值就默认为一个随机数。所以一定要养成初始化变量的习惯!”
这时我突然想到:我忙活半天不久是为了一个随机数吗?
于是我就做了这样的尝试:
#include <stdio.h>
int test()//通过未初始化的变量获得1到100之间的随机值
{
int a;
int random = (unsigned int)a % 100 + 1;
return random;
}
int main()
{
char ch = 0;
while((ch = getchar()) == '\n')
{
printf("%d", test());
}
return 0;
}
可惜,很明显是行不通的 。那么,这是由于(1)函数中的参数在函数结束后未被销毁,还是因为(2)未初始化的变量的值是默认的一个固定值呢?于是我修改了下这个简单的代码并再次运行了程序,如果a未被销毁,那么我得到的一组数据肯定是不同的。
#include <stdio.h>
int test()
{
int a;
int random = (unsigned int)a % 100 + 1;
a++;
return random;
}
int main()
{
char ch = 0;
while((ch = getchar()) == '\n')
{
printf("%d", test());
}
return 0;
}
也就是说,函数结束后,函数内变量肯定是会被销毁的。但是每次运行程序,函数中未初始化的变量的值为某个默认的值,并且这个默认的值在每次运行时都不相同,随机性很强。
这不是正好符合我们要找的随机数起点应该满足的条件吗?
于是我再次修改了一下代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int test()
{
int a;
int random = (unsigned int)a;
return random;
}
int main()
{
char ch = 0;
srand(test());//以未初始化的变量作为随机数起点
while((ch = getchar()) == '\n')
{
printf("%d", rand());
}
return 0;
}
不过很可惜,随机性似乎并不强,试了好几次,每一组数据的第一个总是一个八千多的数字。
把函数改成这样会极大提升随机性,不过会有警告
int test()
{
int a;
int random = (unsigned int)&a;
return random;
}
2.3.感受和总结
感受:以上的探究似乎并不是完全没用,至少说明了srand,time,rand组合的难以替代性,及其被广泛应用的原因。
总结:还是srand,time,rand的组合可靠好用。
3.心得体会
为了学好编程,我加了许多编程学习的群,我发现很多人一遇到bug,一遇到不会的题就求助群中的大佬。我是个新手,但是很喜欢帮别人写代码,找bug,所以老是被和我一样的新手错认为是大佬。其实很多时候他们是自己不愿意花时间去想这个代码怎么写,也懒得一步一步地去调试,观察变量的值在哪里变得与预期不符。我认为想要学好编程,一定要多自己写程序,自己调试找bug,多思考,多探究。