银行业务模拟系统的设计与实现

【实验目的】

  1. 了解离散事件的模拟机制(模拟银行系统);
  2. 掌握队列的基本操作,加深对队列数据结构的理解;
  3. 加强抽象数据类型的能力。

【实验原理】

  1. 创建队列类的定义,时间类和银行类的定义。
  2. 队列的每个元素为一个客户,每个客户有一个客户编号val,采用链式队列存储,可进行出队、入队、判断队空、返回队长等基本操作。
  3. 使用时间类表示时间,精确到分钟,可通过运算符重载实现基本运算。
  4. 使用银行类模拟银行的运转,包含开门关门时间和4个窗口(队列)。操作包括:初始化OpenForDay, 事件驱动EventDrived(),下班处理CloseForDay()

【需求分析】

  1. 问题描述
    假设某银行有四个窗口对外接待客户,从早晨银行开门起不断有客户进入银行。由于每个窗口在某个时刻只能接待一个客户,因此在客户人数众多时需在每个窗口前顺次排队,对于刚进入银行的客户,如果某个窗口的业务员正空闲,则可上前办理业务,反之,若四个窗口均有客户所占,他便会排在人数最少的队伍后面。现在需要编制程序以模拟银行的这种业务活动。

  2. 基本要求
    (1)初始化(OpenForDay),模拟银行开门时各数据结构的状态。
    (2)事件驱动(EventDrived), 对客户到达和离开事件做相应处理。
    (3)下班处理(CloseForDay),模拟银行关门的动作,统计客户平均逗留时间。

【概要设计】

  1. 数据结构:
    (1)队列Queue的每个元素Customer为一个客户,每个客户有一个客户编号val,采用链式队列存储,可进行出队、入队、判断队空、返回队长等基本操作。
    (2)使用时间类Time表示时间,数据成员包括小时hour和分钟minute,默认时间初始化为0:0,可手动输入时间,可通过运算符重载实现时间的>、<、==、+、-、/等运算。
    (3)使用银行类Bank模拟银行的运转,数据成员包括:开门关门时间Open, Close;进入银行的客户总数AllNum,完成业务办理的客户总数TrueNum;每位客户进门时间InTime和离开时间OutTime;以及4个窗口(队列)。数据操作包括:初始化OpenForDay, 事件驱动EventDrived(),下班处理CloseForDay(),发现最短队列GetShort(),显示窗口排队状况ShowWindows()

  2. 程序模块:
    (1)基本定义模块;
    (2)队列操作模块;
    (3)时间操作模块;
    (4)银行操作模块
    ①初始化OpenForDay()
    ②事件驱动EventDrived()
    ③下班处理CloseForDay()

  3. 各模块之间调用关系及算法设计:
    基本定义模块包括队列、时间、银行类的定义;队列、时间、银行模块包括其类的基本操作,银行类是队列类、时间类的友元类,银行的操作包含了队列操作模块中的出入队、时间操作模块中时间运算。

  4. 其他
    规定每分钟20%的概率来一位客户,一位客户有10%的概率在窗口办理时间超过1小时

【程序清单】

BasicStruct.h

#pragma once
#include <iostream>
#define OK 0
#define ERR -1
#define MAXSIZE 400
using namespace std;
typedef int status;
//顾客定义
typedef struct Customer
{
	int val;	//顾客编号
	struct Customer *next;	//后继指针
}Node;
//链式队列定义
class Queue
{
public:
	void InitQueue();	//初始化队列
	bool IsEmpty();		//判断队列是否为空
	void EnQueue(int val);//入队
	int GetHead();	//取队头元素
	void DeQueue();	//出队
	int GetLength();//求队列长度
	friend class Bank;
private:
	Node *front;	//队头指针
	Node *rear;		//队尾指针
};
//时间的定义
class Time
{
public:
	Time() :hour(0), minute(0) {}
	Time(int h, int m) :hour(h), minute(m) {}
	Time(const Time &b);
	void SetTime();	//设置时间
	void display();	//读取时间
	bool operator> (Time &time2);	//时间比较运算>
	bool operator< (Time &time2);	//时间比较运算<
	bool operator== (Time &time2);	//时间比较运算==
	Time operator+ (const Time &time2);	//时间相加+
	Time operator- (const Time &time2);	//时间相减-
	Time operator/ (int x);	//时间相除/
	Time& operator++ ();	//时间自增一分钟(前置)
	friend class Bank;
private:
	int hour, minute;
};
//银行的定义
class Bank
{
public:
	Queue* Windows = new Queue[4];//四个服务窗口
	Time Open, Close;		//开门关门时间
	status OpenForDay();	//初始化(OpenForDay),模拟银行开门时各数据结构的状态。
	status EventDrived(Time Now);//事件驱动(EventDrived), 对客户到达和离开事件做相应处理。
	status CloseForDay();	//下班处理(CloseForDay),模拟银行关门时的动作,统计客户平均逗留时间。
	int GetShort();			//发现最短队列
	void ShowWindows();		//显示窗口排队状况
private:
	int AllNum;		//进入银行的客户总数
	int TrueNum;	//完成业务办理的客户总数
	Time* InTime = new Time[MAXSIZE];//每位客户进门时间.
	Time* OutTime = new Time[MAXSIZE];//每位客户离开时间.
};

QueueOperation.cpp

#include "BasicStruct.h"
//初始化队列
void Queue::InitQueue()
{
	front = rear = new Node;
	if (!front)
		exit(OVERFLOW);
	front->next = NULL;
}

//判断队列是否为空
bool Queue::IsEmpty()
{
	return (front == rear);
}

//入队
void Queue::EnQueue(int val)
{
	Node *newNode = new Node;
	newNode->val = val;
	newNode->next = NULL;
	if (newNode == NULL)
		return;
	else {
		rear->next = newNode;
		rear = rear->next;
	}
}

//取队头元素
int Queue::GetHead()
{
	if (front != rear)
		return front->next->val;
	else
	{
		return ERR;
	}
}

//出队
void Queue::DeQueue()
{
	if (IsEmpty())
		return;
	else
	{
		int val = front->next->val;
		Node *deNode = front->next;
		front->next = deNode->next;

		if (rear == deNode) //only one element
			rear = front;

		delete deNode;
	}
}

//求队列长度
int Queue::GetLength()
{
	int i = 0;
	Node *p;
	p = front;
	while (rear != p)
	{
		p = p->next;
		i++;
	}
	return i;
}

TimeOperation.cpp

#include "BasicStruct.h"
//拷贝构造函数
Time::Time(const Time &b)
{
	this->hour = b.hour;
	this->minute = b.minute;
}

//设置时间
void Time::SetTime()
{
	int h, m;
	cin >> h >> m;
	if (h >= 0 && m >= 0) {	//判断输入值合法性
		hour = h, minute = m;
	}
	else
		return;
}

//读取时间
void Time::display()
{
	cout << "          " << hour << ": " << minute;
}

//时间比较运算>
bool Time::operator> (Time &time2)
{
	if (hour > time2.hour) { return true; }
	else if (hour == time2.hour && minute > time2.minute) { return true; }
	else { return false; }
}

//时间比较运算<
bool Time::operator< (Time &time2)
{
	if (hour < time2.hour) { return true; }
	else if (hour == time2.hour && minute < time2.minute) { return true; }
	else { return false; }
}

//时间比较运算==
bool Time::operator== (Time &time2)
{
	if (hour == time2.hour && minute == time2.minute) { return true; }
	else { return false; }
}

//时间相加+
Time Time::operator+ (const Time &time2)
{
	hour = hour + time2.hour;
	minute = minute + time2.minute;
	//加法,分、秒满60进位
	if (minute >= 60) {
		hour++;
		minute = minute % 60;
	}
	return Time(hour, minute);
}

//时间相减-
Time Time::operator- (const Time &time2)
{
	hour = hour - time2.hour;
	minute = minute - time2.minute;
	//减法借位
	if (hour > 0 && minute < 0) {
		hour--; minute += 60;
	}
	else if (hour < 0 && minute > 0) {
		hour--; minute -= 60;
	}
	return Time(hour, minute);
}

//时间相除
Time Time::operator/ (int x)
{
	if (x != 0) {
		int t = (hour * 60 + minute) / x;
		hour = -(int)(t / 60);
		minute = t % 60;
		return Time(hour, minute);
	}
	else {
		exit;
	}
}

//时间自增一分钟(前置)
Time & Time::operator++ ()
{
	minute++;
	if (minute == 60) {
		hour++; minute = 0;
	}
	return *this;
}

BankOperation.cpp

#include "BasicStruct.h"
#include <time.h>
#define random(x) rand()%x

//初始化(OpenForDay),模拟银行开门时各数据结构的状态。
status Bank::OpenForDay()
{
	Open.SetTime();	//设置开门时间
	Close.SetTime();//设置关门时间
	Windows[0].InitQueue();//初始化各窗口
	Windows[1].InitQueue();
	Windows[2].InitQueue();
	Windows[3].InitQueue();
	AllNum = 0;
	TrueNum = 0;
	return OK;
}

//返回最短队列编号
int Bank::GetShort()
{	//有4个窗口可选择
	int choice[4] = { Windows[0].GetLength(), Windows[1].GetLength(), Windows[2].GetLength(), Windows[3].GetLength() };
	int len[4] = { Windows[0].GetLength(), Windows[1].GetLength(), Windows[2].GetLength(), Windows[3].GetLength() };

	//使用冒泡排序求得最短的队列
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3 - i; j++)
		{
			if (len[j] > len[j + 1])
				swap(len[j], len[j + 1]);
		}
	}

	for (i = 0; i < 4; i++)
	{
		if (len[0] == choice[i])
			return i;
	}
}

//事件驱动(EventDrived), 对客户到达和离开事件做相应处理。
status Bank::EventDrived(Time Now) {
	//srand((unsigned)time(NULL));
	int come = random(5);
	if (come == 1) {//每分钟有20%的几率来一位顾客
		InTime[AllNum] = Now;	//记录顾客进门时间
		int i = 0;
		//顾客前往人数最少的窗口,更新窗口服务时间
		while (i < 4) {
			if (i != GetShort()) {
				i++;
			}
			else {// 这里设置每位顾客有10%的几率办理时长超过1小时
				if (!Windows[i].IsEmpty()) {
					int pre = (Windows[i].rear->val);
					Time t(random(10) == 1 ? 1 : 0, random(60));
					OutTime[AllNum] = OutTime[pre - 1] + t;
					break;
				}
				else {
					Time t(random(10) == 1 ? 1 : 0, random(60));
					OutTime[AllNum] = t + Now;
					Windows[i].EnQueue(AllNum);
					break;
				}
			}
		}
		AllNum++;
	}//if
	for (int i = 0; i < 4; i++) {
		Customer* p = Windows[i].front;
		for (int j = 0; j < Windows[i].GetLength(); j++) {
			if (p->next == NULL)
				break;
			p = p->next;
			if (Now == OutTime[p->val]) {
				Windows[i].DeQueue();// 服务完的客户从队列中删除
				TrueNum++;// 已服务人数加一
			}
		}
	}
	return OK;
}

//下班处理(CloseForDay),模拟银行关门时的动作,统计客户平均逗留时间。
status Bank::CloseForDay()
{	//计算并显示客户平均逗留时间
	Time AveTime(0, 0);
	for (int j = 0; j < AllNum; j++) {
		if (OutTime[j] > Close) {
			continue;	//只统计完成业务办理的客户,仍在排队的不计,队伍解散
		}
		else {
			AveTime = AveTime + OutTime[j] - InTime[j];//出门时间减去进门时间
			if (j < 10) cout << " " << 1 + j;	//打印客户编号
			else cout << 1 + j;
			InTime[j].display();	//打印客户进门时间
			OutTime[j].display();	//打印客户出门时间
			(OutTime[j] - InTime[j]).display();//打印客户逗留时间
			cout << endl;
		}
	}
	AveTime = AveTime / TrueNum;
	cout << "\n完成业务办理的客户总数为:" << TrueNum << endl;//打印完成业务办理的客户数
	cout << "人均逗留时间为";	//打印客户平均逗留时间
	AveTime.display();
	cout << endl;

	//清空所有队列,请未完成业务办理的客户改日再来
	for (int i = 0; i < 4; i++)
	{
		while (!Windows[i].IsEmpty())
		{
			Windows[i].DeQueue();
		}
	}
	return OK;
}

//显示窗口状况
void Bank::ShowWindows()
{	//依次打印各个窗口前的顾客

	for (int i = 0; i < 4; i++) {
		Customer* p = Windows[i].front;
		cout << i + 1 << "号窗口  ";
		while (p != Windows[i].rear)
		{
			p = p->next;
			cout << " " << p->val + 1 << " ";
		}
		cout << endl;
	}
}

Main.cpp

#include <Windows.h>
#include "BasicStruct.h"
int main() {
	Bank ABC;
	cout << "设置银行上班时间和下班时间:" << endl;
	ABC.OpenForDay();//初始化
	Time now(ABC.Open);
	//模拟顾客排队和离队
	while (true)
	{
		system("cls");
		cout << "----------------------------------------" << endl;
		now.display();
		cout << "\n----------------------------------------" << endl;
		cout << endl;
		ABC.ShowWindows();//显示窗口状况
		ABC.EventDrived(now); //事件驱动
		++now;
		//system("pause");
		if (now == ABC.Close)
			break;
	}
	cout << endl;
	cout << "----------------------------------------" << endl;
	cout << "客户编号  " << "进门时间  " << "出门时间  " << "停留时间  " << endl;;
	ABC.CloseForDay();//下班
	system("pause");
	return 0;
}

运行结果
输入上班和下班时间,这里假设9点上班,12点下班
图1
图2
图3
在这里插入图片描述


版权声明:本文为LeopoldZhang2000原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。