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.rhkb.cn/news/431708.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE】数据链路层协议和DNS

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 &#x1f45c;一.以太网 以太网&#xff08;Ethernet&#xff09;是一种局域网技术&#xff0c;它定义了开放系统互连&#xff08;OSI&#xff09;模型中的物理…

Python网络爬虫获取Wallhaven壁纸图片(源码)

** 话不多说&#xff0c;直接附源码&#xff0c;可运行&#xff01; ** import requests from lxml import etree from fake_useragent import UserAgent import timeclass wallhaven(object):def __init__(self):# yellow# self.url "https://wallhaven.cc/search?co…

K8S介绍---搭建集群

Kubernetes介绍 官网&#xff1a;https://kubernetes.io/ 一、应用部署方式演变 1、传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其他技术的参与 缺点&#xff1a;不能为应用程序定义资源使用边界&a…

Java中List、ArrayList与顺序表

List、ArrayList与顺序表 List什么是List常用方法介绍List的使用 ArrayList与顺序表线性表顺序表接口的实现 ArrayList简介ArrayList的使用ArrayList的构造ArrayList的常见操作ArrayList的遍历ArrayList的扩容机制 ArrayList的具体使用杨辉三角简单的洗牌算法 ArrayList的问题及…

双向链表的基本结构及功能实现

1.基本结构: 双向链表是一种链表数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含三个部分&#xff1a; (1).数据域&#xff1a;存储节点的数据 (2).前驱指针:指向前一个节点 (3).后驱指针:指向下一个节点 2.基本特性&#xff1a; 双向链接: 与单向链表…

【WPF】02 按钮控件圆角配置及状态切换

按钮圆角 先从工具箱里拖进来一个Button控件&#xff0c;然后对这个按钮进行美化。 首先在 xaml 里按钮控件部分 添加如下代码&#xff1a; <Button x:Name"btnLogin" Content"登录" HorizontalAlignment"Center" Margin"0,399,0,0&q…

C++20中头文件compare的使用

<compare>是C20中新增加的头文件&#xff0c;此头文件是language support库的一部分。它包括&#xff1a;concepts、classes、customization point objects、functions。 1.concepts&#xff1a;三向比较运算符<>&#xff0c;目的是简化比对对象的过程&#xff0c;…

微服务配置管理——动态路由

动态路由 网关的路由配置全部是在项目启动时由org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator在项目启动的时候加载&#xff0c;并且一经加载就会缓存到内存中的路由表内&#xff08;一个Map&#xff09;&#xff0c;不会改变。也不会监听路由变更新…

【PG备份恢复】基于时间点的恢复(踩坑指南)

1 设置基于时间点恢复所需的配置 1.1 修改配置文件 postgresql.conf vim postgresql.conf archive_mode on archive_command cp %p /data1/backups/pg_wal_archive/%f wal_level replica 1.2 生效配置 2 进行一次全备 2.1 创建备份目录 mkdir -p /data/backup/pg_b…

C语言常见字符串函数模拟实现一:(strlen,strcpy,strcat,strcmp,strstr )

strlen模拟实现 重点&#xff1a;1.字符串已经\0作为结束标志&#xff0c;strlen返回的是字符串\0前面出现的字符个数&#xff08;不包含\0&#xff09; 2.参数指向的字符串必须要以\0结束。 3.注意函数的返回值是size_t&#xff0c;是无符号的&#xff0c;加减是无法对比的。…

thinkphp8 从入门到放弃(后面会完善用到哪里写到哪)

thinkphp8 从入门到放弃 引言 thinkphp* 大道至简一、 thinkphp8 安装安装Composerthinkphp 安装命令(tp-项目名称)多应用安装&#xff08;一个项目不会只有一个应用&#xff09;安装完文件目录如下本地部署配置伪静态好了项目可以run 二、架构服务&#xff08;Service&#xf…

大数据新视界 --大数据大厂之探索ES:大数据时代的高效搜索引擎实战攻略

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

app自动化前置准备环境搭建

编写脚本之前的一些前置准备工作。 1&#xff0c;安装appium server&#xff1a;官网地址&#xff1a;http://appium.io/ 安装教程&#xff1a;https://www.cnblogs.com/gancuimian/p/16536322.html 2&#xff0c;安装appium客户端&#xff1a; appium客户端安装相对较简单…

智谱清影 - CogVideoX-2b-部署与使用

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 体验地址&#xff1a;[丹摩DAMODEL官网](https://www.damodel.com/console/overview) CogVideoX 简介本篇将详细介绍使用丹摩服务器部…

OpenAI GPT o1技术报告阅读(2)- 关于模型安全性的测试案例

✨报告阅读&#xff1a;使用大模型来学习推理(Reason) 首先是原文链接&#xff1a;https://openai.com/index/learning-to-reason-with-llms/ 接下来我们看一个简单的关于模型安全性的测试&#xff0c;当模型被问到一个有风险的话题时&#xff0c;会如何思考并回答用户呢&…

CentOS中使用DockerCompose方式部署带postgis的postgresql(附kartoza/docker-postgis镜像下载)

场景 CentOS中使用Docker部署带postgis的postgresql&#xff1a; CentOS中使用Docker部署带postgis的postgresql_centos postgis插件在容器中如何安装-CSDN博客 上面使用Docker搜索和拉取kartoza/postgis时并没有任何限制。 当下如果不能科学上网时&#xff0c;大部分镜像源…

.Net Core 生成管理员权限的应用程序

创建一个ASP.NET Core Web API项目 给解决方案设置一个名称 选择一个目标框架&#xff0c;这里选择的是 .NET 8.0框架 在Porperties文件夹中添加一个app.manifest文件 设置app.manifest文件属性&#xff0c;生成操作设置为嵌入的资源 双击解决方案名称&#xff0c;编辑WebAppli…

【AI大模型】股票价格预测精度增强,基于变分模态分解、PatchTST和自适应尺度加权层

简介 股票价格指数是金融市场和经济健康的晴雨表&#xff0c;准确预测对投资决策至关重要。股票市场的高频交易和复杂行为使得预测具有挑战性&#xff0c;需开发稳定、准确的预测模型。研究表明&#xff0c;估值比率、数据驱动模型&#xff08;如支持向量机&#xff09;、股票…

Android平台使用VIA创建语音交互应用

Android平台使用VIA创建语音交互应用 概述 在 Android 平台上开发一款语音助手应用需要整合多种技术,包括语音识别(ASR)、文字转语音(TTS)、以及热词检测(Hotword Detection)。这些技术共同构成了语音助手应用的核心交互方式,使用户能够通过语音命令与设备进行无缝交…

RabbitMQ 快速入门

目录 什么是MQ 为什么要使用 MQ MQ 的分类 MQ 的选择 认识 RabbitMQ RabbitMQ 的核心部分 安装 脚本安装 docker 安装 启动 web 管理界面 创建用户 创建消息队列 基本概念 消息应答 持久化 预取值 发布确认 交换机 Exchange 概念 死信队列 死信的来源 延迟…