银行事件模拟程序(第三章 P63-69 算法3.6,3.7)

银行事件模拟程序

 

问题简述:

假设某银行有 4 个窗口接待客户,从早晨银行开门起不断有客户进入银行。由于每个窗口在某个时刻只能接待一个客户,因此在客户人多的时候,需要在每个窗口前顺次排队。对于刚进入银行的客户,如果某个窗口的业务员正空闲,则可以上前办理业务;反之,若 4 个窗口均有客户,他便会排在人数最少的队列后面。现需要编制一个程序以模拟银行的这种业务活动并计算一天中客户在银行逗留的时间。

思考:

(1)需处理两类事件:客户到达事件,客户离开事件。

客户到达事件发生的时刻随客户到来自然形成;客户离开事件由客户事务所需时间和等待所耗时间决定。

由于程序驱动是按事件发生时刻的先后顺序进行,则事件表应是有序表,其主要操作是插入和删除事件。

(2)由于银行有 4 个窗口,因此程序需要 4 个队列,队列中有关客户的主要信息是客户到达的时刻和客户办理事务所需的时间。

以下,我们随机产生下一个客户到达时刻距离此刻客户到达相差的时间,这个用户办理事务所需时间也随机产生,这两个随机时间都是此时刻客户到达时产生。由此,客户到达事件的驱动因素就是上一个客户到达(即可以插入下一个客户到达事件)。

客户离开事件的驱动因素有两个:一个是,客户一到达,就有窗口空闲,则已经可以计算客户离开时间,插入客户离开事件。第二个是,一个客户离开,则他所在队伍的下一个客户可以开始办理事务,则已经可以计算下一个客户离开时间,插入客户离开事件。

 

/* 银行业务模拟。实现算法3.6、3.7的程序 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */

/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 


#define Qu 4 /* 客户队列数 */
#define Khjg 5 /* 两相邻到达的客户的时间间隔最大值 */
#define Blsj 30 /* 每个客户办理业务的时间最大值 */


typedef struct /* 定义ElemType为结构体类型 */
{
	int OccurTime; /* 事件发生时刻 */
	int NType; /* 事件类型,Qu表示到达事件,0至Qu-1表示Qu个窗口的离开事件 */
}Event, ElemType; /* 事件类型,有序链表LinkList的数据元素类型 */




/* -----------------------      带头结点的线性链表类型   ----------------------------*/

typedef struct LNode /* 结点类型 */
{
	ElemType data;
	struct LNode *next;
}LNode, *Link, *Position;

typedef struct LinkList /* 链表类型 */
{
	Link head, tail; /* 分别指向线性链表中的头结点和最后一个结点 */
	int len; /* 指示线性链表中数据元素的个数 */
}LinkList;

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

typedef struct
{
	int ArrivalTime; /* 到达时刻 */
	int Duration; /* 办理事务所需时间 */
}QElemType; /* 定义QElemType(队列的数据元素类型)为结构体类型; */


/* -----------------------   单链队列--队列的链式存储结构  ----------------------------*/

typedef struct QNode
{
	QElemType data;
	struct QNode *next;
}QNode, *QueuePtr;

typedef struct
{
	QueuePtr front, rear; /* 队头、队尾指针 */
}LinkQueue;

/* ------------------------------------------------------------------------------------------*/


/* ---------------------------   需要用到的具有实用意义的线性链表操作  --------------------------*/


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;
}

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; /* 链表空 */
}

ElemType GetCurElem(Link p)
{ /* 已知p指向线性链表中的一个结点,返回p所指结点中数据元素的值 */
	return p->data;
}

Status ListEmpty(LinkList L)
{ /* 若线性链表L为空表,则返回TRUE,否则返回FALSE */
	if (L.len)
		return FALSE;
	else
		return TRUE;
}

Position GetHead(LinkList L)
{ /* 返回线性链表L中头结点的位置 */
	return L.head;
}

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;
}




/* --------------------------------   需要用到的链队列操作  -------------------------------*/

Status InitQueue(LinkQueue *Q)
{ /* 构造一个空队列Q */
	(*Q).front = (*Q).rear = (QueuePtr)malloc(sizeof(QNode));
	if (!(*Q).front)
		exit(OVERFLOW);
	(*Q).front->next = NULL;
	return OK;
}

Status QueueEmpty(LinkQueue Q)
{ /* 若Q为空队列,则返回TRUE,否则返回FALSE */
	if (Q.front == Q.rear)
		return TRUE;
	else
		return FALSE;
}

int QueueLength(LinkQueue Q)
{ /* 求队列的长度 */
	int i = 0;
	QueuePtr p;
	p = Q.front;
	while (Q.rear != p)
	{
		i++;
		p = p->next;
	}
	return i;
}

Status GetHead_Q(LinkQueue Q, QElemType *e) /* 避免与bo2-6.c重名 */
{ /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */
	QueuePtr p;
	if (Q.front == Q.rear)
		return ERROR;
	p = Q.front->next;
	*e = p->data;
	return OK;
}

Status EnQueue(LinkQueue *Q, QElemType e)
{ /* 插入元素e为Q的新的队尾元素 */
	QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
	if (!p) /* 存储分配失败 */
		exit(OVERFLOW);
	p->data = e;
	p->next = NULL;
	(*Q).rear->next = p;
	(*Q).rear = p;
	return OK;
}

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;
}



/* ------------------------------------------------------------------------------------------*/



/* 程序中用到的主要变量(全局)。算法3.7 */
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);
}

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]);
}

void Random(int *d, int *i)
{
	*d = rand() % Blsj + 1; /* 1到Blsj之间的随机数 */
	*i = rand() % Khjg + 1; /* 1到Khjg之间的随机数 */
}

int Minimum(LinkQueue Q[]) /* 返回最短队列的序号 */
{
	int l[Qu];
	int 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;
		}
	return k;
}

void CustomerArrived()
{ /* 处理客户到达事件,en.NType=Qu */
	QElemType f;
	int durtime, intertime, i;
	++CustomerNum;
	Random(&durtime, &intertime); /* 生成随机数 */
	et.OccurTime = en.OccurTime + 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队列的一个离开事件并插入事件表 */
	}
}

void CustomerDeparture()
{ /* 处理客户离开事件,en.NTyPe<Qu */
	int i;
	i = en.NType;
	DeQueue(&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;
		OrderInsert(&ev, et, cmp);
	}
}

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(); /* 处理客户离开事件 */
	} /* 计算并输出平均逗留时间 */
	printf("顾客总数:%d, 所有顾客共耗时:%d分钟, 平均每人耗时: %d分钟\n", CustomerNum, TotalTime, TotalTime / CustomerNum);
}

void main()
{
	printf("请输入银行营业时间长度(单位:分)\n");
	scanf("%d", &CloseTime);
	Bank_Simulation();
}

运行结果:

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
假设某银行有n个窗口对外接待客户,从早晨银行9点开门起到5点关门,不断有客户进入银行,由于每个窗口在某个时刻只能接待一个客户。因此在客户人数众多时需要在每个窗口前顺次排队,对于刚进银行的客户。如果某个窗口的业务员正空闲,则可上前输业务。反之,若个窗口均有客户所占,他便会排在为数最少的队伍后面。编制一个程序模拟银行的这种业务活动并计算一天中客户在银行的平均逗留时间。 首先从题目分析:N个窗口排队,首先就要建立N个队列来存储排队的用户信息 ,然后算出那个队列最短就用户就到那个队伍排队,同时通过随机生成他办理业务的时间和到来的时间,通过计算用户的到来时间和离开时间就可以计算出某个用户在银行的逗留时间 ;话不多说直接上代码。 下面是主函数,由用户输入银行上下班时间,计算营业多长时间Total_time,如何当前时间小于关门的时间,就一直进入customer_into();函数,用户不断的进来 #define FALSE 0 #define TRUE 1 #define QUEUE_SUM 4 //窗口的数量 int rand_business_time=0, rand_wait_time=0;//定义办理时间,等待时间变量 int Total_time=0,now_tim=0;//总时间,当前时间 int go_time[4] = {0,0,0,0};//定义数组存储每个窗口最后一位办理完业务的时间 int sum_nan[4] = {0,0,0,0};//定义数组存储每个窗口排队的人数 int Sign=TRUE; //是否关门标志位 float Sum_Wait_Time=0.0; //等待的总时间 float Sun_Nan=0.0; //总人数 int open_time;//开门时间 int off_time; //关门时间 int main() { Prompted(); printf("输入银行的24小时制营业时间:如营业时间为9:00--17:00,则应输入:9,17\n"); scanf("%d,%d", &open;_time,&off;_time); Total_time = (off_time - open_time) * 60;//计算银行总营业多少分钟 for (int i = 0; i now_time) { customer_into(); //客户进入函数 } printf("银行关门时间到不再接收客人\n\n"); for (int i = 0; i < QUEUE_SUM; i++) { DisposeQueue(&queue;[i],i);//输入在银行关门前还没有办理完业务的客户信息 } printf("平均时间为%.2f分钟",Sum_Wait_Time/Sun_Nan); /*通过各个客户的总等待时间总和/总人数算出逗留平均时间*/ _getch(); return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值