当前位置: 首页 > news >正文

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

目录

1 为什么需要随机数?

1.1 背景介绍

1.2 应用场景        

2 C 语言实现随机数

2.1 rand() 函数

2.1.1 函数原型

2.1.2 功能说明

2.1.3 案例演示

2.2 srand() 函数

2.2.1 函数原型

2.2.2 功能说明

2.2.3 案例演示

2.3 指定范围的随机数

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

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

2.3.3 函数封装

2.3.4 案例演示

3 注意事项

4 综合案例

4.1 随机密码生成器

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 类型的值,表示生成的伪随机数。

#include <stdlib.h>
int rand(void);

2.1.2 功能说明

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

2.1.3 案例演示

#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() 函数的原型如下:

#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 个随机数:

#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] 范围内的随机整数,可以使用以下公式:

// 生成一个在 [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) 范围内的随机浮点数,可以使用以下公式:

// 生成一个在 [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 函数封装

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

// 生成指定范围 [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] 范围内的随机数:

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

#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 个随机整数的数组,并计算数组的平均值。
#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;
}

        输出结果如下所示:


http://www.mrgr.cn/news/36016.html

相关文章:

  • MYSQL隔离性原理——MVCC
  • 量化交易系统开发-实时行情自动化交易-3.4.2.2.Okex交易数据
  • 【React】条件渲染——逻辑与运算符
  • C# 集合与泛型
  • 公司小程序开发制作服务有什么风险点
  • Odoo:免费开源的钢铁冶金行业ERP管理系统
  • WPS中让两列数据合并的方法
  • Linux·进程概念(上)
  • Springboot使用内置对象HttpServletRequest、HttpServletResponse
  • 了解通用 SQL 语法
  • 初识chatgpt
  • 【FastAPI】使用FastAPI和Redis实现实时通知(SSE)
  • 设计云专业软件集中管控方案
  • 【鸿蒙HarmonyOS NEXT】数据存储之关系型数据库RDS
  • Java(基本数据类型)( ̄︶ ̄)↗
  • 实用的云手机软件有哪些?高性价比云手机推荐
  • 【数组】复习与企业真题
  • YOLOv9改进策略【损失函数篇】| Varifocal Loss,解决密集目标检测器训练中前景和背景类别间极端不平衡的问题
  • JavaScript类型判断(总结)
  • 关于宿主机功能正常docker容器重启后dns失效的解决办法
  • 大语言模型之LlaMA系列- LlaMA 2及LLaMA2_chat(上)
  • android13 系统默认设置静态IP
  • 二叉搜索树(来学包会) C++经验+1
  • GEE 案例:一种在不受云层影响并利用合成口径雷达(SAR)数据的情况下监测植被的方法(双极化SAR植被指数)
  • 【Python】的语言基础学习方法 快速掌握! 源码可分享!
  • 怎么批量制作文本或链接静态码?批量静态码在线的生成技巧