队列的应用练习:小型机场调度

前言

如果你已经学习了队列Queue的知识,那么不妨看一看这篇文章,本文将加深你对队列在实际应用中的理解。

如果你还没有学习队列Queue,不妨先看看这篇文章:数据结构——Queue队列(C++)

注:本文中的程序使用C++实现,如果你是Java用户,也可以进行阅读;如果你是Python用户且没有学习过C++或Java,那么这篇文章的部分代码可能看起来会使人困惑,请自行补充C++知识。

小型机场运作模式

我们的机场大体分为三个部分:空中等待队列、地面等待队列和一条跑道。

在每个时间节点,空中可能会飞来m架飞机进入等待队列等待降落,地面可能会前来n架飞机等待起飞(m,n >= 0)。由于你只有一条跑道,所以在每个时间节点,这条跑道只能供一架飞机用于起飞或者降落。同时,为了安全考虑,我们要优先让空中的飞机降落。

以下是程序的具体运行效果:

Airport

分析

先来分析一下整个程序的运行过程:

  • 首先,我们有四个输入。第一个输入为等待队列的最大容量,如果队列满了,那么再有飞机前来时我们将拒绝接收;第二个输入为运行时间,如上图,输入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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值