最近重拾之前半途而廢的c++,恰好看到了《c++ 實現銀行排隊服務模擬》,但是沒有實驗樓的會員,看不到具體的實現,正好用來作為練習。
模擬的是銀行的排隊叫號系統,所有顧客以先來後到的順序在同乙個佇列中等待當有服務視窗空閒時,則隊首的顧客接受服務,完成後則下一位顧客開始接受服務。
本實現是事件驅動型的,處理物件是事件而不是顧客:
有2種事件:顧客到事件和顧客離開事件。
有2個佇列:顧客佇列和事件佇列。
程式的邏輯如下:
1.初始化事件佇列,填充顧客到達事件;
2.處理事件佇列的頭部(總是為最早發生的事件),若為「顧客到達事件」,轉向處理「顧客到達事件」,若為「顧客離開事件」,轉向處理「顧客離開事件」;
3.迴圈進行2,直到事件佇列為空或超出營業時間;
4.進行清理工作。
事件處理
主流程
永遠處理事件佇列的頭部——即最早發生的事件。
void manager::run()
if (_current_event->event_type == eventtype::arrivied)//處理顧客到達事件
customer_arrived();
else if (_current_event->event_type == eventtype::departure)//處理顧客離開事件
customer_departure();
}}生成顧客到達事件
有2種方法:
(1)一次性生成全部顧客到達事件
設定單位時間內到達的顧客數量,生成銀行營業時間內所有的到達事件,處理這些事件,直到事件佇列為空或是超過了銀行的營業時間。
void manager::generate_arrived_event(int& current_time)
++current_time; }
_generate_arrived_time = 0;
while(_generate_arrived_time < _total_serve_time)
(2)分批生成顧客到達事件
僅在需要時生成顧客到達事件,因為顧客接受服務需要一定的時間,通常來說第1種方法生成的到達事件在達到銀行營業時間後不能全部處理完成,這就造成了時間和空間上的浪費。分批生成到達事件的實現是基於這樣的事實:如果顧客隊列為空且事件佇列的長度小於等於服務視窗的數量時,說明餘下的事件將不能讓服務視窗滿載並產生等待服務的顧客,此時就需要生成新的顧客到達事件。
初始化事件佇列:
_www.cppcns.comgenerate_arrived_time = 0;//依舊是以時間順序生成顧客到達事件
while(_generate_arrived_time < init_arrivied_event_num)
判斷條件並生成到達事件:
if(_customer_queue.empty() && _event_queue.size() <= _service_num)
由於顧客到達事件仍是以時間順序從0到營業時間結束來生成的,之所以能夠保證不會在處理了98分鐘時的顧客離開事件(可能是5分鐘時到達的顧客產生的)後再去處理新插入的10分鐘時的顧客到達事件,就是初始化事件佇列時選擇合適的init_arrivied_event_num的意義所在:時刻保證事件佇列的長度大於服務視窗的數量,新生成的顧客到達事件的時間若早於顧客離開事件的時間(以時間為順序生成顧客到達事件就保證了新生成的顧客到達事件的時間不小於事件佇列中已有的顧客到達事件,而顧客離開事件的時間是隨機的,若提前於新生成的顧客到事件處理,可能會失序)也能被正確處理。
生成顧客離開事件
顧客佇列頭部顧客接受服務並出隊,生成顧客離開事件併入隊事件佇列。顧客離開事件的發生時間 = 當前時間 + 顧客接受服務的時長。
void manager::generate_departure_event(int service_index, int current_time)
處理顧客到達事件
處理「顧客到達事件」的邏輯:
1.生成1個顧客,入隊顧客佇列;
2.出隊事件佇列;
3.若有空閒的服務視窗,則生成「顧客離開事件」。
下面是**:
void manager::customer_arrived()
處理顧客離開事件
處理「顧客離開事件」的邏輯:
1.顧客所在服務視窗置為空閒,統計顧客資訊;
2.出隊事件佇列;
3.若顧客佇列不為空且有空閒的服務視窗,生成「顧客離開事件」。
下面是**:
void manager::customer_departure()
}清理工作:
1.尋找仍在接受服務的顧客並統計他們的資訊;
2.釋放動態申請的記憶體。
下面是**:
void manager::end()
} //釋放動態申請的記憶體
_customer_queue.clear();
_event_queue.clear();
delete _services;
}關於佇列的說明
程式中使用的是自定義的佇列,根據需求,可以使用stl中的優先佇列和佇列,前者用於事件佇列,後者用於顧客佇列。
優先佇列的頭部總是優先順序最高的節點,對於事件來說,就是發生的時間越早,事件優先順序越高,所以這是乙個最小堆——時間發生的時間最小(最早)的位於堆頂。這涉及到對event型別的比較,使用stl的greater(需要過載operator>)或是自定義的函式物件來比較2個event物件:
(1)過載operator>運算子
//宣告使用greater作為比較函式的優先佇列
std::priority_queue, std::greater> _event_queue; //event_queue.h
#ifndef bankqueue_event_queue_h
#define bankqueue_event_queue_h
#include "random.h"
enum class eventtype : int ;
class event
event(int occur_time):occur_time(occur_time),
event_type(eventtype::arrivied),
service_index(-1),
next(nullptr){}
event(int occur_time, eventtype event_type, int service_index):
occur_time(occur_time),
event_type(event_type),
service_index(service_index),
next(nullptr) {}
friend bool operator< (const event& event1, const event& event2);//模仿stl的實現,都是通過' (const event& event1, const event& event2);//供`greater`使用
public:
int occur_time;
int service_index;
eventtype event_type;
event程式設計客棧 *next;
};inline bool operator< (const event& event1, const event& event2)
inline bool operator> (const event& event1, const event& event2)
#endif //bankqueue_event_queue_h
(2)比較直觀且簡單的做法是自定義用於比較的函式物件:
//宣告使用eventcomp作為比較函式的優先佇列
std::priority_queue, eventcomp> _event_queue; struct eventcomp
}; 可以在test.h中通過use_self_define_queue巨集來切換2種佇列的使用。
事件佇列的順序
事件佇列要求隊首總是發生時間最早的事件,最小堆是非常好的選擇,通過前面介紹的stl優先佇列可以輕鬆實現。自定義的佇列則使用的是蠻力法,在事件入隊時就進行排序,保證佇列是以發生時間公升序的,在佇列中元素較多時(如幾百個),效率是低於使用stl優先佇列的方法的,這也是為何要分批生成顧客到達事件的原因之一:防止事件佇列的元素過多。
顧客佇列是不需要排序的,所以以模板特例化的方式實現了事件佇列的入隊方法:
template<>
void queue::enqueue(event* event)
else
break;
} if (prev == nullptr)
else
++length;
} 完整**在這裡。
本文標題: c++事件驅動型銀行排隊模擬
本文位址: /ruanjian/c/164441.html
離散事件模擬 銀行排隊
某銀行有四個視窗接待客戶,每個視窗在某個時刻只能接待乙個客戶,人數多時則需要進行排隊,剛進來的客戶,視窗無人則進行業務辦理,有人則排隊,排在人數最少的隊伍後面。問題 計算在銀行關閉之前,計算客戶在銀行逗留的平均時間 用c寫的,給書上 的實現了下,如下 include include include ...
離散事件模擬 銀行排隊
假設不存在插隊的情況,並且人人都會選擇排到人數最少的那乙個視窗排隊。假設人們陸續到達銀行,不會同時到達銀行。如下 include include include define max size 10000 佇列的初始化長度 define end time 10000 銀行關門時間 using nam...
離散事件模擬 銀行排隊時間模擬
在資料結構中有個講述如何模擬銀行排隊,最終算出每個人平均的逗留時間。這是需要資料結構的知識。將銀行的每個視窗看成是乙個佇列,那麼對於每次來乙個人,都需要從最短的佇列進行排隊。其實更優秀的做法是從最短的等待時間佇列來排隊 這裡的做法是這樣的,首選在乙個佇列中插入乙個人,整個事件是事件驅動的,每次去檢查...