1.總客戶數
2.客戶總逗留時間
3.客戶平均逗留時間
問題分析
銀行類:
銀行類的方法:
隊列:
有序鏈表:
void Bank::Simulation()算法:
1.開門營業,OpenForDay()
2.如果有事件發生,那么:
(1) 對於到達事件,處理到達事件,CustomerArrived(Event *event)
(2) 對於離開事件,處理離開事件,CustomerDeparture(Event *event)
3.重復第2步
4.輸出統計結果
void Bank::OpenForDay()算法:
1.初始化_queue_number為某個正整數
2.初始化_close_time為以秒為單位的時間,比如8*3600,表示8個小時
3.初始化_total_time為0
4.初始化_customer_number為0
5.設定第一個客戶到達事件,客戶到達時刻為0
6.隊列和有序鏈表的初始化(這是由C++STL類自己完成的)
void CustomerArrived(Event *event)算法:
1.產生隨機數:客戶辦理業務需要的時間,假設一個客戶最多需要30分鍾
2.產生隨機數:下一個客戶到達的時間間隔,假設最多10分鍾來一個客戶
3.下一個客戶到達時刻是當前事件發生時刻和時間間隔的和
4.如果到達時刻銀行沒有下班,產生一個新的客戶到達事件插入事件有序鏈表
5.給鏈表按事件的發生時刻排序(因為STL中沒有有序鏈表)
6.找一個最短的隊列
7.在最短的隊列中插入新到的客戶
8.如果隊列中有且只有一個客戶,生成該客戶的一個離開事件插入到事件表
這種情況下,離開事件發生時刻=到達時刻+辦理業務需要的時間
9.統計客戶數量
void CustomerDeparture(Event *event)算法:
1.計算該客戶在銀行中的逗留時間,並且累加總逗留時間
客戶在銀行中的逗留時間=客戶離開事件發生時刻-客戶到達時刻
2.從隊列中刪除該客戶
3.如果隊列不空則設定一個新的隊頭客戶離開事件
隊頭離開事件發生時刻=上個離開事件發生時刻(隊頭開始辦業務的時刻)+隊頭辦業務需要時間
[capture list](parameter list)->return type {function body}
[capture list](parameter list)->return type {function body}
舉例:
_event_list.sort(
[](const Event &e1, const Event &e2) -> bool
{return e1._occur_time < e2._occur_time;});
定義頭文件
#ifndef __BANK_H__
#define __BANK_H__
// #include<其它頭文件>
// const、constexpr and class 定義
// extern 多文件共享變量聲明
#endif
<------以下為實現代碼------>
#include "stdafx.h"
#include "bank.h"
#include <iostream>
#include <clocale>
#include <chrono>
#include <cstdlib>
/*
#include <algorithm>
std::sort(_work_queue, _work_queue + _queue_number,
[](const std::queue<QueueNode> &q1,
const std::queue<QueueNode> &q2) -> bool
{return q1.size() < q2.size();});
*/
Bank::Bank(int window, int close_time)
:_queue_number(window), _close_time(close_time),
_total_time(0), _customer_number(0)
{
_work_queue = new std::queue<QueueNode>[window];
srand(std::chrono::system_clock
::to_time_t(std::chrono::system_clock::now()));
}
Bank::~Bank()
{
delete[] _work_queue;
}
void Bank::OpenForDay()
{
// 第一個客戶到達
_event_list.push_back({0, 0});
}
// 客戶到達事件
// 客戶到達時,有三件事需要做:
// 1:為此時刻到達的客戶隨機產生一個辦理事務需要時間
// 2:隨機產生下一客戶到達的時間間隔
// 3:把到達的客戶放入一個最短的工作隊列
void Bank::CustomerArrived(Event *event)
{
++_customer_number;
int duration_time, inter_time;
// 此時刻到達客戶辦理事務需要時間
duration_time = rand() % 1800 + 1; //一個客戶最多要30分鍾
// 下一個客戶在從event->_occur_time+inter_time時刻到來
inter_time = rand() % 600 + 1; // 最多10分鍾來一個客戶
// 下一個客戶到達時間
int t = event->_occur_time + inter_time;
// 銀行還沒有關門
if(t < _close_time) {
_event_list.push_back({t, 0});
// 按到達時間排序事件表,早前晚后
SortEventList();
}
// 選一個最短隊列排隊
int i;
i = FindShortestQueue();
_work_queue[i].push({event->_occur_time, duration_time});
if(_work_queue[i].size() == 1) {
// 這個i隊列第一個客戶,生成他的離開事件
_event_list.push_back(
{event->_occur_time + duration_time, i + 1});
SortEventList();
}
}
void Bank::CustomerDeparture(Event *event)
{
int i = event->_type - 1;
QueueNode customer;
// 客戶事務處理完畢,離開
customer = _work_queue[i].front();
_work_queue[i].pop();
_total_time
+= event->_occur_time - customer._arrival_time;
// 第i個隊列的一個離開事件
if(!_work_queue[i].empty()) {
customer = _work_queue[i].front();
_event_list.push_back(
{customer._duration_time + event->_occur_time, i + 1});
SortEventList();
}
}
int Bank::FindShortestQueue()
{
int result = 0;
for(int i = 0; i < _queue_number; ++i) {
if(_work_queue[result].size() > _work_queue[i].size())
result = i;
}
return result;
}
void Bank::SortEventList()
{
// 方法一,Lambda表達式:
_event_list.sort(
[](const Event &e1, const Event &e2) -> bool
{return e1._occur_time < e2._occur_time;});
// 方法二:
// 你知道怎么寫一個函數來比較兩個event嗎?
// 其實就是把Lambda表達式寫成一個函數,把
// 這個函數作為sort的參數就可以了。
// 方法三,使用 struct Event::operator< :
_event_list.sort();
// 注意:上面的方法一和方法二可以注釋掉任何一個,
// 寫兩個,只是為了演示。
}
void Bank::Simulation()
{
OpenForDay();
Event event;
while(!_event_list.empty()) {
event = _event_list.front();
_event_list.pop_front();
if(event._type == 0) // 到達事件
CustomerArrived(&event);
else
CustomerDeparture(&event);
}
// 計算並輸出平均逗留時間
std::wcout << L"客戶數:" << _customer_number << std::endl
<< L"總逗留時間(小時):" << (double)_total_time / 3600.0
<< std::endl
<< L"平均逗留時間(分鍾):"
<< (double)_total_time / (double)(_customer_number * 60)
<< std::endl;
}
int wmain(int argc, wchar_t *argv[], wchar_t *env[])
{
_wsetlocale(LC_ALL, L"");
Bank bank;
bank.Simulation();
return 0;
}
stdafx.cpp
// stdafx.cpp : source file that includes just the standard includes // bank.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file
頭文件:
bank.h
#ifndef __BANK_H__
#define __BANK_H__
#include <queue>
#include <list>
struct Event
{
int _occur_time; // 事件發生的時刻
int _type; // 事件類型,0表示到達事件,1到
// 4 表示四個窗口的離開事件
bool operator<(const Event &rhs)
{
return _occur_time < rhs._occur_time;
}
};
struct QueueNode
{
int _arrival_time; // 客戶到達時間
int _duration_time;// 客戶需要的服務員時間
};
class Bank
{
public:
explicit Bank(int window_number = 4,
int close_time = 8 * 3600);
~Bank();
void Simulation();
private:
int _queue_number; // 隊列個數
int _close_time; // 關門時間
int _total_time; // 累計客戶逗留時間
int _customer_number; // 客戶總數
std::list<Event> _event_list; // 事件鏈表
std::queue<QueueNode> *_work_queue;// 工作隊列
void OpenForDay();
void CustomerArrived(Event *event);
void CustomerDeparture(Event *event);
int FindShortestQueue();
void SortEventList();
};
#endif
stdafx.h
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include "targetver.h" #include <stdio.h> #include <tchar.h> // TODO: reference additional headers your program requires here
targetver.h
#pragma once // Including SDKDDKVer.h defines the highest available Windows platform. // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include <SDKDDKVer.h>
運行結果:
