一、问题描述及解决思想

1、问题描述

42、离散事件模拟----银行业务模拟

 

2、解决思想

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟42、离散事件模拟----银行业务模拟

我们需要注意“事件”的概念,客户到达和客户离开都称为“事件”。

二、C语言描述

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟

三、C语言实现

#include "stdio.h"

#include "stdlib.h"

#include "math.h"

#define Qu 4 //客户队列数

#define Khjg 5 //两相邻到达的客户的时间间隔最大值

#define Blsj 30//每个客户办理业务的时间最大值

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

typedef int Status;

typedef int Boolean;

typedef struct

{

int OccurTime;  //事件发生时刻

int NType;      //事件类型,Qu表示到达事件,

                            //0-Qu-1表示Qu个窗口的离开事件

}Event,ElemType;//事件类型,有序链表LinkList的数据元类型

typedef struct LNode // 结点类型

 {

  ElemType data;

  LNode *next;

 }*Link,*Position;

struct LinkList // 链表类型

 {

  Link head,tail; // 分别指向线性链表中的头结点和最后一个结点

  int len; // 指示线性链表中数据元素的个数

 };

typedef LinkList EventList; // 事件链表类型,定义为有序链表

typedef struct

{

int ArrivalTime;

int Duration;

}QElemType;     //队列的数据元素类型

typedef struct QNode

{

  QElemType data;

  QNode *next;

}*QueuePtr;      //队列

struct LinkQueue

{

QueuePtr front,rear; // 队头、队尾指针

};

EventList ev;//事件表

Event en; //事件

Event et;//临时变量

LinkQueue q[Qu]; //Qu个队列客户

QElemType customer; //客户记录

int TotalTime=0,CustomerNum=0;//累计客户逗留时间,客户数(初值为0)

int CloseTime; // 银行营业时间(单位是分)

int cmp(Event a,Event b)

 { //依事件a的发生时刻<、=或>事件b的发生时刻分别返回-1、0或1

  if(a.OccurTime==b.OccurTime)

    return 0;

  else

    return (a.OccurTime-b.OccurTime)/abs(a.OccurTime-b.OccurTime);

 }

Status InitList(LinkList &L)

{ // 构造一个空的线性链表

  Link p;

  p=(Link)malloc(sizeof(LNode)); // 生成头结点

  if(p)

   {

    p->next=NULL;

    L.head=L.tail=p;

    L.len=0;

    return OK;

   }

  else

    return ERROR;

}//InitList

Status InitQueue(LinkQueue&Q)

{ // 构造一个空队列Q

  if(!(Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode))))

    exit(-1);

  Q.front->next=NULL;

  return OK;

}//InitQueue

Status OrderInsert(LinkList&L,ElemType e,int (*comp)(ElemType,ElemType))

 { //已知L为有序线性链表,将元素e按非降序插入在L中。(用于一元多项式)

  Link o,p,q;

  q=L.head;

  p=q->next;

  while(p!=NULL&&comp(p->data,e)<0) // p不是表尾且元素值小于e

   {

    q=p;

    p=p->next;

   }

  o=(Link)malloc(sizeof(LNode)); // 生成结点

  o->data=e; // 赋值

  q->next=o; // 插入

  o->next=p;

  L.len++; // 表长加1

  if(!p) // 插在表尾

    L.tail=o; // 修改尾结点

  return OK;

 }//OrderInsert

void OpenForDay()

{

int i;

InitList(ev);

en.OccurTime=0;

en.NType=Qu;

OrderInsert(ev,en,cmp);//插入事件表

for(i=0;i<Qu;++i)

       InitQueue(q[i]);

}//OpenForDay

void Random(int &d,int&i)

{

d=rand()%Blsj+1;//1到Blsj之间的随机数

i=rand()%Khjg+1;

}//Random

int QueueLength(LinkQueue Q)

{ // 求队列的长度

  int i=0;

  QueuePtr p;

  p=Q.front;

  while(Q.rear!=p)

   {

    i++;

    p=p->next;

   }

  return i;

}//QueueLength

int Minimum(LinkQueue Q[]) //返回最短队列的序号

{

int l[Qu],i,k;

for(i=0;i<Qu;i++)

       l[i]=QueueLength(Q[i]);

k=0;

for(i=1;i<Qu;i++)

       if(l[i]<l[0])

       {

       l[0]=l[i];

       k=i;

       }//if

return k;

}//Minimum

Status EnQueue(LinkQueue&Q,QElemType e)

{ // 插入元素e为Q的新的队尾元素

  QueuePtr p;

   if(!(p=(QueuePtr)malloc(sizeof(QNode))))// 存储分配失败

    exit(OVERFLOW);

  p->data=e;

  p->next=NULL;

  Q.rear->next=p;

  Q.rear=p;

  return OK;

}//EnQueue

Status DeQueue(LinkQueue&Q,QElemType &e)

 { //若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR

  QueuePtr p;

  if(Q.front==Q.rear)

    return ERROR;

  p=Q.front->next;

  e=p->data;

  Q.front->next=p->next;

  if(Q.rear==p)

    Q.rear=Q.front;

   free(p);

  return OK;

 }//DeQueue

void CustomerArrived()

{

QElemType f;

int durtime,intertime,i;

++CustomerNum;

Random(durtime,intertime);

et.OccurTime=en.OccurTime+intertime;//下一客户到达时刻,隔了intertime来了下一//个客户

et.NType=Qu; // 队列中只有一个客户到达事件

if(et.OccurTime<CloseTime)

       OrderInsert(ev,et,cmp);  //不让下一个进入营业厅了

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;

    OrderInsert(ev,et,cmp); // 设定第i队列的一个离开事件并插入事件表

  }//if

}//CustomerArrived

Status GetHead(LinkQueueQ,QElemType &e)

{ // 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR

  QueuePtr p;

  if(Q.front==Q.rear)

    return ERROR;

  p=Q.front->next;

  e=p->data;

  return OK;

}//GetHead

 Position GetHead(LinkList L)

 { //返回线性链表L中头结点的位置

  return L.head;

 }//GetHead

Status QueueEmpty(LinkQueue Q)

 { //若Q为空队列,则返回TRUE,否则返回FALSE

  if(Q.front==Q.rear)

    return TRUE;

  else

    return FALSE;

 }//QueueEmpty

void CustomerDeparture()

{ // 处理客户离开事件,en.NType<Qu

int i;

i=en.NType;

DeQueue(q[i],customer);

TotalTime+=en.OccurTime-customer.ArrivalTime;

if(!QueueEmpty(q[i])) //设定第i队列的一个离开事件,并插入事件表

  {

  GetHead(q[i],customer);

   et.OccurTime=en.OccurTime+customer.Duration;

  et.NType=i;

  OrderInsert(ev,et,cmp);

 }//if

}//CustomerDeparture

 Status ListEmpty(LinkList L)

 { //若线性链表L为空表,则返回TRUE,否则返回FALSE

  if(L.len)

    return FALSE;

  else

    return TRUE;

 }//ListEmpty

Status DelFirst(LinkList&L,Link h,Link &q) // 形参增加L,因为需修改L

{ // h指向L的一个结点,把h当做头结点,删除链表中的第一个结点并以q返回。

   //若链表为空(h指向尾结点),q=NULL,返回FALSE

  q=h->next;

  if(q) // 链表非空

   {

     h->next=q->next;

    if(!h->next) // 删除尾结点

      L.tail=h; // 修改尾指针

    L.len--;

    return OK;

   }

  else

    return FALSE; // 链表空

}//DelFirst

ElemType GetCurElem(Link p)

{ // 已知p指向线性链表中的一个结点,返回p所指结点中数据元素的值

  return p->data;

}//GetCurElem

void Bank_Simulation()

{

Link p;

OpenForDay();

while(!ListEmpty(ev))

  {

  DelFirst(ev,GetHead(ev),p);

   en.OccurTime=GetCurElem(p).OccurTime;

  en.NType=GetCurElem(p).NType;

  if(en.NType==Qu)

          CustomerArrived();

  else

          CustomerDeparture();

 }//while

printf("顾客总数:%d, 所有顾客共耗时:%d分钟, 平均每人耗时: %d分钟\n",CustomerNum,TotalTime,TotalTime/CustomerNum);

}//Bank_Simulation

int main()

{

printf("请输入银行营业时间长度(单位:分)\n");

scanf("%d",&CloseTime);

Bank_Simulation();

return OK;    

}

四、举例

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟

42、离散事件模拟----银行业务模拟

五、说明

相当于一个时间轴,但是管着四个队列的进出情况。

第个队列的人员进入时,都要预报下一个人员进来的时刻,并登记下一个人员进来时刻到时间轴,同时将自己进来的时刻及逗留时间选择一个最短的队列进行注册,如果自己是第一个进来的,或者可以排到某一队列的第一位,则还要在时间轴上登记出的时间。

离开时,自己将注册的队列信息删除,同时,当前队列的下一个人向时间轴预报自己离开的时间。

可见,时间轴就是一个事件(无论是来了的,还是走了的)集,通过预判事件的先后发生顺序而控制着整个过程的发展。

 

相关文章: