程序设计与算法综合训练》设计报告3学号:E11514064姓名:汪泓章年级:大一专业:计科项目名称:银行业务模拟系统的设计与实现完成日期:2016年7月1日1.需求分析(1)问题描述:假设某银行有四个窗口对外接待客户,从早晨银行开门起不断有客户进入银行。由于每个窗口在某个时刻只能接待一个客户,因此在客户人数众多时需在每个窗口前顺次排队,对于刚进入银行的客户,如果某个窗口的业务员正空闲,则可上前办理业务,反之,若四个窗口均有客户所占,他便会排在人数最少的队伍后面。现在需要编制程序以模拟银行的这种业务活动并计算一天中客户在银行逗留的平均时间。(2)基本要求1)初始化(OpenForDay),模拟银行开门时各数据结构的状态。2)事件驱动(EventDrived),对客户到达和离开事件做相应处理。3)下班处理(CloseForDay),模拟银行关门时的动作,统计客户平均逗留时间。4)按照题意要求独立进行设计,设计结束后按要求写出设计报告。①输入的形式和输入值的范围:规定银行一天的营业时间为480分钟。②输出的形式:所有顾客业务办理的总时间;办理业务的总顾客数;平均每人办理时间③程序所能达到的功能:通过队列的知识完成离散时间模拟,即已知窗口数和一天的营业时间可以求得平均每人办理的时间。2.概要设计总体设计思想:为了计算这个平均的逗留时间,自然需要知道每个客户到达银行和离开银行这两个时刻,后者减去前者即为每个客户在银行的逗留时间。所有客户逗留时间的总和被一天内进入银行的客户数除便是所求的平均时间。称客户到达银行和离开银行这两个时间发生的事情为“事件”,则整个模拟程序将按事件的先后顺序进行处理。这样一种程序称做事件驱动模拟。下面是上述银行客户的离散事件驱动的模拟算法(1)数据结构和程序模块:下面是模拟程序中需要的数据结构及其操作。a.模拟算法的主要处理对象是“事件”,事件的主要信息是事件的类型和事件的发生时刻。算法中处理的事件有两类:一类是客户到达事件;另一类是客户离开事件。前一类事件发生的时刻随客户的到来自然形成;后一类事件发生的时刻由客户办理业务所需时间和等待时间而定。由于程序驱动是按事件发生时刻的先后顺序进行的,所以事件表应是有序表,其主要操作是插入和删除事件,用一个单链表表示。b.模拟程序中需要的另一数据结构是表示客户排队的队列,由于假设银行有4个窗口,因此程序中需要4个队列,队列中有关客户的信息是客户到达的时刻和客户办理业务所需要的时间。每个队列中的队头客户即为正在窗口办理事务的客户,他办完业务离开队列的时刻就是即将发生的客户离开事件的时刻,这就是说,对每个队头客户都存在一个将要驱动的客户离开事件。因此在任何时刻即将发生的事伯只有5种可能:1)新的客户到达;2)1号窗口的客户离开;3)2号窗口的客户离开;4)3号窗口的客户离开;5)4号窗口的客户离开;从以上分析可知,在模拟程序中只需要两种数据结构:有序链表和队列。程序中用到的头文件、类型定义及主要的函数原型如下:#includestdio.h#includemalloc.h#includetime.h#includestdlib.hintOccurTime;//事件发生时刻intNType;//事件类型,Qu表示到达事件,0至Qu-1表示Qu个窗口的离开事件}Event,ElemType;//事件类型,有序链表LinkList的数据元素类型typedefstructLNode//定义事件表的结点类型{ElemTypedatastructLNode*next;}LNode,*LinkList;typedefLinkListEventList;//事件链表类型,定义为有序链表typedefstruct//定义客户队列的元素类型{intArrivalTime;//到达时刻intDuration;//办理事务所需时间}QElemType;//定义QElemType(队列的数据元素类型)为结构体类型;typedefstructQNode//定义客户队列的结点类型{QElemTypedatastructQNode*next;}QNode,*Queue;typedefstruct{Queuehead;Queuetail;}LinkQueue;LinkQueueq[Qu+1];//Qu个客户队列voidOpenForDay();//模拟银行开门的动作,即初始化操作voidCustomerArrived()//处理客户到达事件voidCustomerDeparture()//处理客户离开事件(2)各模块之间的调用与设计a主程序模块:voidmain(){输出主界面:选择操作:进入银行模拟系统/退出程序;While(进入银行业务模拟窗口){OpenForDay();进入初始化操作;输出格式控制;{银行业务模拟:While(有要处理的时间时){DeQueuel();If(客户到达)CustomerArrived();ElseCustomerDeparture();}计算出客户的平均逗留时间并输出}b函数调用关系图3.详细设计voidOpenForDay()//模拟银行开门的动作,即初始化操作{inti;InitList(ev);//初始化事件链表为空en.OccurTime=0;//设定第一个客户到达事件en.NType=0;//客户到达事件Insert_EventList(ev,en);//插入事件q=(LinkQueue*)malloc((Qu+1)*sizeof(LinkQueue));//为队列申请Qu+1个队头指针,第0个不用for(i=1;iQu+1;++i)//置空队列InitQueue(q[i]);}voidCustomerArrived(){//处理客户到达事件QElemTypef;intdurtime,intertime,i;++CustomerNum;Random(durtime,intertime);//生成随机数//printf(%d%d\n,durtime,intertime);et.OccurTime=en.OccurTime+intertime;//下一客户到达时刻et.NType=0;//队列中只有一个客户到达事件//printf(%d%d\n,et.NType,et.OccurTime);if(et.OccurTimeCloseTime)//银行尚未关门,插入事件表Insert_EventList(ev,et);i=Minimum(q);//求长度最短队列的序号,等长为最小的序号f.ArrivalTime=en.OccurTime;f.Duration=durtime;EnQueue(q[i],f);if(QueueLength(q[i])==1){et.OccurTime=en.OccurTime+durtime;et.NType=i;Insert_EventList(ev,et);//设定第i队列的一个离开事件并插入事件表}}voidCustomerDeparture(){//处理客户离开事件,en.NTyPe!=0inti;i=en.NType;DelQueue(q[i],customer);//删除第i队列的排头客户TotalTime+=en.OccurTime-customer.ArrivalTime;//累计客户逗留时间if(!QueueEmpty(q[i])){//设定第i队列的一个离开事件并插入事件表GetHead_q(q[i],customer);et.OccurTime=en.OccurTime+customer.Duration;et.NType=i;Insert_EventList(ev,et);}}voidBank_Simulation(){inti;OpenForDay();//初始化while(!ListEmpty(ev)){//output_ev(ev);//for(i=1;iQU+1;i++)output_q(q[i]);//getchar();//为观察执行结果用Gethead(ev,en);//printf(事件%d%d\n,en.NType,en.OccurTime);if(en.NType==0)CustomerArrived();//处理客户到达事件elseCustomerDeparture();//处理客户离开事件}//计算并输出平均逗留时间printf(顾客总数:%d,所有顾客共耗时:%d分钟,平均每人耗时:%d分钟\n,CustomerNum,TotalTime,TotalTime/CustomerNum);}4.测试与分析利用随机产生的种子进行事件的模拟,即到达时间和办理业务的时间都是随机产生。如果事件尚未到达下班时间,则将其插入到空队列或者是人数(元素)最少的队列,通过检验,算出在不同的服务窗口数下的等待时间以及服务时间,基本上实现了银行事件的模拟。5.总结该实验涉及到线性表的建立、插入、删除等操作,涉及到了队列的建立、插入、删除,涉及到了离散事件的应用思想,还涉及到了排序的概念。完成这个实验对线性表、队列及C语言编程等多方面的知识将是一个很好的利用,对离散事件也将有一个初步的认识。通过本次实验,提高了自已调试程序的能力。充分体会到了在程序执行时的提示性输出的重要性。6.附录源程序清单:#defineOK1#defineTRUE1#defineFALSE0#defineERROR0#defineINFEASIBLE-1#defineOVERFLOW-2typedefintStatus;//-----------------银行排队模拟//事件和事件表typedefstructQCuEvent{intOccurTime;intNType;structQCuEvent*next;}QCuEvent,*EventList;//窗口前队列元素typedefstructQCuElem{intArrivalTime;intDuration;structQCuElem*next;}QCuElem,*QEptr;//窗口指针typedefstruct{QEptrfront;QEptrrear;}QCustomerp,*QCupp;//主要操作函数StatusOpenForDay(EventList&ev,QCuEventen,QCupp&q);//开门StatusCustomerArrived(EventList&ev,QCupp&q,QCuEventen);//顾客到达StatusCustomerDeparture(EventList&ev,QCupp&q,QCuEventen);//顾客离开voidCloseForDay();//基本操作函数StatusOrderInser(EventList&ev,QCuEventen);//按时间顺序插入事件到事件表intQLength(QCustomerpqn);//求窗口队列长度intMinCuQueue(QCuppq);//求队最短的窗口StatusDelFirstEvent(EventList&ev);//删除事件表中的第一个事件StatusInitCuQueue(QCustomerp&qn);//初始化窗口队列StatusEnCuQueue(QCustomerp&qn,QEptrQ);//进入队列StatusDeCuQueue(QCustomerp&qn,QCuElem&Q);//删除队列中的元素StatusGetQHead(QCustomerpqn,QCuElem&Q);//获得队列中的第一个元素StatusDestoryQueue(QCustomerpqn);//销毁队列voidPtint_QStatus(QCustomerpQCu[]);//打印队列长度voidBank_SimulationFunc();voidtest(charstr[]);#includestdio.h#includestdlib.h#includetime.hin