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

18.5【保姆级教程】用队列进行模拟:从数据结构到现实世界的“预言机”

📢专栏持续更新中!关注博主不迷路,跟着专栏系统学C语言底层开发,从语法入门到工程实战,逐章拆解,保姆级讲解,刚入门的同学跟着学,全程零压力~

上一节我们掌握了抽象数据类型(ADT)的设计方法,学会了把数据结构和操作打包成模块,对外只暴露干净利落的函数接口。今天,我们要把这项技能用在一个非常有趣的地方——用队列来模拟现实生活中的排队场景

你可能觉得“队列”只是个简单的先进先出(FIFO)数据结构,但它在真实世界中的应用无处不在:银行柜台前的顾客、机场跑道上等待起飞的飞机、操作系统里排队等待CPU处理的任务、你刷短视频时后台的请求队列……掌握了队列模拟,你就能用C语言回答这样的问题

  • 一个咨询摊位平均每小时能接待多少顾客?
  • 每位顾客平均要等多久?
  • 排队等待的顾客平均有多少人?
  • 摊位需要设置多少个座位才够用?

这些问题不需要真的去街上蹲一天——用程序模拟几千个顾客,几分钟就能得到有统计意义的答案。这就是数据结构 + 编程的威力。

本节核心知识点梳理(提前划重点,方便后续对照学习):

  1. 问题建模:如何把现实中的排队场景抽象为程序中的数据结构;
  2. 随机数生成:用rand()模拟顾客到达间隔和服务时长的随机性;
  3. 队列 ADT 的复用:直接使用之前学过的队列模块,不必重复造轮子;
  4. 模拟循环的核心逻辑:以“分钟”为单位推进仿真时钟,处理到达、服务、离开事件;
  5. 数据统计与分析:从模拟中提取平均等待时间、平均队列长度、每小时服务人数等指标。

一、问题描述:商业街上的咨询摊位

1.1 场景设定

假设 Sigmund Landers 在商业街设置了一个提供付费建议的摊位。规则如下:

  • 顾客可以购买1分钟、2分钟或3分钟的建议(咨询服务时长);
  • 为确保商业街人流畅通,每个摊位前排队等待的顾客最多为10人(即队列最大长度为10);
  • 顾客是随机出现的,到达间隔不确定;
  • 每位顾客的服务时长也是随机选择的(1、2或3分钟);
  • 如果顾客到达时队列已满(10人),该顾客直接离开(损失一位顾客)。

我们要回答的核心问题

  1. Sigmund 平均每小时能接待多少名顾客?
  2. 每位顾客平均花在咨询上的时间是多少?
  3. 排队等待的顾客平均有多少人?
  4. 有多少顾客因为队列满而流失?

1.2 为什么队列是最合适的数据结构?

这个问题天然符合队列的“先进先出”特性:

  • 先来的顾客先接受服务;
  • 后来的顾客排在队尾;
  • 服务完的顾客从队首离开。

队列在这里就是顾客排队的数字化身。

1.3 模拟策略:以“分钟”为单位推进

我们将模拟时间以分钟为最小单位进行推进。每一分钟里,会发生以下事件:

步骤事件模拟操作
1是否有新顾客到达?用随机数决定,如果到达且队列未满则入队
2当前是否有顾客正在接受服务?如果是,剩余服务时间减1分钟
3服务是否刚好完成?如果是,让该顾客出队,统计其等待时间
4如果服务完成且队列非空从队列中取出下一位顾客,开始服务

这就是离散事件模拟的核心循环。程序运行几千个这样的“分钟”后,就能得出有统计意义的结论。

二、随机数的艺术:让模拟更真实

2.1 顾客到达的随机性

现实中,顾客不是排着队准时出现的。假设平均每 3 分钟来一位顾客,但实际间隔可能是1分钟、5分钟、甚至连续来两位。我们怎么模拟这种“随机感”?

C 语言的rand()函数生成一个 0 到RAND_MAX之间的伪随机整数。通过与运算,可以得到指定范围内的随机数:

#include<stdlib.h>#include<time.h>// 在 main 开头调用一次,用当前时间做种子,确保每次运行结果不同srand((unsignedint)time(NULL));// 生成 1 到 3 之间的随机整数,代表 1分钟、2分钟 或 3分钟intservice_time=rand()%3+1;

2.2 到达概率的设定

假设平均每 3 分钟来一位顾客,则每一分钟有 1/3 的概率来一位新顾客。我们可以这样模拟:

// 每一分钟执行一次:if(rand()%3==0){// 概率约为 1/3// 来了一位新顾客if(queue_size<MAX_QUEUE_SIZE){queue_enqueue(q,service_time);}else{customers_lost++;// 队列满,顾客流失}}

2.3 服务时长的随机性

每位顾客购买的建议时长是随机的(1、2或3分钟):

intservice_time=rand()%3+1;// 1, 2, 或 3

三、模拟程序的完整实现

3.1 需要统计的数据

在开始编写模拟循环前,先明确我们要收集哪些数据:

统计指标变量名含义
接待顾客总数total_served成功接受服务并离开的顾客数量
流失顾客总数total_lost因队列满而直接离开的顾客数量
总等待时间total_wait_time所有顾客在队列中等待的分钟数之和
总服务时间total_service_time所有顾客接受咨询的分钟数之和
模拟总时长SIMULATION_MINUTES模拟多少分钟(如 480 分钟 = 8小时)

通过这些原始数据,可以计算出:

  • 平均每小时接待人数 =total_served / (SIMULATION_MINUTES / 60)
  • 平均等待时间 =total_wait_time / total_served
  • 平均队列长度 =total_wait_time / SIMULATION_MINUTES(这段时间内累积的等待分钟数 ÷ 总分钟数,近似等于任一时刻的平均排队人数)
  • 流失率 =total_lost / (total_served + total_lost)

3.2 完整代码

#include<stdio.h>#include<stdlib.h>#include<time.h>#defineMAX_QUEUE_SIZE10#defineARRIVAL_PROB3// 平均每3分钟来一位顾客(概率 1/3)#defineSIMULATION_MINUTES480// 模拟8小时(480分钟)// ---------- 队列 ADT 的简单实现(内嵌版本,便于教学) ----------structCustomer{intservice_time;// 该顾客需要的服务时长(1~3分钟)intwait_time;// 该顾客在队列中等待的分钟数(入队时记录当前时间)};structQueue{structCustomeritems[MAX_QUEUE_SIZE];intfront;intrear;intsize;};voidqueue_init(structQueue*q){q->front=0;q->rear=0;q->size=0;}intqueue_is_full(structQueue*q){returnq->size>=MAX_QUEUE_SIZE;}intqueue_is_empty(structQueue*q){returnq->size==0;}intqueue_enqueue(structQueue*q,structCustomerc){if(queue_is_full(q))return-1;q->items[q->rear]=c;q->rear=(q->rear+1)%MAX_QUEUE_SIZE;q->size++;return0;}structCustomerqueue_dequeue(structQueue*q){structCustomerc=q->items[q->front];q->front=(q->front+1)%MAX_QUEUE_SIZE;q->size--;returnc;}// ---------- 模拟主程序 ----------intmain(void){structQueueq;queue_init(&q);srand((unsignedint)time(NULL));// 统计变量inttotal_served=0;// 成功服务的顾客总数inttotal_lost=0;// 因队列满而流失的顾客总数inttotal_wait_time=0;// 所有顾客的总等待分钟数inttotal_service_time=0;// 所有顾客的总服务分钟数intcurrent_service_remaining=0;// 当前正在服务的顾客还剩余多少分钟(0 表示空闲)intcurrent_minute;// 模拟时钟的当前分钟数// 模拟主循环:每分钟迭代一次for(current_minute=1;current_minute<=SIMULATION_MINUTES;current_minute++){// 事件1:是否有新顾客到达?if(rand()%ARRIVAL_PROB==0){// 概率 1/ARRIVAL_PROBif(!queue_is_full(&q)){structCustomerc;c.service_time=rand()%3+1;// 随机 1, 2, 3 分钟c.wait_time=current_minute;// 记录到达时刻,后续计算等待时间queue_enqueue(&q,c);}else{total_lost++;// 队列满了,顾客直接离开}}// 事件2:当前是否有顾客正在接受服务?if(current_service_remaining>0){current_service_remaining--;// 服务时间减少 1 分钟// 事件3:服务是否刚好完成?if(current_service_remaining==0){total_served++;}}// 事件4:如果当前空闲,且队列非空,从队列中取下一位顾客开始服务if(current_service_remaining==0&&!queue_is_empty(&q)){structCustomerc=queue_dequeue(&q);current_service_remaining=c.service_time;total_wait_time+=(current_minute-c.wait_time);// 累计该顾客的等待分钟数total_service_time+=c.service_time;}}// 模拟结束,输出统计结果printf("========== 模拟结果 ==========\n");printf("模拟总时长:%d 分钟(约 %.1f 小时)\n",SIMULATION_MINUTES,SIMULATION_MINUTES/60.0);printf("接待顾客总数:%d 人\n",total_served);printf("流失顾客总数:%d 人(队列满)\n",total_lost);printf("平均每小时接待:%.1f 人\n",(double)total_served/(SIMULATION_MINUTES/60.0));if(total_served>0){printf("每位顾客平均等待时间:%.1f 分钟\n",(double)total_wait_time/total_served);printf("每位顾客平均服务时间:%.1f 分钟\n",(double)total_service_time/total_served);}printf("平均队列长度:%.2f 人\n",(double)total_wait_time/SIMULATION_MINUTES);printf("流失率:%.1f%%\n",(double)total_lost/(total_served+total_lost)*100.0);return0;}

3.3 运行结果示例

========== 模拟结果 ========== 模拟总时长:480 分钟(约 8.0 小时) 接待顾客总数:142 人 流失顾客总数:0 人(队列满) 平均每小时接待:17.8 人 每位顾客平均等待时间:0.7 分钟 每位顾客平均服务时间:2.0 分钟 平均队列长度:0.22 人 流失率:0.0%

新手重点:因为每次运行用了srand(time(NULL)),结果会稍有不同,但大致趋势一致。这正是模拟的意义——通过反复运行,得出一个统计范围,而不是精确的单一数值。

3.4 代码逐段拆解

① 数据结构定义

structCustomer{intservice_time;// 服务时长intwait_time;// 到达时刻(用于计算等待时间)};

wait_time记录的是顾客入队时的分钟数(当前模拟时钟的读数)。当这位顾客最终被服务时,用current_minute - c.wait_time就得到了他在队列中等待的总分钟数。

② 模拟主循环的四步事件

for(current_minute=1;current_minute<=SIMULATION_MINUTES;current_minute++){// 事件1:检查新到达// 事件2:推进当前服务// 事件3:检查服务完成// 事件4:取下一位顾客}

这个循环结构是离散事件模拟的通用模板,换个场景同样适用。

③ 统计变量的更新时机

变量何时更新
total_served服务完成时
total_lost到达但队列满时
total_wait_time顾客从队列中取出开始服务时
total_service_time顾客从队列中取出时

④ 平均队列长度的计算

平均队列长度 = 总等待分钟数 / 模拟总分钟数

这背后的原理是利特尔法则:在一个稳定系统中,平均排队人数 = 顾客到达率 × 平均等待时间。我们的计算方式等价于这个公式,统计意义上合理。

3.5 你可以调整的参数

参数当前值如果调整为……会有什么变化?
MAX_QUEUE_SIZE105流失率飙升,排队人数减少
ARRIVAL_PROB3(每3分钟来一位)2(每2分钟来一位)顾客更多,队列更长,等待更久
SIMULATION_MINUTES480(8小时)2880(48小时)样本更大,统计结果更稳定
服务时长范围1~3分钟1~5分钟每位顾客占用更长时间,队列积压更严重

动手试一试:改几个参数重新运行,观察结果如何变化。这就是模拟的魅力——不需要真的去街上开个摊位,就能提前预见各种情况。

四、本章总结(新手必看,快速掌握核心)

核心知识点一句话总结
问题建模把现实排队场景映射为队列数据结构,1分钟为模拟时钟的最小单位
随机数模拟rand() % N控制概率,srand(time(NULL))保证每次运行结果不同
四步事件循环到达检查 → 服务推进 → 完成处理 → 取下一位,这是离散事件模拟的通用模板
数据统计在关键事件点累计原始数据,模拟结束后统一计算平均指标
利特尔法则平均队列长度 = 总等待时间 / 总模拟时间
参数可调队列容量、到达率、服务时长范围等参数都可以自由调整,观察不同场景

入门行动清单

  1. 把上面的完整代码复制到 VS 中,编译运行,观察输出结果;
  2. 修改MAX_QUEUE_SIZE为 5,重新运行,对比流失率的变化;
  3. 修改ARRIVAL_PROB为 2(顾客来得更频繁),重新运行,观察等待时间的变化;
  4. 尝试将模拟时长设为60 * 24(模拟一整天),看看结果是否更稳定;
  5. 思考一下:你身边还有哪些场景可以用队列模拟?食堂打饭?地铁安检?试试动手改编代码。

队列模拟的魔法已经在你手里了。它不仅仅是一个数据结构练习,更是把编程能力应用于解决现实问题的思维方式——从观察世界到建模抽象,再到代码实现和数据分析,这条链路是高级程序员的必备素养。

👉关注博主,专栏持续更新,从基础到实战,保姆级讲解 C 语言核心特性与工程技巧。队列之后,我们将进入二叉树的世界——那是数据结构的又一个重要篇章。我们下一节见!

#C语言 #数据结构 #队列 #离散事件模拟 #随机数 #保姆级教程 #新手避坑 #嵌入式开发 #CSDN #C语言实战

🎁欢迎关注公众号,获取更多技术干货!

🚀 C语言宝藏资源包免费送!14 本 C++ 经典书 + 编译工具全家桶 + 高效编程技巧,搭配 C 语言精选书籍、20 + 算法源码 + 项目规范,还有 C51 单片机 400 例实战!从零基础到嵌入式开发全覆盖,学生党、职场人直接抄作业~ 关注文章末尾的博客同名公众号,回复【C 语言】一键解锁全部资源,手慢也有!​

http://www.jsqmd.com/news/987815/

相关文章:

  • PlayStation 3模拟器终极指南:如何在现代电脑上重温PS3经典游戏
  • 石家庄车灯升级门店排行:5家正规机构实测对比 - 起跑123
  • 2026东莞配眼镜产品口碑全解析:瞳壤五款真实体验深度测评 - 配眼镜新资讯
  • 2026年儿童竹蜻蜓厂家选型指南:产品、品质与供货能力三维度解析 - 企师傅推荐官
  • 如何在5分钟内免费生成高质量3D资产?Hunyuan3D-2终极指南
  • 如何快速上手Duix Avatar:打造专属AI数字人的完整实践指南
  • 2026定制竹蜻蜓厂家推荐:金华市精彩塑胶制品有限公司,聚焦儿童玩具与文旅礼品定制配套 - 企师傅推荐官
  • sync.Pool 的真正分界线不是对象大小——一次 benchmark 翻车记录
  • 2026年稻花香源头厂家/产地直供排行榜:五常稻花香2号/正宗优质稻花香大米最新精选推荐 - 企业推荐官【官方】
  • 成都钻石回收套路拆解,虚标高报价、刻意压低 4C 等级猫腻曝光 - 奢侈品回收评测
  • 你还在用 `+ “\\n“` 拼多行字符串吗?Java 的文本块都已经能让代码“像人写的”了!
  • 2026年郑州航空港区长短途搬家运输公司:设备搬迁、企业搬迁、机场货物搬卸分析报告 - 品研笔录
  • 无代码测试革命:Hercules如何用AI重构软件质量保障体系
  • Claudian插件终极指南:如何用AI助手提升Obsidian知识管理效率
  • 深入理解 Apache Flink 可扩展状态
  • 石家庄专业车灯升级门店排行 资质与服务实测对比 - 起跑123
  • 【信息科学与工程学】计算机科学与自动化/控制——第九十二篇 自动化控制01
  • 2026东莞GEO优化公司实力排名!实测技术、案例、效果综合对比 - 新闻快传
  • 2026防爆型气体采样探头厂家排行榜:工业安全监测核心部件选购攻略 - 品研笔录
  • 2026年必备收藏:解决AIGC烦恼的免费实用网站
  • 2026 纺织服饰配套优选:复合型高周波热转印标定制厂家严选 - 变量人生001
  • N_m3u8DL-RE终极指南:3步破解流媒体下载难题
  • 5、【AI产品经理概述】行业现状与职业前景
  • 2026年 重庆摆闸/人行通道闸/三辊闸/翼闸最新推荐榜单:厂家实力与稳定耐用的选型指南 - 品牌发掘
  • 2026上海高端手表回收:江诗丹顿回收市场行情解析 - 奢侈品回收评测
  • 2026年郑州航空港区公司企业搬迁公司全景分析:深度测评选对团队少走弯路! - 品研笔录
  • 2026广州楼梯房翻新室内设计公司排行实测 - 互联网科技品牌测评
  • 2026年|如何快速去除论文AI痕迹?10款智能工具高效降低AI率
  • 前后端模块化分离实战:从零搭建用户列表展示(HTML+CSS+JS + json-server)
  • 【干货】DeepSeek / 豆包数学公式完美转 Word 攻略!告别乱码,效率翻倍!AI 导出鸭一键快速转换公式