C语言——rand函数

一、rand函数

这是一个在 C 标准库 <stdlib.h> 中定义的函数,用于生成伪随机数,默认情况下,它生成从 0 到 RAND_MAX 的伪随机数,其中 RAND_MAX 是一个常数,通常是 32767

1、函数原型:

2、函数返回值:

返回产生的从 0 到 RAND_MAX 的伪随机数。

3、使用示例:

在使用 rand 函数时要配合 srand 函数设置 rand 函数的随机种子。

1)产生0,n的整数(n<RAND_MAX):

cpp 复制代码
	rand() % (n + 1);

例如产生0,100的整数:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
	srand((unsigned int)time(NULL));
	int n = 100;
	int random_number = rand() % (n + 1); // 生成 [0, n] 范围内的随机数

	printf("Random Number between 0 and %d: %d\n", n, random_number);
	return 0;
}

2)产生n,m的整数(m<RAND_MAX,n>0):

cpp 复制代码
    int random_number = rand() % (n - m + 1) + m; // 生成 [m, n] 范围内的随机数
  1. 模运算rand() % (n - m + 1) 的作用是确定 rand() 生成的随机数范围在 [0, n-m] 内。

  2. m :最后通过加上 m,将 [0, n-m] 的范围调整到 [m, n]。这样做的目的是将原来的范围平移,使得最小值从 0 变为 m

例如产生5,10的整数:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
	srand((unsigned int)time(NULL));
	int m = 5;
	int n = 10;
	int random_number = rand() % (n - m + 1) + m; // 生成 [m, n] 范围内的随机数

	printf("Random Number between %d and %d: %d\n", m, n, random_number);
	return 0;
}

3)产生n.m,a.b的浮点数(n.m>0,a.b<RAND_MAX):

cpp 复制代码
		printf("%.1lf ",((double)rand() / RAND_MAX) * (a_b - n_m) + n_m);
  1. (double)将rand产生的随机数强制转换为浮点数,这样在除以RAND_MAX时可以是浮点数除法,而不是整数除法。
  2. ((double)rand() / RAND_MAX)可以产生0,1的浮点数,然后乘以 (a_b - n_m) 来确定步长。
    • n_m 来确定范围。
  3. 打印时可以决定是几位浮点数。

例如产生1.0,10.0的浮点数:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
	srand((unsigned int)time(NULL));
	double n_m = 1.0;
	double a_b = 10.0;
	
	double random_number = ((double)rand() / RAND_MAX) * (a_b - n_m) + n_m;

	printf("Random float number between %.1lf and %.1lf is: %.1lf\n", n_m, a_b, random_number);
	return 0;
}

4、srand的设置

1)srand函数使用注意

srand的函数原型:

如果你尝试多次运行下面的代码:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
	printf("%d ", rand() % 100);
	return 0;
}

你会发现每次运行的结果都是一样的,这是因为什么呢?

实际上每次调用 rand() 函数之前,如果没有显式调用 srand(seed) 来设置种子值,系统会自动将种子值设为 1作为默认的初始种子。实际上作为全局静态变量,seed实际上在一般的实现上实在声明时就默认初始化为了1。

所以每次运行程序时,种子都会重置为1,所以每次重新运行这个程序产生的值是同一个。

这里的种子设置为一样的,产生的就是一样的,又是为什么呢?

主要是这里的rand的内部实现是使用了线性同余法产生随机数。

线性同余生成器(Linear Congruential Generator, LCG)是一种产生伪随机数的算法,其数学公式通常表示为:

  • 是序列中的数值。
  • 是乘数(multiplier)。
  • 是增量(increment)。
  • 是模数(modulus)。
  • 是下一个产生的随机数。
  • 是当前的随机数(或种子)。

在这个公式中,种子 (( n = 0 ) 时的 ( X ))是用来启动随机数序列的初始值,而 srand 函数设置的就是这个种子值。

当你在 C 语言中调用 srand 函数并传递一个种子值时,你设置了序列的起始点 。这个种子值会影响到随机数生成的整个序列,因为它是计算下一个随机值时用到的当前随机数 。也就是说当前产生的随机数 是作为下一个随机数 产生的种子的。

在上面的例子中,种子在程序不断重新运行时不断被重置为1,而且程序只产生一个随机数,所以产生的这个唯一的随机数的种子一直是1,又因为程序中的乘数、增量和模数都是设置好的,所以这个随机数就只受种子的影响了,种子一直是同一个,所以产生的这个随机数也一直是同一个。

下面的程序与这个原理也很相似:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
	int n = 1000;
	while (n--)
	{
		srand((unsigned int)time(NULL));
		printf("%d ", rand() % 100);
	}
	return 0;
}

你会发现产生的1000个随机数全部都是一样的,这也是因为每次循环迭代时都调用 srand((unsigned int)time(NULL)) 来重新设置随机数生成器的种子。由于 time(NULL) 在很短的时间内(例如,一秒内,当然这里程序的运行是毫秒级的)返回的值是相同的,因此在这样一个快速的循环中,srand 一直被设置为相同的种子值。这会导致 rand() 在每次迭代中产生相同的随机数序列。

因此,正确的做法是在程序的开始处(或者在生成随机数之前)只调用一次 srand,使用一个种子(如当前时间),这样可以在整个程序的生命周期内生成不同的随机数。

2)线性同余法优缺点:

线性同余法的优点是简单易实现,计算效率高。但是,它也存在一些缺点。首先,生成的随机数序列可能不够随机,会显示出一些模式,尤其是在低维度的测试中可能会被检测出来。其次,如果参数选择不当,可能会导致随机数的周期过短,使得生成的随机数序列在较短的时间内重复。因此,在实际应用中,需要仔细选择参数,或者使用更复杂的随机数生成算法来获得更好的随机性和周期性。

二、rand函数和srand函数的具体实现

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

static unsigned long next = 1;

/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    next = next * 1103515245 + 12345;
    return (unsigned int)(next/65536) % 32768;
}

void mysrand(unsigned int seed) {
    next = seed;
}

int main(void) {
    mysrand(1234);
    printf("Random number: %d\n", myrand());
    return 0;
}

这里的这一语句:

cpp 复制代码
static unsigned long next = 1;

印证了我们之前的话,如果没有显式调用srand函数,那么种子就默认是1,可以发现这里的srand函数的功能就是将我们设置的随机种子赋值给这个全局变量next,然而如果我们没有调用srand函数,next变量从最开始就是1,也就不会改变。

实际上这里的这个next,或者说每次调用srand函数中的next就相当于上面公式里的

cpp 复制代码
    next = next * 1103515245 + 12345;

这里的两个数字(1103515245)是乘数和(12345)是增量,不同编译器的具体数据可能不一样,

cpp 复制代码
    return (unsigned int)(next/65536) % 32768;

这里的模数是 32768

对于srand的实现就一个功能:

cpp 复制代码
void mysrand(unsigned int seed) {
    next = seed;
}

将我们传入的种子参数作为产生下一个随机数的种子。

相关推荐
isyangli_blog18 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb20081118 小时前
FastAPI APIRouter
开发语言·python
Benszen18 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆18 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木18 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充18 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~18 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball61619 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草19 小时前
反射、Tomcat执行
java·开发语言
雪的季节20 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt