文章学习自:小项目分析之C++ 实现模拟银行排队
使用C++实现银行排队系统
一、问题定义与分析
问题定义
•要解决的问题——银行一天之内的:
1.总客户数
2.客户总逗留时间
3.客户平均逗留时间
问题分析
•新来的人找个短的队伍,站在队尾开始排队
•排在队头的人可以办理业务
•排队等待办业务的客户是在分散的、随机的时间点到来的
•特点:离散事件、要排队
•掌握每个客户到达银行和离开银行这两个时刻
•统计出客户总数
•称客户到达银行和客户离开银行这两个时刻发生的事情为“事件”
•整个模拟按事件发生的先后顺序进行处理
•事件驱动模拟
•事件的主要信息是事件类型和事件发生的时刻
•两类事件:客户到达事件和客户离开事件
•事件应存储在有序表里
•有序表按照事件发生的时刻顺序排序
•队列中的客户的主要信息是客户到达的时刻和客户办理业务所需要的时间
•队列数量和银行的窗口数量相同
•每个队列的队头客户就是正在办理业务的客户
•每个队头客户都存在一个将要发生的客户离开事件
二、类与算法设计
类设计
•有序表选用有序链表,主要操作是插入和删除
•队列,客户排队
•类图:用来表示类以及类和类之间的关系的逻辑视图
•利用类图来记录类的结构,这些类构成了程序的架构
银行类:
•要有一个表示队列数量的属性
•要有一个打烊时间属性
•要有一个总客户数属性
•要有一个客户总逗留时间属性
•聚合一个或多个队列和一个有序链表
银行类的方法:
•开门营业
•处理客户到达事件
•处理客户离开事件
•帮助客户选择一个最短的队列
•确保按照事件发生的时间顺序处理事件
算法设计
一共定义三个类事件类Event、客户类Client、银行类Bank
事件类Event:包含两个成员,事件发生的时间,与时间的类型,类型主要是客户到达事件还是客户离开事件。
客户类Client:包含两个成员,客户到达的时间与客户停留的时间
银行类Bank:
包括成员队列数量(即银行窗口数量)、客户人数、客户总逗留时间、打烊时间、以及窗口队列数组、事件链表
包括方法:
开门营业void_open_for_day();
将客户人数、总逗留时间归零,并且随机产生第一个客户的到达时间,加入到事件链表中;
处理客户到达事件void_handle_arrival_event(Event*event);
1)产生客户逗留时间,最长为30min
2)产生下一个客户到达的时间间隔,最长为10min,计算下一个客户到达时间,若时间小于打烊时间,则产生新的客户到达事件插入到事件链表中,并排序
3)选择窗口队列中最短的队列
4)生成客户成员,并且插入到最短的队列中
5)若队列中只有一个客户,则产生客户离开事件,并插入到事件链表中,排序链表(重点)
6)增加客户总数量
处理客户离开时间void_handle_leave_event(Event*event);
1)增加客户总的逗留时间
2)将客户成员从相应的队列中取出
3)若队列中仍然有客户成员,则产生该客户的离开事件插入到事件链表中,并排序
返回最短队列int_find_short_queue();
时间链表排序void_sort_event();
仿真函数void_simulation();
1)调用开门营业函数,初始化各项数据
2)依次从事件链表中取出事件,并且根据事件的类型调用相应的函数进行处理,知道事件链表为空
3)返回相应的数据
源码:
bank.h 文件
#ifndef BACK_H
#define BACK_H
#include
#include
#include
#include
#include
using namespace std;
//事件类,成员为事件发生的时间与事件的类型
struct Event{
int _occur_time; //时间发生的时间
int _type; //时间的类型,0代表到达,1-n代表1-n号窗口的离开
bool operator<(const Event &event)
{
return _occur_time < event._occur_time;
}
};
//客户类,成员为客户到达的时间和逗留的时间
struct Client{
int _arrival_time; //客户到达的时间
int _stay_time; //客户逗留的时间
};
//银行类
class Bank{
public:
Bank():_queue_number(4),_client_number(0),_total_stay_time(0),_snoring_time(8*3600)
{
_window_queue = new queue[_queue_number];
srand((unsigned)time(NULL)); //产生随机数种子,为了使每次生成的随机数不同
}
Bank(int _work_number, int _close_time);
~Bank();
void _simulation();
private:
int _queue_number; //队列数量,即银行窗口的数量
int _client_number; //客户总个数
int _total_stay_time; //客户总逗留时间
int _snoring_time; //打烊时间
queue *_window_queue; //银行窗口队列,成员为客户
list _event_list; //银行事件的有序链表
void _open_for_day(); //开门营业
void _handle_arrival_event(Event *event); //处理客户到达事件
void _handle_leave_event(Event *event); //处理客户离开事件
int _find_short_queue(); //返回最短队列
void _sort_event(); //事件链表排序
};
#endif // BACK_H
bank.cpp 文件
#include "back.h"
#include
#include
#include
#include
using namespace std;
Bank::Bank(int _work_number, int _close_time):_queue_number(_work_number),_snoring_time(3600*_close_time),
_client_number(0),_total_stay_time(0)
{
_window_queue = new queue[_queue_number];
srand((unsigned)time(NULL)); //产生随机数种子,为了使每次生成的随机数不同
}
Bank::~Bank()
{
delete []_window_queue;
}
void Bank::_open_for_day()
{
_client_number = 0;
_total_stay_time = 0;
int i = rand()%600; //第一个客户在开门十分钟内随机到达
_event_list.push_back({i, 0});
}
/* * 1)产生客户逗留时间,最长为30min
** 2)产生下一个客户到达的时间间隔,最长为10min,计算下一个客户到达时间,若时间小于打烊时间,则产生新的客户到达事件 插入到事件链表中,并排序
** 3)选择窗口队列中最短的队列
** 4)生成客户成员,并且插入到最短的队列中
** 5)若队列中只有一个客户,则产生客户离开事件,并插入到事件链表中,排序链表(重点)
** 6)增加客户总数量
*************************/
void Bank::_handle_arrival_event(Event *event)
{
int _stat_time; //逗留时间
int _next_client_time; //下一个客户到达间隔时间
_stat_time = rand()%1800 + 1; //随机产生逗留时间
_next_client_time = rand()%600 + 1; //随机产生下一个客户到达时间
//如果下一个客户的到达时间超过了打烊时间,则不进行处理,否则插入有序数列
if(_next_client_time + event->_occur_time < _snoring_time)
{
Event _next_client_arrival; //创建下一个客户到达时间
_next_client_arrival._occur_time = _next_client_time + event->_occur_time;
_next_client_arrival._type = 0;
_event_list.push_back(_next_client_arrival);
_sort_event();
}
int i; //选择最短队列
i = _find_short_queue();
_window_queue[i].push({event->_occur_time, _stat_time}); //将到达的客户插入到最短队列中
//若队列中只有一个客户时,则产生一个离开时间插入到时间序列中
if(_window_queue[i].size() == 1)
{
_event_list.push_back({event->_occur_time + _stat_time, i+1}); //注意最后为i+1
_sort_event();
}
++_client_number;
}
/* 1)增加客户总的逗留时间
2)将客户成员从相应的队列中取出
3)若队列中仍然有客户成员,则产生该客户的离开事件插入到事件链表中,并排序
************************/
void Bank::_handle_leave_event(Event *event)
{
Client client = _window_queue[event->_type - 1].front(); //取出离开事件对用的客户
_total_stay_time += client._stay_time;
_window_queue[event->_type - 1].pop(); //客户出队列
if(!_window_queue[event->_type - 1].empty())
{
Client next_client = _window_queue[event->_type - 1].front();
_event_list.push_back({next_client._arrival_time + next_client._stay_time, event->_type}); //产生新的离开事件插入事件链表中
_sort_event(); //队列排序
}
}
int Bank::_find_short_queue()
{
int minSize = 0xFFFF;
int index = -1;
for(int i = 0; i < _queue_number; ++i){
if(_window_queue[i].size() < minSize){
minSize = _window_queue[i].size();
index = i;
}
}
return index;
}
void Bank::_sort_event()
{
_event_list.sort([](const Event &event1, const Event &event2){return event1._occur_time < event2._occur_time;});
_event_list.sort();
}
/* 1)调用开门营业函数,初始化各项数据
2)依次从事件链表中取出事件,并且根据事件的类型调用相应的函数进行处理,知道事件链表为空
3)返回相应的数据
***********************/
void Bank::_simulation()
{
_open_for_day(); //开门营业
Event event;
while(!_event_list.empty())
{
event = _event_list.front(); //取出序列表最前边的事件
_event_list.pop_front(); //删除序列表最前边的事件
if(event._type == 0)
_handle_arrival_event(&event);
else
_handle_leave_event(&event);
}
cout << "客户数:" << _client_number << endl
<< "客户总逗留时间:" << (double)_total_stay_time/3600 << "小时" << endl
<< "平均逗留时间:" << (double)_total_stay_time/(double)(_client_number * 60) << "分钟"
<< endl;
}
main 文件
#include
#include "back.h"
using namespace std;
int main(int argc, char *argv[])
{
Bank bank;
bank._simulation();
return 0;
}