核心概念
rand():生成一个伪随机数。srand():设置rand()函数生成随机数时所使用的“种子”(Seed)。
rand() 函数
功能
rand() 函数用于在程序中生成一个伪随机整数。

头文件
使用 rand() 需要包含 <stdlib.h> 头文件。
#include <stdlib.h>
函数原型
int rand(void);
它不接受任何参数,并返回一个在 0 到 RAND_MAX 之间的整数。
RAND_MAX
RAND_MAX 是一个在 <stdlib.h> 中定义的宏,它代表了 rand() 函数能返回的最大值,根据标准,RAND_MAX 的值至少是 32767(即 0x7FFF),但在很多现代编译器(如 GCC、Clang、MSVC)中,它的值通常是 2147483647(即 0x7FFFFFFF)。
重要特性:伪随机性
rand() 产生的数字是“伪随机”的,这意味着:

- 确定性:如果使用相同的种子启动随机数序列,
rand()会产生完全相同的数字序列,这对于需要可重复性测试的场景(如模拟、算法验证)非常有用。 - 可预测性:因为它的算法是确定的,所以严格来说它不是真正的随机,但对于大多数普通应用,其随机性已经足够。
srand() 函数
功能
srand() 函数用于初始化随机数生成器,它的作用是设置 rand() 函数所使用的“种子”。
头文件
使用 srand() 同样需要包含 <stdlib.h> 头文件。
#include <stdlib.h>
函数原型
void srand(unsigned int seed);
它接受一个无符号整型参数 seed 作为种子。
为什么需要 srand()?
如果你不调用 srand(),rand() 会默认使用 1 作为种子,这意味着,你的程序每次运行时,rand() 都会从同一个序列开始,产生完全相同的“随机数”。

示例:不使用 srand() 的情况
#include <stdio.h>
#include <stdlib.h> // 包含 rand() 的头文件
int main() {
printf("第一次运行程序:\n");
for (int i = 0; i < 5; i++) {
printf("%d ", rand());
}
printf("\n\n");
printf("第二次运行程序:\n");
for (int i = 0; i < 5; i++) {
printf("%d ", rand());
}
printf("\n");
return 0;
}
运行结果: 你会发现,两次运行的输出是完全一样的。
第一次运行程序:
1804289383 846930886 1681692777 1714636915 1957747793
第二次运行程序:
1804289383 846930886 1681692777 1714636915 1957747793
如何正确使用:让每次运行结果都不同
为了让每次程序运行时都产生不同的随机数序列,我们需要在程序开始时调用一次 srand(),并给它一个每次运行都不同的种子。
最佳实践:使用 time(0) 作为种子
time(0) 函数(来自 <time.h>)返回自 Unix 纪元(1970年1月1日00:00:00 UTC)以来经过的秒数,这个值几乎每次程序运行时都会不同,因此是 srand() 的绝佳种子。
示例:正确使用 srand() 和 rand()
#include <stdio.h>
#include <stdlib.h> // 包含 rand() 和 srand() 的头文件
#include <time.h> // 包含 time() 的头文件
int main() {
// 1. 使用当前时间作为随机数生成器的种子
// 只需要在程序开始时调用一次 srand()
srand((unsigned int)time(NULL));
printf("第一次运行程序:\n");
for (int i = 0; i < 5; i++) {
printf("%d ", rand());
}
printf("\n\n");
// 如果你再次运行这个程序,由于时间不同,种子也不同,
// 所以输出的随机数序列也会不同。
// 注意:不要在循环中反复调用 srand()!
// 下面的代码是错误的:
/*
for (int i = 0; i < 5; i++) {
srand((unsigned int)time(NULL)); // 错误!每次循环都会重置种子
printf("%d ", rand());
}
*/
// 这样做会导致 `rand()` 在极短的时间内被多次重置,很可能得到相同的数字。
return 0;
}
可能的运行结果(每次运行都会不同):
第一次运行程序:
1219328119 874542582 1975554324 1219328119 1975554324
生成特定范围的随机数
rand() 默认生成 [0, RAND_MAX] 范围内的数,但我们通常需要其他范围的随机数,[1, 100]。
这可以通过取模运算()和偏移来实现。
通用公式
要生成 [min, max] 范围内的随机整数,可以使用以下公式:
int random_num = rand() % (max - min + 1) + min;
公式解析:
rand() % N:生成[0, N-1]范围内的数。rand() % (max - min + 1):生成[0, (max - min)]范围内的数。rand() % (max - min + 1) + min:将整个范围向右平移min,最终得到[min, max]范围内的数。
示例:生成 1 到 100 之间的随机数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand((unsigned int)time(NULL));
// 生成 1 到 100 之间的随机数
int number = rand() % 100 + 1; // 100 - 1 + 1 = 100
printf("一个1到100之间的随机数是: %d\n", number);
// 生成 50 到 60 之间的随机数
int number2 = rand() % (60 - 50 + 1) + 50; // 11
printf("一个50到60之间的随机数是: %d\n", number2);
return 0;
}
潜在问题:模偏差
当 RAND_MAX 不是 (max - min + 1) 的整数倍时,使用 运算可能会导致某些数字出现的概率略高于其他数字,这种现象称为“模偏差”。
RAND_MAX 是 32767,你想要 [0, 99] 的随机数。32767 % 100 是 67,这意味着数字 0 到 67 出现的概率会比 68 到 99 稍高一点。
对于大多数普通应用,这种偏差可以忽略不计,但如果需要高质量的随机数(如密码学、科学模拟),应该使用更现代的方法,C++11 的 <random> 库或 C11 的 <stdatomic.h> 等。
总结与最佳实践
| 函数 | 头文件 | 作用 | 使用场景 |
|---|---|---|---|
rand() |
<stdlib.h> |
生成伪随机整数。 | 需要随机数的核心功能。 |
srand() |
<stdlib.h> |
初始化随机数生成器的种子。 | 必须在 rand() 之前调用,以确保每次运行结果不同。 |
最佳实践清单:
- 包含头文件:始终包含
<stdlib.h>和<time.h>。 - 单次调用
srand():在main函数的开头,调用一次srand((unsigned int)time(NULL));。绝对不要在循环中调用。 - 使用
time()作为种子:这是获得不同随机数序列最简单、最可靠的方法。 - 使用公式生成范围:使用
rand() % (max - min + 1) + min来生成特定范围的随机数。 - 理解伪随机:
rand()生成的是伪随机数,对于需要高安全性的场景,应考虑更专业的随机数生成库。
