C语言生成随机数详解

目录

一、生成随机数代码

二、自定义rand()函数生成的随机数范围

1.自定义[0, n]:rand() % (n+1)

2.自定义[a, a+n]:rand() % (n+1)+a


首先,我们需要知道的是计算机很难生成真正的随机数。主要是因为计算机是基于固定的算法和逻辑来运行的,而随机数是不可预测的,这就导致程序生成随机数时需要破坏计算机的固有逻辑,这是不合理的。为了生成随机数,程序通常会使用伪随机数生成器(PRNG),这是一种算法,可以根据一个初始值生成一系列看似随机的数字。然而,由于PRNG是基于固定的算法实现的,因此生成的随机数实际上是可预测的,只要知道初始值和算法就可以重现生成的随机数序列。为了生成真正的随机数,程序需要使用真正的随机数生成器(TRNG),这种生成器可以从物理现象(如电子噪音、光子的行为等)中提取随机性,从而生成真正随机的数字。然而,由于TRNG的实现通常需要使用专用的硬件设备和复杂的算法,因此在许多情况下使用PRNG已经足够了。

C语言提供了生成随机数的rand()函数,下面是对该函数的介绍。

下面是C语言标准文件中给出的rand函数定义的例子:可以看出该函数生成的随机数是伪随机数。

由上图可知,当不设定srand()函数种子值时,rand函数的next永远是1,那就代表程序每次运行,生成的随机数集合都是完全相同的,也就是说,在不设定srand()函数时,程序第1次运行产生的第i个数和程序第n次运行产生的第i个数完全相同。

所以说,为了让每次程序运行时,rand()函数产生的随机数集合不同,我们需要程序每次运行时,srand()函数的输入参数都不相同,也就是rand()函数的next都不同,我们还需要个随机数作为srand()函数的输入参数,但这似乎有些套娃的意味了,为了产生随机数,我们却需要随机数去产生随机数。

但是,如果我们仔细去思考,可以知道,我们并不一定需要随机数去作为srand()函数的输入参数,我们只需要一个时刻都在变化的值就可以,即使我们能明显看出该数的变化规律。因为,只要每次的next值不同,并且我们不去计算next每次迭代生成的数,那么rand()函数对于我们来说,相当于就是薛定谔的猫,只要我们不打开,rand()函数生成的数,对于我们来说就是随机数(伪随机)。

由此,我们可以想到了时间,时间无时无刻不发生着变化,这满足了我们的需求。此时引入一个新的概念那就是时间戳。

时间戳在这里不详细介绍了,我们只需要知道,时间戳(Unix timestamp)是指自协调世界时(UTC)的1970年1月1日00:00:00起至现在所经过的秒数。它被广泛用于计算机系统中记录和表示时间。

C语言库中提供了time()函数,利用时间戳。

由上图可知,time()函数的输入参数是指针变量,该指针变量是储存时间戳的位置。输入参数还可为NULL(空),即返回的时间戳不保存。可通过下面的代码,连续产生伪随机数。

一、生成随机数代码

int main()
{
	int a = 0;
	srand((unsigned int)time(NULL));
    //time()函数的返回值类型是time_t,不是srand()函数需要的输入参数类型(unsigned int),所以需要类型转换。
	while (1)
	{
		a = rand();
		printf("%d\n", a);
		Sleep(1000);//延迟
	}
	return 0;
}

运行结果:

注意:不要将srand()函数放到循环内部,因为如果这样的话,每次循环的种子值都会发生变化,又因为时间是连续每秒增加1的,所以得出的随机数,会呈现逐渐增加状态,下图就是将srand()函数放入循环内部生成的数。

rand()函数生成的随机数的范围:由本文第一张图可知,rand()函数返回值最后一步计算是‘%32768’,因此rand()函数生成随机数的范围是0~32767。

二、自定义rand()函数生成的随机数范围

1.自定义[0, n]:rand() % (n+1)

int main()
{
	int a;
	srand((unsigned int)time(NULL));
	while (1)
	{
		a = rand() % 101 ;//生成在[0, 100]的数
		printf("%d\n", a);
		Sleep(1000);
	}
	return 0;
}

2.自定义[a, a+n]:rand() % (n+1)+a

int main()
{
	int a;
	srand((unsigned int)time(NULL));
	while (1)
	{
		a = rand() % 100 + 1 ;//生成[1, 100]内的数
		printf("%d\n", a);
		Sleep(1000);
	}
	return 0;
}

当然,还可以rand()求余后乘上倍数,令区间扩大至需要的倍数。不要忘记rand函数()范围是0~32767,若求余%时的被除数,大于32768,那么rand()函数生成的数范围也只能在0~32767,若需要更大的区间,可以rand()求余后乘上倍数,再利用加减运算,平移区间。

需要注意的是,这个算法并不是完美的,它的随机性存在一些局限性。如果我们对这个算法进行简单的分析,就可以发现它的周期很短,只有2^32(这段代码本质实现的是一个线性同余生成器,需要了解为什么其周期是2^32,可以去浏览器搜索如何计算线性同余生成器的周期)。也就是说,当生成的数字达到2^32时,它就会开始重复之前的序列。因此,在使用这个算法时需要注意,如果需要更高质量的随机数,需要使用更为复杂的算法和更大的周期。 

本人水平有限,若以上内容出现错误,请指正,让我们共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值