rand与srand如何正确使用?

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 C语言 正文

核心概念

  • rand()生成一个伪随机数。
  • srand()设置 rand() 函数生成随机数时所使用的“种子”(Seed)。

rand() 函数

功能

rand() 函数用于在程序中生成一个伪随机整数。

c语言rand srand
(图片来源网络,侵删)

头文件

使用 rand() 需要包含 <stdlib.h> 头文件。

#include <stdlib.h>

函数原型

int rand(void);

它不接受任何参数,并返回一个在 0RAND_MAX 之间的整数。

RAND_MAX

RAND_MAX 是一个在 <stdlib.h> 中定义的宏,它代表了 rand() 函数能返回的最大值,根据标准,RAND_MAX 的值至少是 32767(即 0x7FFF),但在很多现代编译器(如 GCC、Clang、MSVC)中,它的值通常是 2147483647(即 0x7FFFFFFF)。

重要特性:伪随机性

rand() 产生的数字是“伪随机”的,这意味着:

c语言rand srand
(图片来源网络,侵删)
  1. 确定性:如果使用相同的种子启动随机数序列,rand() 会产生完全相同的数字序列,这对于需要可重复性测试的场景(如模拟、算法验证)非常有用。
  2. 可预测性:因为它的算法是确定的,所以严格来说它不是真正的随机,但对于大多数普通应用,其随机性已经足够。

srand() 函数

功能

srand() 函数用于初始化随机数生成器,它的作用是设置 rand() 函数所使用的“种子”。

头文件

使用 srand() 同样需要包含 <stdlib.h> 头文件。

#include <stdlib.h>

函数原型

void srand(unsigned int seed);

它接受一个无符号整型参数 seed 作为种子。

为什么需要 srand()

如果你不调用 srand()rand() 会默认使用 1 作为种子,这意味着,你的程序每次运行时,rand() 都会从同一个序列开始,产生完全相同的“随机数”。

c语言rand srand
(图片来源网络,侵删)

示例:不使用 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;

公式解析:

  1. rand() % N:生成 [0, N-1] 范围内的数。
  2. rand() % (max - min + 1):生成 [0, (max - min)] 范围内的数。
  3. 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_MAX32767,你想要 [0, 99] 的随机数。32767 % 10067,这意味着数字 067 出现的概率会比 6899 稍高一点。

对于大多数普通应用,这种偏差可以忽略不计,但如果需要高质量的随机数(如密码学、科学模拟),应该使用更现代的方法,C++11 的 <random> 库或 C11 的 <stdatomic.h> 等。


总结与最佳实践

函数 头文件 作用 使用场景
rand() <stdlib.h> 生成伪随机整数。 需要随机数的核心功能。
srand() <stdlib.h> 初始化随机数生成器的种子。 必须在 rand() 之前调用,以确保每次运行结果不同。

最佳实践清单:

  1. 包含头文件:始终包含 <stdlib.h><time.h>
  2. 单次调用 srand():在 main 函数的开头,调用一次 srand((unsigned int)time(NULL));绝对不要在循环中调用。
  3. 使用 time() 作为种子:这是获得不同随机数序列最简单、最可靠的方法。
  4. 使用公式生成范围:使用 rand() % (max - min + 1) + min 来生成特定范围的随机数。
  5. 理解伪随机rand() 生成的是伪随机数,对于需要高安全性的场景,应考虑更专业的随机数生成库。
-- 展开阅读全文 --
头像
dede文章如何实现跳转到指定页面?
« 上一篇 今天
C语言compare函数如何正确实现与使用?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]