zoukankan      html  css  js  c++  java
  • 银行排队模拟(队列,模拟,数据结构)

    问题描述:
    已知:
    (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]<<"
    "<<"NORMAL客户的平均办理时间:"<<sum[2]/num[2]<<endl;
    }
    
    

    问题:上述算法仅仅求出了每种客户服务时间的平均值,但并没有考虑排队问题,也就是排队时间 + 服务时间 = 逗留事件。既然要模拟排队过程,那就必须确定某一时间某个人选择了哪个窗口,实现这个过程要么有固定的模式,要么就用随机数模拟,下面的一个代码就通过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|
    ",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
    ",en.OccurTime,intertime);
        }
    }
    
    void CustomerDepature(){
        //顾客离开事件
        int i=en.NType;
        DelQueue(&q[i],&customer);
        printf("A customer leaves at:%d
    ",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--------------------------
    ");
            if(en.NType==0)
                CustomerArrived();
            else
                CustomerDepature();
            PrintQueue();
            PrintEventList();
        }
        printf("
    Total time is: %d min,average time is: %g min.
    ",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("
    ");
    }
    
    void PrintEventList(){
        //输出事件队列
        printf("Current Eventlist is:
    ");
        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
    ",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.
    ");
            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.
    ");
            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.
    ");
            return ERROR;
        }
        while(p!=NULL){
            printf("OccurTime:%d,Event Type:%d
    ",p->OccurTime,p->NType);
            p=p->next;
        }
        printf("
    ");
        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.
    ");
            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.
    ");
            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.
    ");
            return ERROR;
        }
        while(p){
            printf("(%d,%d) ",p->ArriveTime,p->Duration);
            p=p->next;
        }
        printf("
    ");
        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

  • 相关阅读:
    【c/c++】#pragma的用法
    【c/c++】#pragma的用法
    【matlab】matlab中 mcc、mbuild和mex命令详解
    【codecs】数据流ES、PES、TS、PS简介
    【codecs】数据流ES、PES、TS、PS简介
    【SVAC】SVAC标准简介
    【python】Python实现SMTP发送邮件
    【c】#pragma once 与#ifndef的区别与作用
    【c】#pragma once 与#ifndef的区别与作用
    【Tools/PC】如何在一台电脑上实现双屏幕显示?
  • 原文地址:https://www.cnblogs.com/tomjobs/p/10617593.html
Copyright © 2011-2022 走看看