营业窗口队列模拟-1

    技术2023-04-09  116

    营业窗口队列模拟

    海纳百川,大道致远

    时光荏苒,青春在这里生长,为了理想我们在这里摩拳擦掌, 岁月不居,梦想依旧灿烂,纯真的理想在这里放飞。

    要求及功能

    实现具有n个窗口的现实队列模拟,统计每人的等待时间。 1). 随机产生顾客的到达时间和服务时间存盘。 2). 实现队列的插入和删除。 3). 当有顾客离开时,根据队列长度调整队尾。 4). 考虑顾客中途离队的情况。 5). 考虑顾客具有优先级的情况。

    思路与理解

    本数据结构课程设计的问题是营业窗口模拟问题。使用了随机数,队列,数组,排序等方法,以计算机代码进行营业窗口的队列模拟。 课程设计需解决的问题是营业窗口的模拟。我们的解决思路是首先通过随机数生成,将顾客的到达时间等数据存盘;接着将他们的到达顺序进行重新排列并按时间先后顺序进行标号;然后进行营业队列的入队、出队和中途离队的排队模拟;最后进行统计输出。

    思维导图分析

    具体的代码实现

    具体的函数

    头文件部分,以及一些初始化的定义 #pragma once #include <stdlib.h> #include <stdio.h> #include <string.h> #define K 6 //窗口数量 #define N 200 //来了多少的顾客 #define MaxSize N //将队列的长度用来了多少人来定义 int Q = 0; //中途离开的客户统计 LinkQueue* temp = NULL; //把那个用来中途离开后的中途队列给全局 //定义时间 struct thetime { int h; int m; }; //定义每个客户的到达银行时间,等待时间,开始办理时间, //处理时间,结束时间(离开银行时间),身份 struct customers { thetime ArriveTime; //到达银行时间 thetime WaitTime; //等待时间 thetime StartTime; //开始办理时间 thetime FinishTime; //结束办理时间 int BusinessTime; //处理时间,处理时间为整数 int QueueNumber; //客户进入银行的序号,先用来标记 int VIP; //定义是否是VIP }; typedef struct _tag_Node { customers data; struct _tag_Node* next; } DataNode; //链队数据结点 typedef struct { DataNode* front; DataNode* rear; } LinkQueue; //链队头结点 //初始化队列 void CreateQueue(LinkQueue*& queue); //销毁队列 void DestroyQueue(LinkQueue* queue); //队列的长度 int QueueLength(LinkQueue* queue); //出队操作 bool deQueue(LinkQueue*& queue, customers& e); //入队操作 void enQueue(LinkQueue*& queue, customers e); //队列是否为空 bool QueueEmpty(LinkQueue* queue); //欢迎界面函数 void welcom(); void end(); //用户到达银行时间表函数 void customers_time(struct customers& customer); //按用户的到达时间将顾客的先后顺序进行排序(由于顾客的到达时间是随机产生的) void customer_sort(customers customer[]); //随机是否离队 bool IsLeaf(int n); //判断顾客需要去哪个窗口排队,算出等待时间最少的队列 int judge_queue_in(LinkQueue* queue[], customers& customer, int QueueCustomerNumber[]); //展示窗口服务情况,主要是看有没有错误,后面可以去掉 void ShowQueue(LinkQueue* queue[]); //判断下一个顾客到来时,哪个队列的队首客户是否已经办理完业务,并进行出队 void LeaveQueue(LinkQueue* queue[], customers customer, int QueueCustomerNumber[]); //剩余的顾客进行正常出队 void LeftLeaveQueue(LinkQueue* queue[], customers customer, int QueueCustomerNumber[]); //顾客进入队列函数 void customers_in_queue(LinkQueue* queue[], customers customer[], int QueueCustomerNumber[]); 主函数部分 void main() { srand((unsigned int)time(NULL)); //使每次编译完后产生的随机数不同 welcom(); //欢迎界面 customers customer[N]; customers customer2[N]; LinkQueue* queue[K]; int i; int QueueCustomerNumber[K]; //初始化队列 for (i = 0; i < K; i++) { CreateQueue(queue[i]); QueueCustomerNumber[i] = 0; //初始窗口服务客户个数 } for (int i = 0; i < N; i++) { customers_time(customer[i]); //初始用户时间 customer2[i] = customer[i]; //初始用户时间 printf("第%d位顾客的到达时间为:%d:%d\n", i + 1, customer[i].ArriveTime.h, customer[i].ArriveTime.m); } //把人进行排队,就是把那个随机的时间生成排序后,重新拿号 customer_sort(customer); //按顾客进入银行的先后顺序进行排序 customer_sort(customer2); //按顾客进入银行的先后顺序进行排序 printf("---------------------------取号---------------------------\n"); for (int i = 0; i < N; i++) { customer[i].QueueNumber = i + 1; printf("第%d位顾客的到达时间为:%d:%d\n", i + 1, customer[i].ArriveTime.h, customer[i].ArriveTime.m); } printf("---------------------------开始营业---------------------------\n"); customers_in_queue(queue, customer, QueueCustomerNumber); //客户进队列 printf("---------------------------停止营业---------------------------\n"); printf("---------------------------人员统计---------------------------\n"); for (int i = 0; i < N; i++) //打印输出的结果 { printf("第%d位顾客到达时间:%d:%02d ", i + 1, customer[i].ArriveTime.h, customer[i].ArriveTime.m); printf("结束时间:%d:%02d ", customer[i].FinishTime.h, customer[i].FinishTime.m); printf("服务时间:%d 分钟 ", customer[i].BusinessTime); printf("等待时间:%d 分钟 \n", customer[i].WaitTime.h * 60 + customer[i].WaitTime.m); } int max_cus_wait_time = customer[0].WaitTime.h * 60 + customer[0].WaitTime.m; int sum_cus_wait_time = max_cus_wait_time; for (int i = 1; i < N; i++) { if (customer[i].WaitTime.h * 60 + customer[i].WaitTime.m > max_cus_wait_time)//这里通过换算成分钟来比较 max_cus_wait_time = customer[i].WaitTime.h * 60 + customer[i].WaitTime.m; sum_cus_wait_time += customer[i].WaitTime.h * 60 + customer[i].WaitTime.m; //这里的等待时间就是进队时那个排队的人的总和加上他自己的时间 } int actual_cus_numbers = QueueCustomerNumber[K - 1]; //我这里先把那个VIP的人数给他,然后再将那些时间什么的给加进去 printf("VIP服务顾客个数:%d\n", QueueCustomerNumber[K - 1]); for (int i = 0; i < K - 1; i++) { printf("窗口%d服务顾客个数:%d\n", i + 1, QueueCustomerNumber[i]); actual_cus_numbers += QueueCustomerNumber[i]; } printf("中途离开顾客个数:%d\n", Q); printf("等待最久顾客的时间是:%d\n", max_cus_wait_time); //输出 printf("平均等待时间为:%0.1f 分钟\n", (double)sum_cus_wait_time / actual_cus_numbers); end(); for (i = 0; i < K; i++) { DestroyQueue(queue[i]); } return 0; }

    一些小细节

    1.到达后如果最后的离开时间超过18:00,将最后的时间进行改变为18:00,时间置零,然后离开。

    2.中途离开这个问题,用的是一个临时的队来存放,然后将原来的队列出队完,在将临时的队进行入队。

    如有问题,欢迎通过邮箱:zack2429@vip.qq.com,进行交流探讨。

    单文件版源码地址:源码下载传送门 多文件班源码地址:源码下载传送门

    更新时间:2020.7.3

    附录

    因为被批为抄袭,所以就把源码写在附录了。虽然大家的思路都是一样的,可是还是被批了。因为可能别人的内容被我复制后然后我没有用自己的个人写法写出来吧,所以放假再写一个吧。 然后有点迷茫查资料和抄袭这个怎么界定啊,用书上的源码算抄袭么?感觉整个人有点蒙。又是“内容你定,分数我定”,又是“和我探讨”,然后问问题都是让人难受,还不回答那种。好谜啊! 希望遇见一个好的老师吧,感觉挺受打击的。加油咯!

    单文件代码 queue.cpp #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #define K 3 //窗口数量 #define N 30 //来了多少的顾客 int Q = 0; //中途离开的客户统计 #define MaxSize N //将队列的长度用来了多少人来定义(包括最后等到下班的客户) //定义时间 struct thetime { int h; int m; }; //定义每个客户的到达银行时间,等待时间,开始办理业务时间, //业务处理时间,结束业务时间(离开银行时间),身份 struct customers { thetime ArriveTime; //到达银行时间 thetime WaitTime; //等待时间 thetime StartTime; //开始办理业务时间 thetime FinishTime; //结束办理业务时间 int BusinessTime; //业务处理时间(假设业务处理时间为整数) int QueueNumber; //客户进入银行的序号,先用来标记 int VIP; //定义是否是VIP(概率) }; //链队数据结点 typedef struct _tag_Node { customers data; struct _tag_Node* next; } DataNode; //链队头结点 typedef struct { DataNode* front; DataNode* rear; } LinkQueue; void CreateQueue(LinkQueue*& queue) //创建链队 { queue = (LinkQueue*)malloc(sizeof(LinkQueue)); memset(queue, 0, sizeof(LinkQueue)); queue->front = queue->rear = NULL; } void DestroyQueue(LinkQueue* queue) //销毁链队 { CreateQueue(queue); free(queue); } int QueueLength(LinkQueue* queue) //返回队列中数据元素个数 { DataNode* p = queue->front; int i = 0; while (p) { i++; p = p->next; } //printf("%d\n", i); return(i); } bool QueueEmpty(LinkQueue* queue) //判断链队是否为空 { return(queue->rear == NULL); } void enQueue(LinkQueue*& queue, customers e) //入队 { DataNode* p = NULL; p = (DataNode*)malloc(sizeof(DataNode)); memset(p, 0, sizeof(DataNode)); p->data = e; if (queue->rear == NULL && queue->front == NULL) queue->front = queue->rear = p; else { queue->rear->next = p; queue->rear = p; } } bool deQueue(LinkQueue*& queue, customers& e) //出队 { DataNode* p; if (queue->rear == NULL) return false; p = queue->front; if (queue->front == queue->rear) queue->front = queue->rear = NULL; else queue->front = queue->front->next; e = p->data; free(p); return true; } LinkQueue* temp = NULL; //欢迎界面函数 void welcom() { printf("学号: 姓名: 班级:\n"); printf("*************营业窗口队列模拟现在开始*************\n"); printf("欢迎光临\n"); printf("\n\n"); }; void end() { printf("\n\n"); printf("*************营业窗口队列模拟结束*************\n"); printf("欢迎下次光临\n"); printf("学号: 姓名: 班级:\n"); } //用户到达银行时间表函数 void customers_time(struct customers& customer) { customer.ArriveTime.h = 9 + rand() % 9; //随机产生顾客到达银行时间 customer.ArriveTime.m = rand() % 60; customer.VIP = rand() % 500; //万分之一有点难 } //判断顾客需要去哪个窗口排队,算出等待时间最少的队列 int judge_queue_in(LinkQueue* queue[], customers &customer, int QueueCustomerNumber[]) { int each_queue_wait_time[K]; //将顾客在每个队列需要等待的时间放在一个数组里 for (int i = 0; i < K - 1; i++) //这里把VIP队列排除 { //顾客的等待时间取决于他队伍最后一个人的结束办理时间 int wait_h = queue[i]->rear->data.FinishTime.h - customer.ArriveTime.h; // each_queue_wait_time[i] = wait_h * 60 + queue[i]->rear->data.FinishTime.m - customer.ArriveTime.m; } int min_time_queue_index = 0; //找出需要时间最短的那个队列 for (int j = 0; j < K - 1; j++) { if (each_queue_wait_time[j] < each_queue_wait_time[min_time_queue_index]) min_time_queue_index = j; } //定义顾客的各项数据 customer.BusinessTime = 10 + rand() % 21 ; customer.StartTime.h = queue[min_time_queue_index]->rear->data.FinishTime.h; customer.StartTime.m = queue[min_time_queue_index]->rear->data.FinishTime.m; customer.FinishTime.h = customer.StartTime.h + (customer.StartTime.m + customer.BusinessTime) / 60; customer.FinishTime.m = (customer.StartTime.m + customer.BusinessTime) % 60; customer.WaitTime.h = customer.FinishTime.h - customer.ArriveTime.h; customer.WaitTime.m = customer.FinishTime.m - customer.ArriveTime.m; return min_time_queue_index; } //判断下一个顾客到来时,哪个队列的队首客户是否已经办理完业务,并进行出队 void LeaveQueue(LinkQueue* queue[], customers customer, int QueueCustomerNumber[]) { for (int i = 0; i < K - 1; i++) { customers e; while (!QueueEmpty(queue[i]) && (queue[i]->front->data.StartTime.h) * 60 + queue[i]->front->data.StartTime.m + queue[i]->front->data.BusinessTime <= (customer.ArriveTime.h) * 60 + customer.ArriveTime.m) { printf("*************顾客%d离开了窗口%d*************\n", queue[i]->front->data.QueueNumber, i + 1); deQueue(queue[i], e); QueueCustomerNumber[i]++; } } while (!QueueEmpty(queue[K - 1]) && (queue[K - 1]->front->data.StartTime.h) * 60 + queue[K - 1]->front->data.StartTime.m + queue[K - 1]->front->data.BusinessTime <= (customer.ArriveTime.h) * 60 + customer.ArriveTime.m) { printf("*************VIP顾客%d离开了VIP窗口*************\n", queue[K - 1]->front->data.QueueNumber); deQueue(queue[K - 1], customer); QueueCustomerNumber[K - 1]++; } } //剩余的顾客进行正常出队 void LeftLeaveQueue(LinkQueue* queue[], customers customer, int QueueCustomerNumber[]) { for (int i = 0; i < K - 1; i++) { while (!QueueEmpty(queue[i])) { printf("***********客户%d离开了窗口%d********\n", queue[i]->front->data.QueueNumber, i + 1); deQueue(queue[i], customer); QueueCustomerNumber[i]++; } } while (!QueueEmpty(queue[K - 1])) { printf("***********VIP客户%d离开了VIP窗口********\n", queue[K - 1]->front->data.QueueNumber); deQueue(queue[K - 1], customer); QueueCustomerNumber[K - 1]++; } } //展示窗口服务情况,主要是看有没有错误,后面可以去掉 void ShowQueue(LinkQueue* queue[]) { for (int i = 0; i < K - 1; i++) { printf("窗口%d:", i + 1); DataNode* m = queue[i]->front; while (m != NULL) { printf("顾客%d ", m->data.QueueNumber); m = m->next; } printf("\n"); } printf("VIP窗口:"); DataNode* m = queue[K - 1]->front; while (m != NULL) { printf("顾客%d ", m->data.QueueNumber); m = m->next; } printf("\n"); } bool IsLeaf(int n) //50分之一的概率离开 { bool IF = false; int i; i = rand() % (50 + n); if (i == 1) IF = true; return IF; } //按用户的到达时间将顾客的先后顺序进行排序(由于顾客的到达时间是随机产生的) void GetNum(customers customer[]) { int min_time_index; //记录顾客到达的最早时间对应的号数 customers min_time_cus, swap_cus; //采用冒泡排序进行排序 for (int i = 0; i <N; i++) { min_time_cus = customer[i]; //先进行初始化 min_time_index = i; for (int j = N-1; j >i; j--) //找出到达时间早的那个人,然后进行排序 { if ((customer[j].ArriveTime.h) * 60 + customer[j].ArriveTime.m < (min_time_cus.ArriveTime.h) * 60 + min_time_cus.ArriveTime.m) { min_time_cus = customer[j]; min_time_index = j; } } if (i != min_time_index) //这里进行交换 { swap_cus = customer[i]; customer[i] = min_time_cus; customer[min_time_index] = swap_cus; } } } //顾客进入队列函数 void Serve(LinkQueue* queue[], customers customer[], int Num[]) { //判断哪个窗口是否有空闲的 int queue_number; for (int i = 0; i < N; i++) { bool queue_free = false; //每次进入队列判断各队首是否已经办理完业务 LeaveQueue(queue, customer[i], Num); if (customer[i].VIP == 0) //有那些vip的人,万分之一的概率,所以时间更短,无需等待 { customer[i].BusinessTime = rand() % 11+1; customer[i].WaitTime.h = 0; customer[i].WaitTime.m = 0; customer[i].StartTime.h = customer[i].ArriveTime.h; customer[i].StartTime.m = customer[i].ArriveTime.m; customer[i].FinishTime.h = customer[i].StartTime.h + (customer[i].StartTime.m + customer[i].BusinessTime) / 60; customer[i].FinishTime.m = (customer[i].StartTime.m + customer[i].BusinessTime) % 60; enQueue(queue[K - 1], customer[i]); //QueueCustomerNumber[K-1]++; queue_free = true; printf("*************VIP顾客%d到达了VIP窗口*************\n", customer[i].QueueNumber); ShowQueue(queue);//, K+1 continue; } for (int j = 0; j < K - 1; j++) { //窗口中有空闲的情况 if (QueueEmpty(queue[j])) { //客户每进入一个队列都需要判断每个队首客户是否已经办理完业务 customer[i].BusinessTime = 10 + rand() % 31; customer[i].WaitTime.h = 0; customer[i].WaitTime.m = 0; customer[i].StartTime.h = customer[i].ArriveTime.h; customer[i].StartTime.m = customer[i].ArriveTime.m; customer[i].FinishTime.h = customer[i].StartTime.h + (customer[i].StartTime.m + customer[i].BusinessTime) / 60; customer[i].FinishTime.m = (customer[i].StartTime.m + customer[i].BusinessTime) % 60; enQueue(queue[j], customer[i]); queue_free = true; printf("*************顾客%d到达了窗口%d*************\n", customer[i].QueueNumber, j + 1); printf("顾客%d需等人数:0\n", customer[i].QueueNumber); ShowQueue(queue); break; } } //窗口中没有空闲的情况 if (queue_free == false) { queue_number = judge_queue_in(queue, customer[i], Num); //判断哪个队列的等待时间最少 if ((customer[i].FinishTime.h) * 60 + customer[i].FinishTime.m > 17 * 60) //人多的时候就后面的给不要了 { printf("亲,今天的服务结束了哟,请下次再来吧!\n"); //deQueue() customer[i].FinishTime.h = 18; customer[i].FinishTime.m = 0; customer[i].BusinessTime = 0; customer[i].WaitTime.h = 17 - customer[i].ArriveTime.h; customer[i].WaitTime.m = 60 - customer[i].ArriveTime.m; printf("*************顾客%d离开了银行*************\n", customer[i].QueueNumber); Q++; continue; } enQueue(queue[queue_number], customer[i]); printf("*************顾客%d到达了窗口%d*************\n", customer[i].QueueNumber, queue_number + 1); printf("客户%d需等人数:%d\n", customer[i].QueueNumber, QueueLength(queue[queue_number]) - 1); ShowQueue(queue); int s; ///这是窗口 for (s = 0; s < K - 1; s++) { customers e; //这里是出队的元素,虽然没啥用 int randnum; //随机产生一个在队列里的数,用队列的长度来随机 int count = 0; //计数通用 randnum = rand() % QueueLength(queue[s]); DataNode* m = queue[s]->front; CreateQueue(temp); int k = QueueLength(queue[s]); if (IsLeaf(k)==true&& k > 3) { while (!QueueEmpty(queue[s]) && count < randnum) { enQueue(temp, m->data); //deQueue(queue[s],e); m = m->next; count++; } printf("*************第%d位顾客有事离开*************\n", m->data.QueueNumber); Q++; m = m->next; //跳过要离开的顾客 count++; while (!QueueEmpty(queue[s]) && count < k) //防止最后一个是空&&m { enQueue(temp, m->data); m = m->next; count++; } while(!QueueEmpty(queue[s]) ) //for (count = 0; count < QueueLength(queue[s]); ) { deQueue(queue[s], e); } m = temp->front; for (count = 0; count < QueueLength(temp); count++) { enQueue(queue[s], m->data); m = m->next; } ShowQueue(queue); } } } } LeaveQueue(queue, customer[N], Num); //离队 LeftLeaveQueue(queue, customer[N], Num); //将剩余的人出队 ShowQueue(queue); } void Summary(customers customer[], int QueueCustomerNumber[]) { for (int i = 0; i < N; i++) //打印输出的结果 { printf("第%d位顾客到达时间:%d:%02d ", i + 1, customer[i].ArriveTime.h, customer[i].ArriveTime.m); printf("结束时间:%d:%02d ", customer[i].FinishTime.h, customer[i].FinishTime.m); printf("服务时间:%d 分钟 ", customer[i].BusinessTime); printf("等待时间:%d 分钟 \n", customer[i].WaitTime.h * 60 + customer[i].WaitTime.m); } int Max_WaitTime = customer[0].WaitTime.h * 60 + customer[0].WaitTime.m; //统计那些等待的时间 int Sum_WaitTime = Max_WaitTime; //来记录最长的,并且计算平均的等待时间 for (int i = 1; i < N; i++) //因为将第0个赋给了初始值,所以i从1开始 { if (customer[i].WaitTime.h * 60 + customer[i].WaitTime.m > Max_WaitTime) //这里通过换算成分钟来比较 Max_WaitTime = customer[i].WaitTime.h * 60 + customer[i].WaitTime.m; Sum_WaitTime += customer[i].WaitTime.h * 60 + customer[i].WaitTime.m; //这里的等待时间就是进队时那个排队的人的总和加上他自己的时间 } int CusNumbers = QueueCustomerNumber[K - 1]; //我这里先把那个VIP的人数给他,然后再将那些时间什么的给加进去 printf("VIP服务顾客个数:%d\n", QueueCustomerNumber[K - 1]); for (int i = 0; i < K - 1; i++) { printf("窗口%d服务顾客个数:%d\n", i + 1, QueueCustomerNumber[i]); CusNumbers += QueueCustomerNumber[i]; } printf("中途离开顾客个数:%d\n", Q); printf("等待最久顾客的时间是:%d\n", Max_WaitTime); //输出 printf("平均等待时间为:%0.1f 分钟\n", (float)Sum_WaitTime / CusNumbers); //强制转换 } int main() { srand((unsigned int)time(NULL)); //使每次编译完后产生的随机数不同 welcom(); //欢迎界面 customers customer[N]; LinkQueue* queue[K]; int i; int QueueCustomerNumber[K]; //初始化队列 for (i = 0; i < K; i++) { CreateQueue(queue[i]); QueueCustomerNumber[i] = 0; //初始窗口服务顾客个数 } for (int i = 0; i < N; i++) { customers_time(customer[i]); //初始顾客时间 } //把人进行排队,就是把那个随机的时间生成排序后,重新拿号 GetNum(customer); //按顾客进入队列的先后顺序进行排序 printf("---------------------------取号---------------------------\n"); for (int i = 0; i < N; i++) { customer[i].QueueNumber = i + 1; printf("第%d位顾客的到达时间为:%d:%02d\n", i + 1, customer[i].ArriveTime.h, customer[i].ArriveTime.m); } printf("---------------------------开始营业---------------------------\n"); Serve(queue, customer, QueueCustomerNumber); //顾客进队列 printf("---------------------------停止营业---------------------------\n"); printf("---------------------------人员统计---------------------------\n"); Summary(customer, QueueCustomerNumber); end(); for (i = 0; i < K; i++) { DestroyQueue(queue[i]); } return 0; }

    更新时间:2020.7.17

    Processed: 0.014, SQL: 9