一、问题定义与分析
问题定义
•要解决的问题——银行一天之内的:

1.总客户数

2.客户总逗留时间

3.客户平均逗留时间

问题分析

•新来的人找个短的队伍,站在队尾开始排队
•排在队头的人可以办理业务
•排队等待办业务的客户是在分散的、随机的时间点到来的
•特点:离散事件、要排队
•掌握每个客户到达银行和离开银行这两个时刻
•统计出客户总数
•称客户到达银行和客户离开银行这两个时刻发生的事情为“事件”
•整个模拟按事件发生的先后顺序进行处理
•事件驱动模拟
•事件的主要信息是事件类型和事件发生的时刻
•两类事件:客户到达事件和客户离开事件
•事件应存储在有序表里
•有序表按照事件发生的时刻顺序排序
•队列中的客户的主要信息是客户到达的时刻和客户办理业务所需要的时间
•队列数量和银行的窗口数量相同
•每个队列的队头客户就是正在办理业务的客户
•每个队头客户都存在一个将要发生的客户离开事件
 
二、类与算法设计
类设计
•有序表选用有序链表,主要操作是插入和删除
•队列,客户排队
 
•类图:用来表示类以及类和类之间的关系的逻辑视图
•利用类图来记录类的结构,这些类构成了程序的架构

银行类:

•要有一个表示队列数量的属性
•要有一个打烊时间属性
•要有一个总客户数属性
•要有一个客户总逗留时间属性
•聚合一个或多个队列和一个有序链表

银行类的方法:

•开门营业
•处理客户到达事件
•处理客户离开事件
•帮助客户选择一个最短的队列
•确保按照事件发生的时间顺序处理事件
 
银行类类图:
 

队列:

•使用STL中的queue
•queue的节点:

有序链表:

•使用STL中的list
•list的节点:
 
算法设计

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.如果队列不空则设定一个新的队头客户离开事件

队头离开事件发生时刻=上个离开事件发生时刻(队头开始办业务的时刻)+队头办业务需要时间

 
三、C++程序编写
 附注:
Lambda表达式简介
•只有当可以应用一个()调用操作符符到一个对象时,这个对象才是可调用对象
•函数:可调用对象
•函数指针:可调用对象
•重载了()调用操作符的类:可调用对象
•Lambda表达式:可调用对象
•一个Lambda表达式表示了一个可调用的代码单元
•可以认为是一个无名的、内联的函数
•可以定义在函数内部
•有一个返回类型、一个参数列表和一个函数体

[capture list](parameter list)->return type {function body}

[capture list](parameter list)->return type {function body}

•capture list:Lambda表达式所在的函数的局部变量
•把局部变量写在capture list里可以在Lambda表达式内部使用它们
•return type:返回类型(尾置返回指定方式)
•parameter list:参数列表
•function body:函数体

举例:

_event_list.sort(

[](const Event &e1, const Event &e2) -> bool

{return e1._occur_time < e2._occur_time;});

定义头文件

•头文件中通常包含类的定义、常量的定义和常量表达式的定义
•习惯:头文件的名字通常和其中定义的类的名字相同
•头文件可以用#include包含其它头文件
•#include是预处理器提供的功能
•为了让头文件可以被安全地包含多次,就需要使用预处理器
•预处理器不是C++语言的一部分
•可以用#define定义一个预处理变量
•预处理器变量有两种状态:己定义和未定义
•#ifdef:如果预处理变量己定义,返回true
•#ifndef:如果预处理量未定义,返回true
•只有测试为true时,才继续预处理直到#endif
•头文件保护符,以bank.h为例 :

#ifndef __BANK_H__

#define __BANK_H__

// #include<其它头文件>

// const、constexpr and class 定义

// extern 多文件共享变量声明

#endif

<------以下为实现代码------>

 
bank.cc
#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>

运行结果:

  

  

  

  

  

小项目分析之C++ 实现模拟银行排队的更多相关文章

  1. java线程基础巩固---采用多线程方式模拟银行排队叫号以及Runnable接口存在的必要性

    采用多线程模拟银行排队叫号: 关于银行拿排队号去叫号的过程我想不必过多解释了,就是有几个业务窗口,并行的处理业务,每处里完一个人,则会叫下一个排队的号去处理业务,一个人是不会被多个窗口工作人员叫号的, ...

  2. 用java模拟银行柜台排队

    4年前在办理银行业务的时候,看到每个办理柜台窗口前都有很多人排队. 同时在那个时间段,我正好重温了数据结构这本书.好像里面有提到银行. 所以当时就用java写了一段小程序来模拟窗口排队的情况. 有兴趣 ...

  3. C++ 事件驱动型银行排队模拟

    最近重拾之前半途而废的C++,恰好看到了<C++ 实现银行排队服务模拟>,但是没有实验楼的会员,看不到具体的实现,正好用来作为练习. 模拟的是银行的排队叫号系统,所有顾客以先来后到的顺序在 ...

  4. 复杂模拟 | 1014 模拟K个同时到来的人在N个窗口,每个窗口只能站M个人的银行排队

    这题我以为还是之前银行排队的思路,但是做着做着就做不下去了了.看了答案我才理解到底是什么个思路. 是这样的:不同于之前排队的题,这里的K个人是同时到来的.所以首先应该让K个人的前N*M(也就是黄线内的 ...

  5. Jdbc来操作事物 完成模拟银行的转账业务

    创建JDBC工具类 package cn.aa4_2.JDBCUtils; import java.io.FileReader; import java.io.IOException; import ...

  6. JDBC事务的处理-----模拟银行转账业务

    定义: 数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成. 概要: 一个数据库事务通常包含了一个序列的对数据库的读/写操作.它的存在包含有以下两个目的: ...

  7. Java基础面试操作题:线程同步代码块 两个客户往一个银行存钱,每人存三十次一次存一百。 模拟银行存钱功能,时时银行现金数。

    package com.swift; public class Bank_Customer_Test { public static void main(String[] args) { /* * 两 ...

  8. PAT甲题题解-1109. Group Photo (25)-(模拟拍照排队)

    题意:n个人,要拍成k行排队,每行 n/k人,多余的都在最后一排. 从第一排到最后一排个子是逐渐增高的,即后一排最低的个子要>=前一排的所有人 每排排列规则如下: 1.中间m/2+1为该排最高: ...

  9. MySQL从库生成大量小的relay log案例模拟

    最近看到"八怪"写的<MySQL:产生大量小relay log的故障一例>,因之前也遇到类似的情况,一直没搞懂原理及复现,看完此文章后,本着实践是检验真理的唯一标准的原 ...

随机推荐

  1. asp.net loading 动画

    https://www.cnblogs.com/AnneHan/p/7477761.htmlhttps://blog.csdn.net/qq_33769914/article/details/7115 ...

  2. oracle 查询分区表的最大分区以及分区字段的最大值

    select 'select max(' || column_name || ') from ' || owner || '.' || name || ';'  from DBA_PART_KEY_C ...

  3. 理解socket.io(一)---相关的API

    理解socket.io(一)---相关的API 1. 什么是Socket.IO?Socket.IO是node.js的一个模块,它用于浏览器与服务端之间实时通信.它提供了服务器和客户端的组件,只需一个模 ...

  4. Android学习之基础知识十三 — 四大组件之服务详解第二讲(完整版的下载示例)

    上一讲学习了很多关于服务的使用技巧,但是当在真正的项目里需要用到服务的时候,可能还会有一些棘手的问题让你不知所措.接下来就来综合运用一下,尝试实现一下在服务中经常会使用到的功能——下载. 在这一讲我们 ...

  5. POJ3301 Texas Trip 计算几何、随机化贪心

    传送门--Vjudge 三分写法似乎有问题,可以去Udebug上看Morass的\(666\)个测试点的数据,我的乱搞有很多比正解答案小,但还是能在SPOJ和POJ过,可见数据之水. 可以对正方形的角 ...

  6. HTML5-应用程序缓存(Application Cache)

    一.什么是应用程序缓存? HTML5 引入了应用程序缓存(Application Cache),这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问. 二.优势 离线浏览 - 用户可在应用 ...

  7. VB6 变量定义作用域的一个奇特形式

    C#或JAVA 下面的i定义是只会限定在if 条件块里的: if (1 == 2) { int i = 000; } else { i = 111;// 错误,未定义. } i = 222;//错误 ...

  8. Luogu P2312 解方程

    据大佬的说法这种大力乱搞题出在除NOIp以外的任何比赛都是很好的然而就是被出在了NOIp 首先对于想直接上高精的同学,我还是祝你好运吧. 我们考虑一个十分显然的性质,若\(a=b\),则对于任一自然数 ...

  9. 机器学习(三)--- scala学习笔记

    Scala是一门多范式的编程语言,一种类似Java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编程和函数式编程的各种特性. Spark是UC Berkeley AMP lab所开源的类Had ...

  10. 汇编 STD和CLD指令

    一.用纯汇编封装函数strcmpW 1.用repnz scasw计算字串长度 2.用repz cmpsw比较字串内容 3.把比较的结果存放在EAX里边返回 __declspec(naked) int ...