C++事宜驅動型銀行列隊模仿。本站提示廣大學習愛好者:(C++事宜驅動型銀行列隊模仿)文章只能為提供參考,不一定能成為您想要的結果。以下是C++事宜驅動型銀行列隊模仿正文
比來重拾之前前功盡棄的C++,正好看到了《C++ 完成銀行列隊辦事模仿》,然則沒有試驗樓的會員,看不到詳細的完成,正好用來作為演習。
模仿的是銀行的列隊叫號體系,一切顧客以先來後到的次序在統一個隊列中期待,當有辦事窗口余暇時,則隊首的顧客接收辦事,完成後則下一名顧客開端接收辦事。
本完成是事宜驅動型的,處置對象是事宜而不是顧客:
有2種事宜:顧客到事宜和顧客分開事宜。
有2個隊列:顧主隊列和事宜隊列。
法式的邏輯以下:
1.初始化事宜隊列,填充顧客達到事宜;
2.處置事宜隊列的頭部(老是為最早產生的事宜),若為“顧客達到事宜”,轉向處置“顧客達到事宜”,若為“顧客分開事宜”,轉向處置“顧客分開事宜”;
3.輪回停止2,直到事宜隊列為空或超越營業時光;
4.停止清算任務。
事宜處置
主流程
永久處置事宜隊列的頭部——即最早產生的事宜。
void Manager::run() { while (_event_queue.size() != 0) { _current_event = _event_queue.front(); if (_current_event->occur_time >= _total_serve_time)//超越營業時光 break; if(_customer_queue.size() == 0 && _event_queue.size() <= _service_num)//生成新的顧客達到事宜 { generate_arrived_event(_generate_arrived_time); _current_event = _event_queue.front();//update current event, deal it with order } 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是同享的 Event* event; int customer_per_minute = Random::uniform(RANDOM_PER_MINUTE);//單元時光內隨機達到的顧客數目,至多為1例 while (customer_per_minute > 0) { event = new Event(current_time); _event_queue.enqueue(event); --customer_per_minute; } ++current_time; } _generate_arrived_time = 0; while(_generate_arrived_time < _total_serve_time) { //_total_serve_time是銀行的總營業時光 generate_arrived_event(_generate_arrived_time); }
(2)分批生成顧客達到事宜
僅在須要時生成顧客達到事宜,由於顧客接收辦事須要必定的時光,平日來講第1種辦法生成的達到事宜在到達銀行營業時光後不克不及全體處置完成,這就形成了時光和空間上的糟蹋。分批生成達到事宜的完成是基於如許的現實:假如顧主隊列為空且事宜隊列的長度小於等於辦事窗口的數目時,解釋余下的事宜將不克不及讓辦事窗口滿載並發生期待辦事的顧客,此時就須要生成新的顧客達到事宜。
初始化事宜隊列:
_generate_arrived_time = 0;//照舊是以時光次序生成顧客達到事宜 while(_generate_arrived_time < INIT_ARRIVIED_EVENT_NUM) {//選擇適合的值,使最後生成的顧客達到事宜略多於辦事窗口數目,包管辦事窗口滿載且有期待顧客便可 generate_arrived_event(_generate_arrived_time); }
斷定前提並生成達到事宜:
if(_customer_queue.empty() && _event_queue.size() <= _service_num) { generate_arrived_event(_generate_arrived_time); _current_event = &_event_queue.top();//update current event, deal it with order }
因為顧客達到事宜還是以時光次序從0到營業時光停止來生成的,之所以可以或許包管不會在處置了98分鐘時的顧客分開事宜(能夠是5分鐘時達到的顧客發生的)後再行止理新拔出的10分鐘時的顧客達到事宜,就是初始化事宜隊列時選擇適合的INIT_ARRIVIED_EVENT_NUM的意義地點:時辰包管事宜隊列的長度年夜於辦事窗口的數目,重生成的顧客達到事宜的時光若早於顧客分開事宜的時光(以時光為次序生成顧客達到事宜就包管了重生成的顧客達到事宜的時光不小於事宜隊列中已有的顧客達到事宜,而顧客分開事宜的時光是隨機的,若提早於重生成的顧客到事宜處置,能夠會掉序)也能被准確處置。
生成顧客分開事宜
顧主隊列頭部顧客接收辦事並出隊,生成顧客分開事宜並入隊事宜隊列。顧客分開事宜的產生時光 = 以後時光 + 顧客接收辦事的時長。
void Manager::generate_departure_event(int service_index, int current_time) { _services[service_index].serve_customer(*_customer_queue.front()); _services[service_index].set_busy();//辦事窗口置為“忙” _services[service_index].set_service_start_time(current_time);//辦事開端時光 _customer_queue.dequeue(); int duration = _services[service_index].get_customer_duration(); Event* event = new Event(current_time + duration, EventType::DEPARTURE, service_index);//生成顧客分開事宜 _event_queue.enqueue(event); }
處置顧客達到事宜
處置“顧客達到事宜”的邏輯:
1.生成1個顧客,入隊顧主隊列;
2.出隊事宜隊列;
3.如有余暇的辦事窗口,則生成“顧客分開事宜”。
上面是代碼:
void Manager::customer_arrived() { int idle_service_num = get_idle_service_index();//獲得余暇的辦事窗口,前往-1解釋未找到 int current_time = _current_event->occur_time; Customer* customer = new Customer(current_time);//顧客達到事宜產生時光即為顧客達到時光, 顧客接收辦事的時長隨機 _customer_queue.enqueue(customer); _event_queue.dequeue(); if (idle_service_num != -1) generate_departure_event(idle_service_num, current_time); }
處置顧客分開事宜
處置“顧客分開事宜”的邏輯:
1.顧客地點辦事窗口置為余暇,統計顧客信息;
2.出隊事宜隊列;
3.若顧主隊列不為空且有余暇的辦事窗口,生成“顧客分開事宜”。
上面是代碼:
void Manager::customer_departure() { int current_time = _current_event->occur_time; int service_index = _current_event->service_index;//顧客分開的辦事窗口 _customer_stay_time += current_time - _services[service_index].get_customer_arrive_time();//統計顧客在銀行的滯留時光 ++_total_served_customer_num;//接收辦事的顧客數量加1 _services[service_index].set_idle(); _event_queue.dequeue(); if(_customer_queue.size() > 0) { service_index = get_idle_service_index();//有顧客分開,必定可以取得1個余暇辦事窗口,這裡獲得最弁言號的辦事窗口 generate_departure_event(service_index, current_time); } }
清算任務:
1.尋覓仍在接收辦事的顧客並統計他們的信息;
2.釋放靜態請求的內存。
上面是代碼:
void Manager::end() { for (int i = 0; i < _service_num; i++) { if (!_services[i].is_idle()) {//統計正在接收辦事的顧客的信息 int service_start_time = _services[i].get_service_start_time(); int arrive_time = _services[i].get_customer_arrive_time(); int duration = _services[i].get_customer_duration(); _customer_stay_time += service_start_time + duration - arrive_time; ++_total_served_customer_num; } } //釋放靜態請求的內存 _customer_queue.clear(); _event_queue.clear(); delete[] _services; }
關於隊列的解釋
法式中應用的是自界說的隊列,依據需求,可使用STL中的優先隊列和隊列,前者用於事宜隊列,後者用於顧主隊列。
優先隊列的頭部老是優先級最高的節點,關於事宜來講,就是產生的時光越早,事宜優先級越高,所以這是一個最小堆——時光產生的時光最小(最早)的位於堆頂。這觸及到對Event類型的比擬,應用STL的greater<Event>(須要重載operator>)或是自界說的函數對象來比擬2個Event對象:
(1)重載operator>運算符
//聲明應用greater<Event>作為比擬函數的優先隊列 std::priority_queue<Event, std::vector<Event>, std::greater<Event>> _event_queue; //event_queue.h #ifndef BANKQUEUE_EVENT_QUEUE_H #define BANKQUEUE_EVENT_QUEUE_H #include "random.h" enum class EventType : int { ARRIVIED, DEPARTURE }; class Event { public: Event():occur_time(Random::uniform(RANDOM_PARAMETER)), event_type(EventType::ARRIVIED), service_index(-1), next(nullptr){} 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的完成,都是經由過程'<'來完成余下的比擬操作符 friend bool operator> (const Event& event1, const Event& event2);//供`greater<Event>`應用 public: int occur_time; int service_index; EventType event_type; Event *next; }; inline bool operator< (const Event& event1, const Event& event2) { return event1.occur_time < event2.occur_time; } inline bool operator> (const Event& event1, const Event& event2) { return event2 < event1;//經由過程'<'完成'>'的功效 } #endif //BANKQUEUE_EVENT_QUEUE_H
(2)比擬直不雅且簡略的做法是自界說用於比擬的函數對象:
//聲明應用EventComp作為比擬函數的優先隊列 std::priority_queue<Event, std::vector<Event>, EventComp> _event_queue; struct EventComp { bool operator()(const Event& lhs, const Event& rhs) const { return lhs.occur_time > rhs.occur_time;//occur_time是私有的,若是公有的,則須要供給接口 } };
可以在test.h中經由過程USE_SELF_DEFINE_QUEUE宏來切換2種隊列的應用。
事宜隊列的次序
事宜隊列請求隊首老是產生時光最早的事宜,最小堆長短常好的選擇,經由過程後面引見的STL優先隊列可以輕松完成。自界說的隊列則應用的是蠻力法,在事宜入隊時就停止排序,包管隊列是以產生時光升序的,在隊列中元素較多時(如幾百個),效力是低於應用STL優先隊列的辦法的,這也是為什麼要分批生成顧客達到事宜的緣由之一:避免事宜隊列的元素過量。
顧主隊列是不須要排序的,所以以模板特例化的方法完成了事宜隊列的入隊辦法:
template<>
void Queue<Event>::enqueue(Event* event) { Event *cur = _front, *prev = nullptr; while (cur != nullptr) { if (cur->occur_time < event->occur_time) { prev = cur; cur = cur->next; } else break; } if (prev == nullptr) { event->next = _front; _front = event; if (_rear == nullptr) _rear = event;//_rear is useless to Event queue } else { event->next = prev->next; prev->next = event; if (prev == _rear) _rear = event; } ++length; }
完全代碼在這裡。
以上就是本文的全體內容,願望對年夜家的進修有所贊助,也願望年夜家多多支撐。