29 C 语言中的随机数实现:rand 与 srand

目录

[1 为什么需要随机数?](#1 为什么需要随机数?)

[1.1 背景介绍](#1.1 背景介绍)

[1.2 应用场景](#1.2 应用场景)

[2 C 语言实现随机数](#2 C 语言实现随机数)

[2.1 rand() 函数](#2.1 rand() 函数)

[2.1.1 函数原型](#2.1.1 函数原型)

[2.1.2 功能说明](#2.1.2 功能说明)

[2.1.3 案例演示](#2.1.3 案例演示)

[2.2 srand() 函数](#2.2 srand() 函数)

[2.2.1 函数原型](#2.2.1 函数原型)

[2.2.2 功能说明](#2.2.2 功能说明)

[2.2.3 案例演示](#2.2.3 案例演示)

[2.3 指定范围的随机数](#2.3 指定范围的随机数)

[2.3.1 获取 [min, max] 范围内的随机整数](#2.3.1 获取 [min, max] 范围内的随机整数)

[2.3.2 获取 [min, max) 范围内的随机浮点数](#2.3.2 获取 [min, max) 范围内的随机浮点数)

[2.3.3 函数封装](#2.3.3 函数封装)

[2.3.4 案例演示](#2.3.4 案例演示)

[3 注意事项](#3 注意事项)

[4 综合案例](#4 综合案例)

[4.1 随机密码生成器](#4.1 随机密码生成器)

[4.2 随机数生成器](#4.2 随机数生成器)


1 为什么需要随机数?

在数字世界的各个领域,随机数扮演着至关重要的角色。它们不仅是确保系统公平性与不可预测性的基石,也是保障数据安全、优化算法性能、模拟现实世界复杂现象的关键工具。随机数生成(Random Number Generation, RNG)的需求源于多个方面,包括但不限于编程实践、娱乐游戏、科学研究以及信息安全等领域。

1.1 背景介绍

  • 编程:在软件开发中,随机数常用于实现诸如抽奖、随机排序、负载均衡等功能,它们帮助程序做出不可预测的选择,提高用户体验和系统效率。
  • 游戏开发:游戏世界充满了未知与惊喜,随机事件如掉落物品、怪物刷新、玩家属性加成等,都是基于随机数来实现的,这些机制大大增强了游戏的趣味性和挑战性。
  • 模拟实验:在科学研究中,模拟实验是探索自然规律、预测未来趋势的重要手段。随机数被用于构建模型的随机性部分,如粒子运动、人口增长模拟等,以确保模拟结果更加接近真实世界的复杂性和不确定性。
  • 密码学:在信息安全领域,随机数更是不可或缺。加密算法的密钥生成、随机数填充(如填充块密码的最后一个分组)、会话密钥的协商等,都依赖于高质量的随机数来保证通信的安全性和数据的保密性。

1.2 应用场景

  • 游戏中的随机事件:在《魔兽世界》等角色扮演游戏中,随机掉落的装备和道具、随机触发的剧情事件等,都是由随机数控制的,这种机制保持了游戏的新鲜感和玩家的探索欲望。
  • 数据分析中的样本选择:在统计学和数据分析中,研究者经常需要从大数据集中随机抽取样本进行分析,以确保样本的代表性和结果的可靠性。例如,市场调查中的随机抽样就依赖于随机数生成。
  • 加密算法的密钥生成:在 SSL/TLS 协议中,每次建立安全连接时都会生成一对唯一的会话密钥,这些密钥的生成过程涉及到复杂的随机数算法,以确保通信双方之间传输的数据不被未经授权的第三方窃取或篡改。
  • 随机化算法:在计算机科学中,许多算法通过引入随机性来提高性能或解决特定问题。例如,遗传算法、模拟退火算法等启发式搜索算法,通过随机变异和选择机制来探索解空间,寻找问题的近似最优解。

2 C 语言实现随机数

在 C 语言中,实现随机数通常依赖于标准库中的 <stdlib.h> 头文件,特别是 rand() 函数和 srand() 函数。

2.1 rand() 函数

2.1.1 函数原型

rand() 函数是 C 语言标准库 <stdlib.h> 中用于生成伪随机数的函数。它没有参数,返回一个 int 类型的值,表示生成的伪随机数。

cpp 复制代码
#include <stdlib.h>
int rand(void);

2.1.2 功能说明

rand() 函数生成一个伪随机数 。伪随机数是通过一个算法(通常基于当前时间或其他输入值)产生的,因此它们不是真正的随机数,而是看起来随机的。由于算法的确定性,给定相同的种子(通过 srand() 函数设置),rand() 函数将产生相同的随机数序列。

2.1.3 案例演示

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

int main() {
    // 生成一个随机数
    int random_number = rand();

    printf("生成的随机数是: %d\n", random_number);

    return 0;
}

2.2 srand() 函数

2.2.1 函数原型

srand() 函数用于设置随机数生成的种子,它是 C 语言标准库 <stdlib.h> 中的一个函数。srand() 函数的原型如下:

cpp 复制代码
#include <stdlib.h>  
void srand(unsigned int seed);
  • unsigned int seed 是函数的参数,表示要设置的种子值。函数没有返回值(即返回类型为 void)。

2.2.2 功能说明

srand() 函数用于初始化随机数生成器(RNG)。在调用 rand() 函数生成随机数之前,应该先调用 srand() 函数来设置种子值。种子值是一个整数,它决定了随机数生成算法的起始点。如果不调用 srand(),或者每次程序运行时都使用相同的种子值,那么 rand() 函数将产生相同的随机数序列。

通常,使用当前时间(通过 <time.h> 头文件中的 time() 函数获取)作为种子值,因为这样可以确保每次程序运行时都能得到不同的随机数序列。

2.2.3 案例演示

以下是一个使用 srand() 和 rand() 函数的示例程序,该程序生成并打印了 10 个随机数:

cpp 复制代码
#include <stdio.h>  
#include <stdlib.h>  
#include <time.h>  
  
int main() {  
    // 设置随机数种子为当前时间  
    srand((unsigned int)time(NULL));  
  
    // 生成并打印 10 个随机数  
    for (int i = 0; i < 10; i++) {  
        // 生成随机数并打印  
        printf("第%d个随机数: %d\n", i + 1, rand());  
    }  
  
    return 0;  
}

2.3 指定范围的随机数

2.3.1 获取 [min, max] 范围内的随机整数

rand() 函数会生成一个范围在 **[0, RAND_MAX]**范围内的整数,其中 RAND_MAX 是 <stdlib.h> 中定义的一个常量,表示 rand() 函数能返回的最大值。为了将生成的随机数限制在指定的范围内,可以使用取模运算(%)和适当的偏移量。

假设要生成一个在**[min, max]**范围内的随机整数,可以使用以下公式:

cpp 复制代码
// 生成一个在 [min, max] 范围内的随机整数
int randomNumberInRange = rand() % (max - min + 1) + min;

解释如下 :

1. 理解 rand() 函数:

rand() 是一个标准库函数,用于生成一个伪随机数。它返回一个在 [0, RAND_MAX] 范围上的整数,其中 RAND_MAX 是 <stdlib.h> 中定义的一个常量,表示 rand() 函数能返回的最大值。

2. 计算模数 (max - min + 1):

我们需要确定随机数的范围大小,即从 min 到 max 上有多少个整数。这可以通过计算 max - min + 1 来实现。例如,如果 min 是 10,max 是 20,那么范围大小就是 20 - 10 + 1 = 11,意味着有 11 个整数(10, 11, 12, ..., 20)。

3. 使用模运算符 %:

接下来,我们使用模运算符 % 来限制 rand() 函数生成的随机数。rand() % (max - min + 1) 的作用是将 rand() 生成的随机数限制在一个更小的范围内,即从 0 到 max - min(实际上是到 max - min 的下一个整数,因为模运算的结果是从 0 开始的)。由于我们想要的范围是从 min 开始的,所以我们需要的是从 0 到 max - min 的整数,这样加上 min 后就能得到从 min 到 max 的整数了。

4. 加上 min:

最后,我们将上一步得到的结果(一个在 [0, max-min] 范围内的整数)加上 min。这样,原本在 [0, max-min] 范围内的整数就被 "平移" 到了 [min, max] 范围内。

2.3.2 获取 [min, max) 范围内的随机浮点数

假设要生成一个在**[min, max)**范围内的随机浮点数,可以使用以下公式:

cpp 复制代码
// 生成一个在 [min, max) 范围内的随机浮点数
(double)rand() / RAND_MAX * (max - min) + min;

解释如下 :

1. 类型转换:

(double)rand():将 rand() 函数生成的整数转换为 double 类型。这是为了确保后续运算能够产生浮点数结果。

2. 生成 [0.0, 1.0) 范围内的浮点数:

(double)rand() / RAND_MAX:由于 rand() 返回一个 [0, RAND_MAX] 范围内的整数,而 RAND_MAX 是 rand() 能返回的最大值,因此 (double)rand() / RAND_MAX 将生成一个 [0.0, 1.0) 范围内的浮点数。注意这里是不包括 1.0 的,因为 rand() 几乎不可能恰好返回 RAND_MAX(尽管在理论上有可能,但实际上由于浮点数的表示方式,这种情况几乎不会发生)。

3. 调整范围到 [0.0, max-min):

接下来,我们将上一步得到的浮点数乘以 (max - min)。这样,原本在 [0.0, 1.0) 范围内的浮点数就被 "拉伸" 到了 [0.0, max-min) 范围内。这个新范围仍然不包括其上限(即 max-min),但包括了下限 0.0。

4. 平移范围到 [min, max):

最后,我们通过加上 min 将这个范围 "平移" 到 [min, max)。现在,我们得到了一个在 [min, max) 范围内的随机浮点数,它包括了 min 但不包括 max。

5. 关于端点值:

  • min 是可以被取到的,因为我们在最后一步加上了 min。
  • max 是取不到的,这是由浮点数的表示和运算方式决定的。特别是,当我们乘以 (max - min) 并可能进行四舍五入时,结果永远不会恰好等于 max(除非 max 和 min 相等,但这种情况下范围就没有意义了)。此外,即使在没有四舍五入的情况下,由于浮点数的精度限制,也很难精确表示 max 这样的特定值。

2.3.3 函数封装

可以将生成指定范围随机数的功能编写成函数,在需要的时候,调用即可,如下所示:

cpp 复制代码
// 生成指定范围 [min,max] 的随机整数
int generateRandomInt(int min, int max)
{
    return rand() % (max - min + 1) + min;
}

// 生成指定范围内 [min,max) 的随机浮点数
double generateRandomFloat(double min, double max)
{
    return (double)rand() / RAND_MAX * (max - min) + min;
}

2.3.4 案例演示

以下是一个完整的示例,展示了如何生成一个在 [10, 50] 范围内的随机数:

cpp 复制代码
#include <stdio.h>  
#include <stdlib.h>  
#include <time.h>  
  
int main() {  
    // 设置随机数种子  
    srand((unsigned)time(NULL));  
  
    // 生成并打印一个在 [10, 50] 范围内的随机数  
    int min = 10;  
    int max = 50;  
    int randomNumberInRange = rand() % (max - min + 1) + min;  
    printf("随机数: %d\n", randomNumberInRange);  
  
    return 0;  
}

3 注意事项

种子设置时机 :应该在程序开始时(通常是在 main() 函数的开头)调用 srand() 函数,并且只调用一次。如果在循环中多次调用 srand() 并使用相同的种子(如连续调用 time(NULL) 但时间没有变化),则可能会得到相同的随机数序列。

种子值的选择 :为了得到不同的随机数序列,应该选择一个随时间变化的值作为种子,如当前时间。但是,如果程序在极短的时间内被多次执行(如在一秒内多次启动和停止),则可能仍然会得到相同的随机数序列。在这种情况下,可以考虑使用更复杂的种子生成策略,如结合多个不同的时间戳或系统状态值。

随机数质量:rand() 函数生成的随机数质量(如周期长度、分布均匀性等)可能不足以满足所有应用的需求。在需要高质量随机数的场合(如加密应用),应该考虑使用专门的随机数生成库或函数。

可移植性:尽管 srand() 和 rand() 是 C 语言标准库的一部分,但不同编译器或平台上的实现可能有所不同。因此,在跨平台开发中,应该注意测试随机数生成的行为是否符合预期。


4 综合案例

4.1 随机密码生成器

编写一个 C 语言程序,该程序能够生成一个指定长度的随机密码。密码应包含大写字母、小写字母、数字以及特殊字符(如 !@#$%^&*() 等)。用户需要输入密码的长度,然后程序输出一个符合要求的随机密码。

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

// 定义字符集(全局字符串)
const char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;':\",./<>?";

// 生成随机密码的函数
void generateRandomPassword(int length)
{
    if (length <= 0)
    {
        printf("密码长度必须为正数。\n");
        return;
    }

    for (int i = 0; i < length; i++)
    {
        printf("%c", getRandomChar());
    }
    printf("\n");
}

// 生成随机字符的函数
char getRandomChar()
{
    // 生成索引-随机数范围 [0, chars长度(不包括结束符) - 1]
    int index = rand() % (int)strlen(chars);
    return chars[index];
}

// 主函数
int main()
{
    int length;

    // 设置随机数种子
    srand((unsigned)time(NULL));

    // 获取用户输入的密码长度
    printf("请输入密码的长度:");
    scanf("%d", &length);

    // 生成并打印随机密码
    generateRandomPassword(length);

    return 0;
}

4.2 随机数生成器

编写一个 C 语言程序,实现以下功能:

  • 生成一个指定范围内的随机整数。
  • 生成一个指定范围内的随机浮点数。
  • 生成一个随机字符(大写字母)。
  • 生成一个包含 10 个随机整数的数组,并计算数组的平均值。
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 生成指定范围 [min,max] 的随机整数
int generateRandomInt(int min, int max)
{
    return rand() % (max - min + 1) + min;
}

// 生成指定范围内 [min,max) 的随机浮点数
double generateRandomFloat(double min, double max)
{
    return (double)rand() / RAND_MAX * (max - min) + min;
}

// 生成一个随机的大写字母
char generateRandomChar()
{
    return (char)(rand() % 26 + 'A');
}

// 生成一个包含 10 个随机整数的数组,并计算平均值
double generateRandomArrayAndAverage(int min, int max)
{
    int array[10];
    int sum = 0;

    for (int i = 0; i < 10; i++)
    {
        array[i] = generateRandomInt(min, max);
        sum += array[i];
        printf("随机整数 %d: %d\n", i + 1, array[i]);
    }

    double average = (double)sum / 10;
    return average;
}

int main()
{
    // 使用当前时间作为随机数生成器的种子
    srand((unsigned int)time(NULL));

    // 生成一个 1 到 100 之间的随机整数
    int randomInt = generateRandomInt(1, 100);
    printf("生成的 1 到 100 之间的随机整数是: %d\n", randomInt);

    // 生成一个 0.0 到 10.0 之间的随机浮点数
    double randomFloat = generateRandomFloat(0.0, 10.0);
    printf("生成的 0.0 到 10.0 之间的随机浮点数是: %f\n", randomFloat);

    // 生成一个随机的大写字母
    char randomChar = generateRandomChar();
    printf("生成的随机大写字母是: %c\n", randomChar);

    // 生成一个包含 10 个随机整数的数组,并计算平均值
    double average = generateRandomArrayAndAverage(1, 100);
    printf("[1-100]中 10 个随机整数的平均值是: %f\n", average);

    return 0;
}

输出结果如下所示:

相关推荐
邓校长的编程课堂10 小时前
C++编程&玩转物联网:用树莓派Pico点亮RGB彩灯世界
嵌入式开发·树莓派pico·c++编程·随机数应用·rgb led·pwm技术·stem教育
Thanks_ks1 个月前
41 C 语言共用体:共用体数据类型、共用体变量、访问共用体成员、与结构体的区别
结构体·编程技巧·共用体·c 语言编程·共用体内存分析·共用体成员的访问·共用体变量
野老杂谈3 个月前
13.3 正则表达式的应用
python·正则表达式·数据清洗·文本处理·编程技巧
yangshuquan5 个月前
聊聊 C# dynamic 类型,并分享一个将 dynamic 类型变量转为其它类型的技巧和实例
c#·编程技巧·dynamic·基础知识
迷失蒲公英5 个月前
在线随机密码生成工具
随机密码生成·随机密码
yangshuquan5 个月前
总结一下 C# 如何自定义特性 Attribute 并进行应用
c#·特性·编程技巧
zaiyang遇见8 个月前
Median of an Array(贪心策略,编程技巧)
数据结构·算法·编程技巧·信息学奥赛·程序设计竞赛·贪心策略
疯狂敲代码的老刘1 年前
优雅提效:Guava的字符串处理工具
java·guava·编程技巧
lyhoo1631 年前
AlphaControls控件TsRadioGroup的使用
delphi·编程技巧·第三方控件·alphacontrols