银行排队模拟(队列,模拟,数据结构,待补)

问题描述:
已知:
(1)银行有多个窗口
(2)存在不同优先级的客户
要求:
(1)模拟计算客户的到达,服务,离开的过程
(2)模拟计算客户的总逗留事件。

思路:
3种解决方向
1.STL队列与vector实现该问题。
2.C语言数据结构实现链式链表实现该问题。
3.C++面向对象程序设计结合事件驱动编程。

实现:
设计一个队列类(C++prime plus P461)
(1)队列存储有序的项目序列
(2)能够创建空队列
(3)队列空间有一定限制
(3)能够检查队列是否为空
(4)能够检查队列是否已满
(5)能够在队尾添加元素(push操作)
(6)能够在队首删除元素(pop操作)
(7)能够确定队列中的项目数

1. STL队列与vector实现该问题

基础队列模拟部分:

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int main()
{
    queue<int>Customer[3];//两类客户
    vector<int>num(3,0);//窗口编号,不妨假设存在2个窗口
    int T;//客户个数
    cin>>T;
    int n = T;
    while(T--)
    {
        char s;
        cin>>s;
        if(s == 'V')//VIP客户
        {
            Customer[0].push(1);
            num[1]++;
        }
        else if(s == 'N')//normal普通客户
        {
            Customer[0].push(2);
            num[2]++;
        }
    }

    while(n--)
    {
        int _time;
        cin>>_time;//输入时间
        Customer[Customer[0].front()].push(_time);
        Customer[0].pop();
    }

    vector<int>sum(4,0);//求出每类客户的平均时间。
    while(!Customer[1].empty())
    {
        sum[1]+= Customer[1].front();
        Customer[1].pop();
    }

    while(!Customer[2].empty())
    {
        sum[2]+= Customer[2].front();
        Customer[2].pop();
    }

    cout<<"VIP客户的平均办理时间:"<<sum[1]/num[1]<<"\n"<<"NORMAL客户的平均办理时间:"<<sum[2]/num[2]<<endl;
}

问题:上述算法仅仅求出了每种客户服务时间的平均值,但并没有考虑排队问题,也就是排队时间 + 服务时间 = 逗留事件。既然要模拟排队过程,那就必须确定某一时间某个人选择了哪个窗口,实现这个过程要么有固定的模式,要么就用随机数模拟,下面的一个代码就通过vector矢量类实现了随机过程模拟(power by yz)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include <ctime>
using namespace std;
const int LAST_TIME = 1000;		//持续时间
const int TIME_MAX = 5;			//每个人排队的最大时间


class Customers
{
	int time_use;
	int time_arrive;
	int mode;
	bool type;		//顾客类型,如果type = true代表VIP用户
	int num;		//用户编号
	static int id;
public:
	
	Customers(int time_use = 0,int time_arrive = 0,int mode = -1,bool type = false):time_use(time_use),time_arrive(time_arrive),mode(mode),type(type)
	{
		num = id++;
	}

	~Customers(){}
	int getTime_use() const
	{
		return time_use;
	}

	// 获取到达的时间
	int getTime_arrive() const
	{
		return time_arrive;
	}

	// 返回该顾客所在的位置,0-4代表五个窗口,-1代表在队列中
	int getMode() const
	{
		return mode;
	}

	bool is_VIP() const
	{
		return type;
	}

	int getNum() const
	{
		return num;
	}

	int static getID()
	{
		return id;
	}
};

int Customers::id = 1000;		//人员编号

struct Windows
{
	int number;		//窗口编号
	bool work;				//判断是否在处理
	Customers customer;		//当前窗口的顾客


}windows[5] = { {0,false},{1,false}, {2,false},{3,false},{4,false} };//代表四个窗口

int go();

int main()
{
	int sum = go();
	printf("\n一共服务了%d个人,每个窗口平均处理%d个人\n", Customers().getID()- 1000, (Customers().getID() -1000) / 5);
	printf("人均办理时间: %3d \n", sum / LAST_TIME);
	return 0;
}

//mode = true代表VIP用户,mode == false代表普通用户
void Customer_insert(bool mode, int time, vector<Customers>v)
{
	if (!mode)		//普通用户
	{
		for (int i = 0; i < 5; i++)
		{
			if (!windows[i].work)	//窗口空闲
			{
				windows[i].customer = Customers(1 + rand() % TIME_MAX, time, i);	//普通用户直接进入窗口
				windows[i].work = true;
				printf("当前时间:%5d : 编号为%6d的普通顾客进入窗口%d办理业务!\n\n",time, windows[i].customer.getNum(), i);
				return;
			}
		}
		v.push_back({ 1 + rand() % TIME_MAX,time,-1 });		//窗口均处于办理状态,则刚来的人需要排队,并在队尾
		printf("当前时间:%5d : 编号为%6d的普通顾客前往排队\n\n",time, v.back().getNum());
	}
	else			//VIP用户
	{
		for (int i = 0; i < 5; i++)
		{
			if (!windows[i].work)	//窗口空闲
			{
				windows[i].customer = Customers(1 + rand() % TIME_MAX, time, i, true);	//VIP用户直接进入窗口
				windows[i].work = true;
				printf("当前时间:%5d : 编号为%6d的VIP顾客进入窗口%d办理业务!\n\n", time,windows[i].customer.getNum(), i);
				return;
			}
		}
		for (int i = 0; i < v.size();i++)
		{
			if (v[i].is_VIP())
			{
				continue;
			}
			else
			{
				v.insert(v.begin() + i, { 1 + rand() % TIME_MAX, time, -1, true });
				printf("当前时间:%5d : 编号为%6d的VIP顾客前往排队\n\n",time, v.back().getNum());
				return;
			}
		}
		v.push_back({ 1 + rand() % TIME_MAX, time,-1, true });
		printf("当前时间:%5d : 编号为%6d的VIP顾客前往排队\n\n",time, v.back().getNum());
	}
}

int go()
{
	vector<Customers>v;	//保存排队中的人
	int now = 0;
	srand((unsigned)time(NULL));
	int sum = 0;

	while (1)
	{
		now++;
		if (now >= LAST_TIME)
		{
			for (int i = 0; i < 5;i++)
			{
				if (windows[i].work)
					//先判断是否空闲,然后判断客户是否处理完毕业务
				{
					if(windows[i].customer.is_VIP())
						printf("当前时间:%5d : 编号为%6d的VIP顾客离开了%d窗口\n\n", now, windows[i].customer.getNum(), i);	//输出信息
					else
						printf("当前时间:%5d : 编号为%6d的普通顾客离开了%d窗口\n\n", now, windows[i].customer.getNum(), i);	//输出信息
					windows[i].work = false;		//空闲状态
					sum += 1000 - windows[i].customer.getTime_arrive();
				}
			}
			return sum;
		}
		for (int i = 0; i < 5;i++)
		{
			if (windows[i].work && windows[i].customer.getTime_arrive() + windows[i].customer.getTime_use() <= now)
				//先判断是否空闲,然后判断客户是否处理完毕业务
			{
				//cout <<"get" <<windows[i].customer.getTime_arrive() + windows[i].customer.getTime_user() << " now: "<<now << endl;
				if (windows[i].customer.is_VIP())
					printf("当前时间:%5d : 编号为%6d的VIP顾客离开了%d窗口\n\n", now, windows[i].customer.getNum(), i);	//输出信息
				else
					printf("当前时间:%5d : 编号为%6d的普通顾客离开了%d窗口\n\n", now, windows[i].customer.getNum(), i);	//输出信息
				windows[i].work = false;		//空闲状态
				sum +=windows[i].customer.getTime_use();
			}
		}
		int rand_number = 1 + rand() % 10;
		if (rand_number <= 3)continue;			//3/10的可能性没有人来排队
		else if (rand_number <= 9)				//6/10的可能性为普通用户
		{
			Customer_insert(false, now, v);
		}
		else									//VIP用户
		{
			Customer_insert(true, now, v);
		}
	}
}
    上述代码通过矢量类方便插入的功能实现了排队和插队的功能,同时利用随机数生成模拟每个人排队和办理的过程,中间还建立了Customer类,基本实现了类的结构。
	不过既然学习的是C++和队列模拟,向量的使用再方便,也是不能完全达到学习目的的,接下来的代码分别用纯C和面向对象的方法实现了队列的抽象数据类型。

2.C语言数据结构实现链式链表实现该问题。

//离散事件模拟,模拟银行营业时的排队情况
//不考虑顾客中途离开,顾客到达事件随机,业务办理时间
//长度随机,选择最短的队排队,不再换队
//作者:nuaazdh
//时间:2011年12月10日 08:52:37
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;
typedef struct Event{   //事件类型
    int OccurTime;  //事件发生时刻
    int NType;      //事件类型,0表示到达事件,1至4表示四个窗口的离开事件
    struct Event *next;
}Event,ElemType;

typedef struct{ //单向链表结构
    ElemType *head;//头指针
    ElemType *tail;//尾指针
    int len;    //长度
}LinkList;

typedef LinkList EventList; //事件链表

typedef struct QElemType{ //队列元素
    int ArriveTime;//到达时间
    int Duration;//办理业务所需时间
    struct QElemType *next;
}QElemType;

typedef struct{//队列结构
    QElemType *head;//头指针
    QElemType *tail;//尾指针
}LinkQueue;

Event NewEvent(int occurT,int nType);
    //根据OccurTime和NType值,创建新事件
Status InitList(LinkList *L);
    //初始化事件链表
Status OrderInsert(LinkList *L,Event e);
    //将事件e按发生时间顺序插入有序链表L中
Status ListEmpty(LinkList *L);
    //判断链表L是否为空,为空返回TRUE,否则返回FALSE
Status DelFirst(LinkList *L,ElemType *e);
    //链表L不为空,删除其首结点,用e返回,并返回OK;否则返回ERROR
Status ListTraverse(LinkList *L);
    //遍历链表
Status InitQueue(LinkQueue *Q);
    //初始化队列Q
Status EmptyQueue(LinkQueue *Q);
    //若队列Q为空,返回TRUE,否则返回FALSE
Status DelQueue(LinkQueue *Q,QElemType *e);
    //若队列Q不为空,首结点出队,用e返回,并返回OK;否则返回ERROR
Status EnQueue(LinkQueue *Q,QElemType e);
    //结点e入队Q
int QueueLength(LinkQueue Q);
    //返回队列Q的长度,即元素个数
Status GetHead(LinkQueue *Q,QElemType *e);
    //若队列Q不为空,用e返回其首结点,并返回OK,否则返回ERROR
Status QueueTraverse(LinkQueue *Q);
    //遍历队列Q

//------------------//
int Min(int a[],int n);
    //返回长度为n的数组a第一个最小值的下标,从1开始
int ShortestQueue();
    //获取最短队列的编号
void OpenForDay();
    //初始化操作
void CustomerArrived();
    //顾客达到事件
void CustomerDepature();
    //顾客离开事件
void Bank_Simulation();
    //银行排队模拟
void PrintEventList();
    //输出事件队列
void PrintQueue();
    //打印当前队列
//----全局变量-----//
EventList ev;
Event en;
LinkQueue q[5];
QElemType customer;
int TotalTime,CustomerNum;
int CloseTime=200;//关闭时间,即营业时间长度

//--------------main()------------------//
int main()
{
    Bank_Simulation();
    return 0;
}


//--------------模拟排队----------------//
void OpenForDay(){
    //初始化操作
    int i;
    TotalTime=0;    CustomerNum=0;
    InitList(&ev);//初始化事件队列
    en.OccurTime=0;
    en.NType=0;
    OrderInsert(&ev,en);
    for(i=1;i<=4;i++)
        InitQueue(&q[i]);//初始化四个窗口队列
}//OpenForDay

void CustomerArrived(){
    //顾客达到事件
    int durtime,intertime,i,t;
    QElemType e;
    ++CustomerNum;
    intertime=rand()%5+1;//间隔时间在5分钟内
    durtime=rand()%30+1;//办理业务时间在30分钟内
    t=en.OccurTime+intertime;
    if( en.OccurTime<CloseTime){//银行尚未关门
        printf("A new customer arrived at:%d,his durTime=%d,the next intertime=%d|\n",en.OccurTime,durtime,intertime);//下一位顾客达到时间
        OrderInsert(&ev,NewEvent(t,0));
        i=ShortestQueue();//最短队列
        e.ArriveTime=en.OccurTime;
        e.Duration=durtime;
        EnQueue(&q[i],e);
        if(QueueLength(q[i])==1)
            OrderInsert(&ev,NewEvent(en.OccurTime+durtime,i));
    }else{
        printf("maxinum exceed!stop,en.OccurTime=%d,intertime=%d\n",en.OccurTime,intertime);
    }
}

void CustomerDepature(){
    //顾客离开事件
    int i=en.NType;
    DelQueue(&q[i],&customer);
    printf("A customer leaves at:%d\n",en.OccurTime);//输出顾客离开时间
    TotalTime+=en.OccurTime-customer.ArriveTime;
    if(!EmptyQueue(&q[i])){
        GetHead(&q[i],&customer);
        OrderInsert(&ev,NewEvent(en.OccurTime+customer.Duration,i));
    }
}

void Bank_Simulation(){
    //银行排队模拟
    OpenForDay();
    srand((unsigned)time(NULL));
    while(!ListEmpty(&ev)){
        DelFirst(&ev,&en);
        printf("--------action--------------------------\n");
        if(en.NType==0)
            CustomerArrived();
        else
            CustomerDepature();
        PrintQueue();
        PrintEventList();
    }
    printf("\nTotal time is: %d min,average time is: %g min.\n",TotalTime,(float)TotalTime/CustomerNum);
}

void PrintQueue(){
    //打印当前队列
    int i;
    for(i=1;i<=4;i++){
        printf("Queue %d have %d customer(s):",i,QueueLength(q[i]));
        QueueTraverse(&q[i]);
    }
    printf("\n");
}

void PrintEventList(){
    //输出事件队列
    printf("Current Eventlist is:\n");
    ListTraverse(&ev);
}
int Min(int a[],int n){
    //返回长度为n的数组a第一个最小值的下标,从0开始
    int i,tmp,ind=0;
    tmp=a[0];
    for(i=1;i<n;i++){
        if(a[i]<tmp){
            tmp=a[i];
            ind=i;
        }
    }
    return ind;
}

int ShortestQueue(){
    //获取最短队列的编号
    int i,a[4];
    for(i=1;i<=4;i++){
        a[i-1]=QueueLength(q[i]);
        //printf("队%d的长度为%d\n",i,QueueLength(q[i]));
    }
    return Min(a,4)+1;//队列从1开始编号
}

//-----------队和链表操作--------------//
Event NewEvent(int occurT,int nType){
    //根据OccurTime和NType值,创建新事件
    Event e;
    e.OccurTime=occurT;
    e.NType=nType;
    return e;
}

Status InitList(LinkList *L){
    //初始化事件链表
    L->head=L->tail=(ElemType *)malloc(sizeof(ElemType));
    if(!L->head){
        printf("Apply for memory error.LinkList initialize failed.\n");
        exit(0);
    }
    L->head->next=NULL;
    return OK;
}

Status OrderInsert(LinkList *L,Event e){
    //将事件e按发生时间顺序插入有序链表L中
    ElemType *p,*q,*newptr;
    newptr=(ElemType *)malloc(sizeof(ElemType));
    if(!newptr){
        printf("Apply for memory error,new node can't insert intot the Eventlist.\n");
        exit(0);
    }
    *newptr=e;
    if(TRUE==ListEmpty(L)){//链表为空
       L->head->next=newptr;
       L->tail=newptr;
       L->tail->next=NULL;
       return OK;
    }
    q=L->head;
    p=L->head->next;
    while(p){//遍历整个链表
        if(p->OccurTime>=newptr->OccurTime)
            break;
        q=p;
        p=p->next;
    }
    q->next=newptr;
    newptr->next=p;
    if(!p)//插入位置为链表尾部
        L->tail=newptr;
    return OK;
}

Status ListEmpty(LinkList *L){
    //判断链表L是否为空,为空返回TRUE,否则返回FALSE
    if((L->head==L->tail)&&(L->head!=NULL))
        return TRUE;
    else
        return FALSE;
}

Status DelFirst(LinkList *L,ElemType *e){
    //链表L不为空,删除其首结点,用e返回,并返回OK;否则返回ERROR
    ElemType *p=L->head->next;
    if(!p)
        return ERROR;
    L->head->next=p->next;
    *e=*p;
    free(p);
    if(L->head->next==NULL)
        L->tail=L->head;
    return OK;
}

Status ListTraverse(LinkList *L){
    //遍历链表
    Event *p=L->head->next;
    if(!p){
        printf("List is empty.\n");
        return ERROR;
    }
    while(p!=NULL){
        printf("OccurTime:%d,Event Type:%d\n",p->OccurTime,p->NType);
        p=p->next;
    }
    printf("\n");
    return OK;
}

Status InitQueue(LinkQueue *Q){
    //初始化队列Q
    Q->head=Q->tail=(QElemType *)malloc(sizeof(QElemType));
    if(!Q->head){
        printf("Apply for memory error.LinkQueue initialize failed.\n");
        exit(0);
    }
    Q->head->next=NULL;
    return OK;
}

Status EmptyQueue(LinkQueue *Q){
    //若队列Q为空,返回TRUE,否则返回FALSE
    if(Q->head==Q->tail&&Q->head!=NULL)
        return TRUE;
    else
        return FALSE;
}

Status DelQueue(LinkQueue *Q,QElemType *e){
    //若队列Q不为空,首结点出队,用e返回,并返回OK;否则返回ERROR
    QElemType *p=Q->head->next;
    if(!p)
        return ERROR;
    *e=*p;
    Q->head->next=p->next;//修正队首指针
    free(p);
    if(!Q->head->next)//队空
        Q->tail=Q->head;
    return OK;
}

Status EnQueue(LinkQueue *Q,QElemType e){
    //结点e入队Q
    QElemType *p=(QElemType *)malloc(sizeof(QElemType));
    if(!p){
        printf("Apply for memory error,new element can't enqueue.\n");
        exit(0);
    }
    *p=e;
    p->next=NULL;
    Q->tail->next=p;//插入队尾
    Q->tail=p;//修改队尾指针
    return OK;
}

int QueueLength(LinkQueue Q){
    //返回队列Q的长度,即元素个数
    int count=0;
    QElemType *p=Q.head->next;
    while(p){
        p=p->next;
        count++;
    }
    return count;
}

Status GetHead(LinkQueue *Q,QElemType *e){
    //若队列Q不为空,用e返回其首结点,并返回OK,否则返回ERROR
    if(EmptyQueue(Q))
        return ERROR;
    *e=*(Q->head->next);
        return OK;
}

Status QueueTraverse(LinkQueue *Q){
    //遍历队列Q
    QElemType *p=Q->head->next;
    if(!p){
        printf("--Is empty.\n");
        return ERROR;
    }
    while(p){
        printf("(%d,%d) ",p->ArriveTime,p->Duration);
        p=p->next;
    }
    printf("\n");
    return OK;
}

3.C++面向对象程序设计结合事件驱动编程。

先暂时贴个代码,以后填坑吧。。。

#include<iostream>
#include <cstdlib>
#include <cmath>
#include<deque>
#include<ctime>
#define RANDOM_PARAMETER 100//生成随机数的区间0-99
using namespace std;
 
//大大的疑问????把函数放在类里???
class Random {//随机数生成类
public:
	// [0, 1) 之间的服从均匀分布的随机值???
	static double uniform(double max = 1) {
		return ((double)std::rand() / (RAND_MAX))*max;
	}
};
 
 
typedef struct costomer{
	//顾客的数据结构
	//顾客是队列的数据存储基础,所以操作上没要求,用结构体就ok
	int arrive_time;//顾客的随机到达时间
	int duration;//顾客业务的随机耗费时间
	costomer * next;
	 // 结构体的默认构造函数???
	costomer(int arrive_time = 0,int duration = Random::uniform(RANDOM_PARAMETER)) :arrive_time(arrive_time),
		duration(duration) ,next(nullptr){}
	//在结构体的构造函数中,实现对duration的随机数的生成
} Costomer;
 
 
 
//窗口状态的枚举
enum Win_Status {
	SERVICE,//服务中0
	IDLE//空闲1
};
 
 
//工作窗口类定义
class ServiceWindows {
private:
	Costomer costomer;//存储处理客户的信息
	Win_Status status;//表示窗口状态
public:
	ServiceWindows()//构造函数
	{
		status = IDLE;//初始的时候空闲
	}
	void setBusy()//窗口设置为繁忙
	{
		status = SERVICE;
	}
	void setIdle()//窗口设置为空闲
	{
		status = IDLE;
	}
 
	inline void serveCustomer(Costomer &customer) {//读取新客户业务
		costomer = customer;
	}
 
	bool IsIdle()
	{
		if (status == IDLE)
			return true;
		else
			return false;
	}
	int getArriveTime()
	{
		return costomer.arrive_time;
	}
	int getDurationTime()
	{
		return costomer.duration;
	}
	
};
 
 
 
//设计事件表,即,事件的数据结构
struct Event {
	int occur_time;//事件发生的时间,用于之后的事件的排序
 
	//描述时间的类型,-1表示到达,》=0表示离开,并且表示相应的窗口编号
	int EventType;
	Event * next;
 
	//所以,又是结构体的构造函数?
	Event(int time = Random::uniform(RANDOM_PARAMETER) ,int type = -1):occur_time(time),EventType(type)
		,next(nullptr) {}
};
 
 
//可插入队列的的实现
template<class T>
class Queue {
private:
	T * front;
	T * rear;//头指针and尾指针
public:
	Queue();//构造函数,带有头节点的
	~Queue();//析构函数
	void clearQueue();//清空队列
	T* enqueue(T & join);//入队
	T * dequeue();//出队
	T * orderEnqueue(Event& event);//只适用于事件入队
	int length();//获得队列长度
};
 
 
 
 
//系统队列的设计
class QueueSystem {
private:
	int total_service_time;//总的服务时间
	int total_costomer;//总的服务顾客总数
	int total_stay_time;//总的等待时间
	int windows_number;//窗口数目
	int avg_stay_time;//平均时间
	int avg_costomers;//平均顾客数目
 
	ServiceWindows*  windows;//创建服务窗口数组的指针
	Queue<Costomer> customer_list;//客户排队等待的队列
	Queue<Event>       event_list;//时间队列????
	Event*          current_event;//事件指针
 
	double run();// 让队列系统运行一次
	
	void init();// 初始化各种参数
	
	void end();// 清空各种参数
	
	int getIdleServiceWindow();// 获得空闲窗口索引
	
	void customerArrived();// 处理顾客到达事件
	
	void customerDeparture();// 处理顾客离开事件
 
public:
	// 初始化队列系统,构造函数
	QueueSystem(int total_service_time, int window_num);
 
	// 销毁,析构函数
	~QueueSystem();
 
	// 启动模拟,
	void simulate(int simulate_num);
 
	inline double getAvgStayTime() {
		return avg_stay_time;
	}
	inline double getAvgCostomers() {
		return avg_costomers;
	}
 
};
 
 
 
 
int main()
{
	
	srand((unsigned)std::time(0)); // 使用当前时间作为随机数种子
 
	int total_service_time = 240;       // 按分钟计算
	int window_num = 4;
	int simulate_num = 100000;    // 模拟次数????这是干嘛用的???
 
	QueueSystem system(total_service_time, window_num);//构建这个系统,初始化
 
	system.simulate(simulate_num);//开启模拟???这又是神马意思
 
	cout << "The average time of customer stay in bank: "
		<< system.getAvgStayTime() << endl;
	cout << "The number of customer arrive bank per minute: "
		<< system.getAvgCostomers() << endl;
	getchar();
	return 0;
}
template<class T>
Queue<T>::Queue()
{
	front = new T;//有一个头节点的链表
	if (!front)
		exit(1);//内存分配失败,终止程序
	rear = front;
	front->next = nullptr;//头节点
}
 
template<class T>
Queue<T>::~Queue()//析构函数,清空链表,释放头节点
{
	clearQueue();
	delete front;//释放头节点内存
}
 
 
template<class T>
void Queue<T>::clearQueue()
{
	T *temp_node;
	//清空链表的时候用头节点往前边推进,知道最后的NULL,这个方法比较巧妙
	while (front->next) {
		temp_node = front->next;
		front->next = temp_node->next;
		delete temp_node;
	}
 
	this->front->next = NULL;
	this->rear = this->front;
}
 
template<class T>
T * Queue<T>::enqueue(T & join)
{//从队尾加入
	T * new_node= new T;
	if (!new_node)
		exit(1);
	*new_node = join;
	new_node->next = nullptr;
 
	rear->next = new_node;
	rear = rear->next;
	return front;//返回头指针,
}
 
template<class T>
T * Queue<T>::dequeue()//注意,这里实现的不是删除节点,而是将节点从链表拆除,拿走使用
{
	if (!front->next)//空,全面的错误检查
		return nullptr;
 
	T * temp = front->next;
	front->next = temp->next;//将首节点拆除,以便于后来带走
	
	if (!front->next)//错误预警,判断是不是拿走的是不是最后一个元素
		rear = front;
 
	return temp;//返回出队的元素指针,在这里不释放。
}
 
 
 
template<class T>
int Queue<T>::length()
{
	T *temp_node;
	temp_node = this->front->next;
	int length = 0;
	while (temp_node) {
		temp_node = temp_node->next;
		++length;
	}
	return length;
}
 
template<class T>
T * Queue<T>::orderEnqueue(Event & event)//对于事件列表,要按照时间的顺序插入
{
	Event* temp = new Event;
	if (!temp) {
		exit(-1);
	}
	*temp = event;//赋值
 
	// 如果这个列表里没有事件, 则把 temp 事件插入
	if (!front->next) {
		enqueue(*temp);
		delete temp;
		return front;
	}
 
	// 按时间顺序插入
	Event *temp_event_list = front;
 
	// 如果有下一个事件,且下一个事件的发生时间小于要插入的时间的时间,则继续将指针后移
	while ( temp_event_list->next  &&  temp_event_list->next->occur_time < event.occur_time) {
		temp_event_list = temp_event_list->next;
	}//最终得到的temp_event_list的下一个是时间大于新输入event的,所以应该插入在temp_event_list之后
 
	// 将事件插入到队列中
	temp->next = temp_event_list->next;
	temp_event_list->next = temp;
 
	// 返回队列头指针
	return front;
}
/*
我们来看入队方法和出队方法中两个很关键的设计:
 
入队时尽管引用了外部的数据,但是并没有直接使用这个数据,反而是在内部新分配了一块内存,再将外部数据复制了一份。
出队时,直接将分配的节点的指针返回了出去,而不是拷贝一份再返回。
在内存管理中,本项目的代码使用这样一个理念:谁申请,谁释放。
 
队列这个对象,应该管理的是自身内部使用的内存,释放在这个队列生命周期结束后,依然没有释放的内存。
*/
 
QueueSystem::QueueSystem(int total_service_time, int window_num):
	total_service_time(total_service_time),
windows_number(window_num),
total_stay_time(0),
total_costomer(0)
{//构造函数
	windows = new ServiceWindows[windows_number];//创建 num 个工作窗口
}
 
QueueSystem::~QueueSystem()
{
	delete [] windows ;//释放窗口内存
}
 
void QueueSystem::simulate(int simulate_num)//这个地方一直没搞懂,模拟?
{
	double sum = 0;//累计模拟次数????
 
	//这个循环可以说是这个系统跑起来运行的发动机吧
	for (int i = 0; i != simulate_num; ++i) {
		// 每一遍运行,我们都要增加在这一次模拟中,顾客逗留了多久
		sum += run();
	}
 
 
	/*模拟结束,进行计算,类似复盘*/
 
	// 计算平均逗留时间
	avg_stay_time = (double)sum / simulate_num;
	// 计算每分钟平均顾客数
	avg_costomers = (double)total_costomer / (total_service_time*simulate_num);
}
 
 
// 系统开启运行前, 初始化事件链表,第一个时间一定是到达事件,所以采用默认构造就ok
void QueueSystem::init() {
 
	Event *event = new Event;//创建一个默认的事件,到达。
	current_event = event;//并且是当前事件
}
 
 
 
// 系统开始运行,不断消耗事件表,当消耗完成时结束运行
double QueueSystem::run() {
 
	init();//在这里初始化????
	
	while (current_event) {
		// 判断当前事件类型
		if (current_event->EventType == -1) {
			customerArrived();//事件类型为-1,处理客户到达事件
		}
		else {
			customerDeparture();//处理客户离开事件
		}
 
		delete current_event;//处理完毕,释放当前的事件
		// 从事件表中读取新的事件
		current_event = event_list.dequeue();//出队列,
	};
	end();//结束
 
	// 返回顾客的平均逗留时间
	return (double)total_stay_time / total_costomer;
}
 
 
 
 
// 系统运行结束,将所有服务窗口置空闲。并清空用户的等待队列和事件列表????
void QueueSystem::end() {
	// 设置所有窗口空闲
	for (int i = 0; i != windows_number; ++i) {
		windows[i].setIdle();
	}
 
	// 顾客队列清空
	customer_list.clearQueue();
 
	// 事件列表清空
	event_list.clearQueue();
}
 
 
// 处理用户到达事件
void QueueSystem::customerArrived() {
 
	total_costomer++;//用户数目++
 
	// 生成下一个顾客的到达事件
 
	int intertime = Random::uniform(100);  // 下一个顾客到达的时间间隔,我们假设100分钟内一定会出现一个顾客
										   // 下一个顾客的到达时间 = 当前时间的发生时间 + 下一个顾客到达的时间间隔
	int time = current_event->occur_time + intertime;
	Event temp_event(time);//结构体构造函数,参数为到达时间,然后业务时间在构造函数中生成
	// 如果下一个顾客的到达时间小于服务的总时间,就把这个事件插入到事件列表中
	
	if (time < total_service_time) {
		event_list.orderEnqueue(temp_event);
	} // 否则不列入事件表,且不加入 cusomer_list
		// 同时将这个顾客加入到 customer_list 进行排队
	  // 处理当前事件中到达的顾客
	Costomer *customer = new Costomer(current_event->occur_time);
	if (!customer) {
		exit(-1);
	}
	customer_list.enqueue(*customer);//将的用户加入列表
 
	// 如果当前窗口有空闲窗口,那么直接将队首顾客送入服务窗口
	int idleIndex = getIdleServiceWindow();
	if (idleIndex >= 0) {
		customer = customer_list.dequeue();//客户指针
		windows[idleIndex].serveCustomer(*customer);//将客户信息传递给空闲的窗口处理
		windows[idleIndex].setBusy();//窗口设置为忙碌
 
		// 顾客到窗口开始服务时,就需要插入这个顾客的一个离开事件到 event_list 中
		// 离开事件的发生时间 = 当前时间事件的发生时间 + 服务时间
		Event temp_event(current_event->occur_time + customer->duration, idleIndex);
		event_list.orderEnqueue(temp_event);//将离开的事件按照时间的先后插入事件链表
	}
	delete customer;//释放已经传递到窗口的客户信息
}
 
 
//获取空闲窗口的序号
int QueueSystem::getIdleServiceWindow() {
	for (int i = 0; i != windows_number; ++i) {//遍历查找
		if (windows[i].IsIdle()) {
			return i;
		}
	}
	return -1;
}
 
 
// 处理用户离开事件
void QueueSystem::customerDeparture() {
	// 如果离开事件的发生时间比总服务时间大,我们就不需要做任何处理
	if (current_event->occur_time < total_service_time) {
		// 顾客总的逗留时间 = 当前顾客离开时间 - 顾客的到达时间
		total_stay_time += current_event->occur_time - windows[current_event->EventType].getArriveTime();
 
		// 如果队列中有人等待,则立即服务等待的顾客
		//把窗口交给排队中的新的客户
		if (customer_list.length()) {
			Costomer *customer;
			customer = customer_list.dequeue();
			windows[current_event->EventType].serveCustomer(*customer);
 
			// 因为有新的客户进入柜台,所以要为这个新的客户编写离开事件事件,并送到事件列表中
			Event temp_event(
				current_event->occur_time + customer->duration,
				current_event->EventType
			);
			event_list.orderEnqueue(temp_event);
 
			delete customer;
		}
		else {
			// 如果队列没有人,且当前窗口的顾客离开了,则这个窗口是空闲的
			windows[current_event->EventType].setIdle();
		}
 
	}
}

https://blog.csdn.net/weixin_35929051/article/details/52494728
https://blog.csdn.net/taikeqi/article/details/76652229
https://blog.csdn.net/weixin_41879093/article/details/82843772
https://www.shiyanlou.com/courses/557

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值