前言
如果你已经学习了队列Queue的知识,那么不妨看一看这篇文章,本文将加深你对队列在实际应用中的理解。
如果你还没有学习队列Queue,不妨先看看这篇文章:数据结构——Queue队列(C++)
注:本文中的程序使用C++实现,如果你是Java用户,也可以进行阅读;如果你是Python用户且没有学习过C++或Java,那么这篇文章的部分代码可能看起来会使人困惑,请自行补充C++知识。
小型机场运作模式
我们的机场大体分为三个部分:空中等待队列、地面等待队列和一条跑道。
在每个时间节点,空中可能会飞来m架飞机进入等待队列等待降落,地面可能会前来n架飞机等待起飞(m,n >= 0)。由于你只有一条跑道,所以在每个时间节点,这条跑道只能供一架飞机用于起飞或者降落。同时,为了安全考虑,我们要优先让空中的飞机降落。
以下是程序的具体运行效果:
分析
先来分析一下整个程序的运行过程:
- 首先,我们有四个输入。第一个输入为等待队列的最大容量,如果队列满了,那么再有飞机前来时我们将拒绝接收;第二个输入为运行时间,如上图,输入5,则程序会给出机场从第0个时间节点到第4个时间节点的运行情况;第三、第四个输入仅仅为我们期待起飞率和降落率,与实际运行无关,因此此处仅仅为两个无关的输入。
- 然后机场开始运行,可以看到,在每个时间节点,程序先输出跑道的状态:跑道为空、进行飞机起飞、进行飞机降落。随后,随机生成m架飞机和n架飞机分别准备降落和准备起飞。
- 最后,到达预设时间,机场关闭,输出运行日志。
通过上面的执行过程,我们在来分析一下我门大体需要编写哪些类,并且这些类里需要有哪些属性和方法。这是一个将过程转化为对象的步骤,如果你刚刚学完了C语言并且正在学习C++,这一步骤能加快你从“面向过程”的思想转化到“面向对象”的思想。
我们通过伪代码来编写我们可能需要的类的框架,这将有助于你理解分析整个程序的构建过程:
class Airport {
private:
int max_cap; //最大可同时接待飞机数量
int running_time; //机场运行时间
int idle_time; //跑道为空的时间
queue<Plane> ground; //地面队列
queue<Plane> sky; //空中队列
Runway land; //跑道降落记录
Runway fly; //跑道起飞记录
public:
Airport(int m_cap); //初始化机场
void accept(Plane a,bool fly_or_land); //接待飞机
void runway_operate(); //跑道操作:起飞&降落
void close_airport();
//关闭机场,运行总结,可以将这个方法放到析构函数中
};
class Plane {
private:
int accepted_time; //被机场接收的时刻
int process_time; //被机场处理 起飞/降落 请求的时刻
int num; //飞机编号
bool fly_or_land; 起飞(false)or落地(true)?
public:
Plane(int n,int ac_time,bool s_or_land); //初始化飞机
void fly(int p_time); //起飞
void land(int p_time); //降落
int wait_time(); //获取飞机等待时间
int number(); //获取飞机编号
};
class Runway {
bool fly_or_land; //起飞(f)/降落(t) 跑道
public:
Runway();
void set_kind(bool f_ot_l); //设置跑道用途
int asked; //收到的总飞机请求数量
int accepted; //接收的数量
int rejected(); //拒绝的数量
int processed; //实际处理的数量
int left(); //机坪上剩余飞机的数量
int wait; //请求被延时的飞机数量
double wait_rate(); //等待率
double observed_rate(int total_time);
//单位时间机收到的飞机请求
};
class PlaneBuilder {
public:
int build(); //随机生成几架飞机
}
接下来,我们便可以用伪代码分析一下整个程序的驱动函数main是怎样的结构:
int main() {
PlaneBuilder a; //创建一个飞机生成器
Airport b; //创建机场
input_max_queue(); //输入
input_time();
input_expect();
for (int i=0;i<time;i++) { //对每个时间节点进行处理
runway_operate(); //先对跑道进行操作:降落/起飞
build_plane(); //生成飞机
accept_plane(); //接收飞机
}
conclude(); //输出机场日志
return 0;
}
代码
进行了上面的分析,便可以开始对每个类进行实现了:
Plane
类头(.h)
#ifndef PLANE_H
#define PLANE_H
class Plane {
int accepted_time; //被机场接收的时刻
int process_time; //被机场处理 起飞/降落 请求的时刻
int num; //飞机编号
bool fly_or_land; //起飞(false)or落地(true)?
public:
Plane();
Plane(int n,int ac_time,bool s_or_land); //初始化飞机
void fly(int p_time); //起飞
void land(int p_time); //降落
int wait_time(); //获取飞机等待时间
int number(); //获取飞机编号
};
#endif //PLANE_H
方法实现(.cpp)
#include"plane.h"
#include<iostream>
using namespace std;
Plane::Plane() {
num=0;
accepted_time=0;
fly_or_land=false;
process_time=0;
}
Plane::Plane(int n,int ac_time,bool f_or_land) {
num=n; //初始化飞机编号
accepted_time=ac_time; //初始化飞机进入机场时的时刻
fly_or_land=f_or_land;
process_time=0;
if (!fly_or_land) cout<<"↗: ";
else cout<<"↘: ";
cout<<"Plane number "<<num<<" ready to "; //输出飞机进入机场时的状态
if (!fly_or_land) cout<<"take off."<<endl;
else cout<<"land"<<endl;
}
void Plane::fly(int p_time) {
process_time=p_time; //记录进入跑道时的时刻
cout<<"Plane number "<<num<<" took off after "<<process_time-accepted_time-1
<<" time units in the takeoff queue."<<endl; //输出起飞状态
}
void Plane::land(int p_time) {
process_time=p_time; //记录进入跑道时的时刻
cout<<"Plane number "<<num<<" landed after "<<process_time-accepted_time-1
<<" time units in the landing queue."<<endl; //输出降落状态
}
int Plane::wait_time() {
return process_time-accepted_time-1; //获取飞机在机场内的等待时间
}
int Plane::number() {
return num; //获取飞机编号
}
Runway
类头(.h)
#ifndef AIRPORT_RUNWAY_H
#define AIRPORT_RUNWAY_H
class Runway {
bool fly_or_land; //起飞(f)/降落(t) 跑道
public:
Runway();
void set_kind(bool f_ot_l);
int asked; //收到的总飞机请求数量
int accepted; //接收的数量
int rejected(); //拒绝的数量
int processed; //实际处理的数量
int wait; //请求被延时的飞机数量
double wait_rate(); //等待率
double observed_rate(int total_time); //单位时间机收到的飞机请求
};
#endif //AIRPORT_RUNWAY_H
方法实现(.cpp)
#include "runway.h"
Runway::Runway() {;
asked=0;
accepted=0;
processed=0;
wait=0;
}
void Runway::set_kind(bool f_ot_l) {
fly_or_land=f_ot_l; //将跑道设置为 起飞(f)/降落(t) 跑道
}
int Runway::rejected() {
return asked-accepted; //输出机场拒绝的飞机数
}
double Runway::wait_rate() {
return (double)wait/(double)accepted; //等待率
}
double Runway::observed_rate(int total_time) {
return (double)asked/(double)total_time;
//单位时间机收到的飞机请求
}
Airport
类头(.h)
#ifndef AIRPORT_AIRPORT_H
#define AIRPORT_AIRPORT_H
#include"Queue.cpp"
#include"runway.h"
#include"plane.h"
using namespace std;
class Airport {
int max_cap; //最大可同时接待飞机数量
int running_time; //机场运行时间
int idle_time; //跑道为空的时间
CircleQueue<Plane> ground; //地面队列
CircleQueue<Plane> sky; //空中队列
Runway land; //降落记录
Runway fly; //起飞记录
public:
Airport(int m_cap,int t); //初始化机场
void accept(bool fly_or_land,int now); //接待飞机
void runway_operate(int now); //跑道操作:起飞&降落
void close_airport(); //关闭机场,运行总结
};
void introduction(int &max_queue,int &run_time);
//机场初始化介绍及输入
#endif //AIRPORT_AIRPORT_H
方法实现(.cpp)
#include"airport.h"
#include"plane.h"
#include<iostream>
using namespace std;
Airport::Airport(int m_cap,int t) {
this->max_cap=m_cap;
this->running_time=t;
this->idle_time=0;
this->fly.set_kind((0));
this->land.set_kind((1));
}
void Airport::accept(bool fly_or_land,int now) {
if (!fly_or_land) {
this->fly.asked++;
Plane a(fly.asked+land.asked,now,fly_or_land); //创建一架准备起飞的飞机
if (this->ground.size()==max_cap) { //队列满载,拒绝接待
cout<<"!: Plane number "<<a.number()<<" told to try to takeoff again later"<<endl;
return;
}
this->fly.accepted++;
this->ground.push(a); //入队
}
else {
this->land.asked++;
Plane a(fly.asked+land.asked,now,fly_or_land); //创建一架准备降落的飞机
if (this->sky.size()==max_cap) { //队列满载,拒绝接待
cout<<"!: Plane number "<<a.number()<<" directed to another airport"<<endl;
return;
}
this->land.accepted++;
this->sky.push(a); //入队
}
}
void Airport::runway_operate(int now) {
cout<<">>"<<now<<": "; //输出当前跑道状态
if (sky.empty()&&ground.empty()) { //跑道为空
cout<<"Runway is idle."<<endl;
idle_time++;
}
else {
if (!sky.empty()) { //用于降落
sky.front().land(now);
land.processed++;
if (sky.front().wait_time()) land.wait++; //检测飞机有没有经过等待
sky.pop();
}
else { //用于起飞
ground.front().fly(now);
fly.processed++;
if (ground.front().wait_time()) fly.wait++; //检测飞机有没有经过等待
ground.pop();
}
}
}
void Airport::close_airport() { //总结机场运行记录
cout<<"Simulation has concluded after "<<running_time<<" time units."<<endl
<<"Total number of planes processed "
<<fly.asked+land.asked<<endl
<<"Total number of planes asking to land "
<<land.asked<<endl
<<"Total number of planes asking to take off "
<<fly.asked<<endl
<<"Total number of planes accepted for landing "
<<land.accepted<<endl
<<"Total number of planes accepted for takeoff "
<<fly.accepted<<endl
<<"Total number of planes refused for landing "
<<land.rejected()<<endl
<<"Total number of planes refused for takeoff "
<<fly.rejected()<<endl
<<"Total number of planes that landed "
<<land.processed<<endl
<<"Total number of planes that took off "
<<fly.processed<<endl
<<"Total number of planes left in landing queue "
<<sky.size()<<endl
<<"Total number of planes left in takeoff queue "
<<ground.size()<<endl;
cout<<"Percentage of time runway idle "
<<100*(double)idle_time/(double)running_time<<"%"<<endl;
cout<<"Average wait in landing queue "
<<land.wait_rate()<<" time units"<<endl;
cout<<"Average wait in takeoff queue "
<<fly.wait_rate()<<" time units"<<endl;
cout<<"Average observed rate of planes wanting to land "
<<land.observed_rate(running_time)<<" per time unit"<<endl;
cout<<"Average observed rate of planes wanting to take off "
<<fly.observed_rate(running_time)<<" per time unit"<<endl;
}
void introduction(int &max_queue,int &run_time) {
cout<<"This program simulates an airport with only one runway."<<endl
<<"One plane can land or depart in each unit of time."<<endl;
label:
cout<<"Up to what number of planes can be waiting to land or take off at any time: ";
cin>>max_queue;
getchar();
if (max_queue>100) {
cout<<"Sorry, we don't have so much place!"<<endl;
goto label;
}
cout<<"How many units of time will the simulation run: ";
cin>>run_time;
getchar();
double land_rate,fly_rate;
label1:
cout<<"Expected number of arrivals per unit time: ";
cin>>land_rate;
getchar();
cout<<"Expected number of departures per unit time: ";
cin>>fly_rate;
getchar();
if (land_rate<0.0||fly_rate<0.0) {
cout<<"These rate must be nonnegative."<<endl;
goto label1;;
}
if (land_rate+fly_rate>1.0) {
cout<<"Safety warning: This airport will become saturated."<<endl;
}
}
PlaneBuilder
这是一个随机数生成器,我们通过获取时间戳并对一个特定的素数取模来获得伪随机数。
类头(.h)
#ifndef AIRPORT_PLANE_BUILDER_H
#define AIRPORT_PLANE_BUILDER_H
class PlaneBuilder { //随机生成飞机
int seed;
int multiplier,add_on;
double build_real();
int reseed();
public:
PlaneBuilder();
int build(int low,int high);
};
#endif //AIRPORT_PLANE_BUILDER_H
方法实现(.cpp)
#include"plane_builder.h"
#include<ctime>
#include<iostream>
PlaneBuilder::PlaneBuilder() {
seed=time(NULL)%INT32_MAX;
multiplier=2743;
add_on=5923;
}
int PlaneBuilder::reseed() {
seed=seed*multiplier+add_on;
return seed;
}
double PlaneBuilder::build_real() {
double max=INT32_MAX+1.0;
double temp=reseed();
if (temp<0) temp+=max;
return temp/max;
}
int PlaneBuilder::build(int low,int high) {
//生成的随机数在low和high之间
if (low>high) {
return build(high,low);
}
else {
return (int)((high-low)*build_real()+low);
}
}
CircleQueue
在我的示例代码中,使用了自己使用数组实现的CircleQueue,你也可以替换为上篇文章数据结构——Queue队列(C++)中我们实现的链式Queue或使用STL模版库中的queue。
#include<iostream>
template<typename T> class CircleQueue {
T data[100];
int count;
int real;
public:
CircleQueue();
bool empty();
int size();
void push(T in_);
void pop();
T front();
T back();
void clear();
};
template<typename T> CircleQueue<T>::CircleQueue() {
count=0;
real=0;
}
template<typename T> bool CircleQueue<T>::empty() {
return !(bool)count;
}
template<typename T> int CircleQueue<T>::size() {
return count;
}
template<typename T> void CircleQueue<T>::push(T in_) {
if (count==100) {
std::cout<<"full!"<<std::endl;
return;
}
data[real]=in_;
real++;
count++;
if (real>=100) real=0;
}
template<typename T> void CircleQueue<T>::pop() {
if (count==0) {
std::cout<<"empty!"<<std::endl;
return;
}
count--;
}
template<typename T> T CircleQueue<T>::front() {
int i=real;
for (int k=0;k<count;k++) {
i--;
if (i<0) i=99;
}
return data[i];
}
template<typename T> T CircleQueue<T>::back() {
int i=real-1;
if (i<0) i=99;
return data[i];
}
template<typename T> void CircleQueue<T>::clear() {
real=0;
count=0;
}
main
#include"plane_builder.h"
#include"airport.h"
enum request {fly=0,land=1};
int main() {
int max_cap,total_time,land_request,fly_request;
introduction(max_cap,total_time); //输入机场容量及运行总时间
Airport HongQiao(max_cap,total_time); //创建机场
PlaneBuilder p; //创建一个飞机生成器
for (int clock=0;clock<total_time;clock++) {
HongQiao.runway_operate(clock); //处理机场中的飞机
land_request=p.build(0,2); //随机生成准备降落的飞机数量
while (land_request--) { //接待需要降落的飞机
HongQiao.accept(land,clock);
}
fly_request=p.build(0,3); //随机生成准备起飞的飞机数量
while (fly_request--) { //接待需要起飞的飞机
HongQiao.accept(fly,clock);
}
}
cout<<"---------------------------Airport Summary--------------------------"<<endl;
HongQiao.close_airport();
getchar();
return 0;
}
总结
完成本文的练习程序后,相信你能对队列Queue的应用有更加深刻的印象与理解,同时加深了你对面向对象这一思想的理解与思考。下面我将放出整个程序的CLion工程文件:https://pan.baidu.com/s/1jXkGl4YE7qb67YRyJ7fQBg?pwd=6666