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

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. http://blog.csdn.net/pipisorry/article/details/51471222

    这个博主很有意思 机器学习之用Python从零实现贝叶斯分类器 参数估计:贝叶斯思想和贝叶斯参数估计

  2. LabelImg 图像图像标注工具

    https://github.com/tzutalin/labelImg windos版本请不要把存放在中文目录下,否则打不开.

  3. 转载 Spring详细教程

    SpringMVC学习笔记---- 一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于Spring ...

  4. MATLAB常用快捷键命令总结

    1. 在命令窗口(Command Window)中: 1)[↑.↓]——切换到之前.之后运行过的命令,可以重复按多次来达到你想要的命令: 2)[Tab]——自动补全.在command窗口,输入一个命令 ...

  5. PAT A1151 LCA in a Binary Tree (30 分)——二叉树,最小公共祖先(lca)

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  6. 软概(lesson 1):Javaweb实现用户登录界面

    一.问题描述 二.网站系统开发所需要的技术 网站界面开发:html 后台所需要的技术:java基本内容,数据库语句,连接数据库实现增删改查 本题所用技术:数据库链接以及增加功能,基本html语句 技术 ...

  7. kubernetes中infra容器的理解

    1. infra容器和用户容器的关系 1.1 pause 是k8s的基础设施的一部分,pod中其他容器通过pause容器跟其他pod进行通信. 1.2 pod中其他容器跟pause容器共享命名空间1. ...

  8. 如何使用串口来给STM32下载程序

    前言 第一次学习STM32的时候,不知道有调试器这个东西,所以一直是通过串口来给STM32下载程序,下载速度也还算可以,一般是几秒钟完成.后来用了调试器,可以直接在Keil环境下进行下载,而且还可以进 ...

  9. KVM虚拟机管理——资源调整

    1. 概述2. 计算资源调整2.1 调整处理器配置2.2 调整内存配置3. 存储资源调整3.1 根分区扩展3.2 添加磁盘4. 网络资源调整 1. 概述 KVM在使用过程中,会涉及到计算(CPU,内存 ...

  10. Munge服务部署和测试

    1. 概述2. 下载3. 安装3.1 源码简要说明3.2 编译安装3.3 配置3.4 创建munge.key3.5 启动方式 1. 概述 munge是认证服务,用于生成和验证证书.应用于大规模的HPC ...