小项目分析之C++ 实现模拟银行排队
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>
运行结果:
 
  
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号