Airport Simulation 是数据结构与算法教材中用于演示 Queue 的一个小程序(大多数教师似乎会跳过这个练习)。主程序会通过输入总的运行时间、队列里可以等待的最多飞机数量,平均每个时间单元到来的飞机和离开的飞机(提供泊松分布的均值生成随机数)。
运行效果
程序的结构不算复杂,利用 Runaway 类来封装两个 landing 和 takeoff 队列,处理飞机的请求和行为;Plane 类来封装飞机的状态和信息,以及在受到指令时输出信息到控制台。只是教材里的讲述有些分散,运行效果也由于输出太多而显得凌乱无比。练习布置许久之后也是一直没有找到一整块时间写完这个练习。这里是前几天完成的一个经过输出优化后的程序,格式比较易于观测。
该程序应在 Linux 或 macOS 下编译。在 Windows 下编译由于不支持彩色输出会出现一些奇怪的转移序列,sleep 函数和 usleep 函数也应当用 windows.h 中提供的方法实现。
main.cpp 和 Random.h 的源代码如下。其中 Random.h 用于提供泊松分布的随机数生成,参考自 CSDN。
你可以在这里找到源代码的下载地址。另外,关于在类 unix 系统的控制台输出中不同颜色的文字的方法,我在这里单独写了一篇帖子来介绍详细方法。
#include <iostream>#include <queue>#include <unistd.h>#include "Random.h"using namespace std;typedef int feedback;const int success = 1;const int fail = 0;const int USPEED_SHORT = 1000;const int USPEED_LONG = 1000;enum flightStatus { null, toLand, toTakeoff};enum runawayAct { idle, letTakeoff, letLand};string percentage(int A, int B) { char *temp = new char[100]; sprintf(temp, "%3.2f%%", (double)A / (double)B * 100); string output = temp; return output;}class Plane { private: int ID; int comingTime; flightStatus status; public: Plane() { ID = -1; comingTime = -1; status = null; } Plane(int _ID, int _comingTime, flightStatus _status) { usleep(USPEED_SHORT); ID = _ID; comingTime = _comingTime; status = _status; cout << "[PLANE MESSAGE] Plane NO." << ID << " is applying to " << ((status == toLand) ? "land " : "take off ") << endl; } feedback land(int currentTime) { usleep(USPEED_LONG); cout << "\033[42m[PLANE MESSAGE] Plane NO." << ID << " has landed safely.\033[0m" << endl << " Waiting time before landing: " << currentTime - comingTime << "." << endl; } feedback takeoff(int currentTime) { usleep(USPEED_LONG); cout << "\033[42m[PLANE MESSAGE] Plane NO." << ID << " has taken off.\033[0m" << endl << " Waiting time before taking off: " << currentTime - comingTime << "." << endl; } feedback reject(int currentTime) { usleep(USPEED_LONG); if(status == toLand) { cout << "\033[41m[AIRPORT MESSAGE] Plane NO." << ID << "'s landing request is rejected.\033[0m" << endl << " It has been directed to other airports." << endl; } else{ cout << "\033[41m[AIRPORT MESSAGE] Plane NO." << ID << " 's taking off request is rejected.\033[0m" << endl << " This flight is delayed." << endl; } } int getTime() { return comingTime; }};class Runaway{ private: queue<Plane> landingQueue; queue<Plane> takeoffQueue; int limit; runawayAct act; int landingRequests; int landingAccepted; int landingRejected; int takeoffRequests; int takeoffAccepted; int takeoffRejected; int idleUnit; int landingWaitedTime; int takeoffWaitedTime; public: Runaway(int _limit) { limit = _limit; landingRequests = 0; landingAccepted = 0; takeoffRequests = 0; takeoffAccepted = 0; takeoffRejected = 0; idleUnit = 0; landingWaitedTime = 0; takeoffWaitedTime = 0; } feedback CanLand(const Plane ¤t) { landingRequests++; feedback result; if (landingQueue.size() < limit) { result = success; landingQueue.push(current); } else{ result = fail; landingRejected++; } return result; } feedback CanTakeoff(const Plane ¤t) { takeoffRequests++; feedback result; if (takeoffQueue.size() < limit) { result = success; takeoffQueue.push(current); } else{ result = fail; takeoffRejected++; } return result; } runawayAct Act(int timeNow, Plane &moving) { runawayAct result; if (!landingQueue.empty()) { landingAccepted++; moving = landingQueue.front(); landingWaitedTime += timeNow - moving.getTime(); landingQueue.pop(); return letLand; } else if(!takeoffQueue.empty()) { takeoffAccepted++; moving = takeoffQueue.front(); takeoffWaitedTime += timeNow - moving.getTime(); takeoffQueue.pop(); return letTakeoff; } else{ idleUnit++; return idle; } } void SumUp(int simulationTime) { cout << endl; cout << "\033[36m=============== SIMULATION RESULTS ===============\033[0m" << endl << "\033[5;3mCalculating...\033[0m" << endl; sleep(2); cout << "\033[1ATotal simulation time: " << simulationTime << endl << "Total flights simulated: " << landingRequests + takeoffRequests << endl << "Total flights required to land: " << landingRequests << endl << "Landing request accepted: " << landingAccepted << endl << "Landing request rejected: " << landingRejected << endl << "Total flights required to take off: " << takeoffRequests << endl << "Taking off request accepted: " << takeoffAccepted << endl << "Taking off request rejected: " << takeoffRejected << endl << "Flights still left in landing queue: " << landingQueue.size() << endl << "Flights still left in taking off queue: " << takeoffQueue.size() << endl << "Average waiting time in landing queue: " << (float)landingWaitedTime / (float)landingAccepted << endl << "Average waiting time in taking off queue: " << (float)takeoffWaitedTime / (float)takeoffAccepted << endl << "Average observing time for landing flights: " << (float)landingRequests / (float)simulationTime << endl << "Average observing time for taking off flights: " << (float)takeoffRequests / (float)simulationTime << endl << "Ratio for successfully landed flights: " << percentage(landingAccepted, landingRequests) << endl << "Ratio for successfully took off flights: " << percentage(landingAccepted, landingRequests) << endl << "Ratio of runaway idle time: " << percentage(idleUnit, simulationTime) << endl << endl << "\033[3;2mSimulation finished.\033[0m" << endl; }};void initilize(int &totalTime, int &queueLimit, float &arrivingRate, float &departureRate) { cout << "\033[2J\033[0;0H"; cout << "Welcome to the \033[46mAIRPORT SIMULATOR\033[0m." << endl; sleep(1); cout << "\033[36mPlease enter how many time units you will simulate:\033[0m\n" << flush; cin >> totalTime; cout << "\033[36mHow many flights can be waiting to land or takeoff?\033[0m" << endl << flush; cin >> queueLimit; cout << "\033[36mWhat is the expected arriving flights per unit time?\033[0m" << endl << flush; cin >> arrivingRate; cout << "\033[36mWhat is the expected departing flights per unit time?\033[0m" << endl << flush; cin >> departureRate;}int main() {START: float arrivingRate, departureRate; int totalTime, queueLimit; initilize(totalTime, queueLimit, arrivingRate, departureRate); int currentTime; int planeID = 0; Random randomSeed; Runaway airport(queueLimit); for (currentTime = 0; currentTime < totalTime; currentTime++) { cout << "\033[2m----- Current excution time: " << currentTime << " -----\033[0m" << endl; int comingPerUnit = randomSeed.poisson(arrivingRate); for (int num = 0; num < comingPerUnit; num++, planeID++) { Plane currentPlane(planeID, currentTime, toLand); if(airport.CanLand(currentPlane)==fail) { currentPlane.reject(currentTime); } } int departingPerUnit = randomSeed.poisson(departureRate); for (int num = 0; num < departingPerUnit; num++, planeID++) { Plane currentPlane(planeID, currentTime, toTakeoff); if(airport.CanTakeoff(currentPlane)==fail) { currentPlane.reject(currentTime); } } Plane movingPlane; switch (airport.Act(currentTime, movingPlane)) { case letLand: movingPlane.land(currentTime); break; case letTakeoff: movingPlane.takeoff(currentTime); break; case idle: cout << "\033[46m[AIRPORT MESSAGE]: Runaway is idle.\033[0m" << endl; break; } } airport.SumUp(totalTime); char ch; cout << "\033[36mDo you want to initialize another simulation? \n([R] or [r] to restart, any other key to exit.)\033[0m" << endl; while(cin.get()!='\n') ; if (toupper(ch = cin.get()) == 'R') goto START; return 0;}
#ifndef RANDOM_H_INCLUDED#define RANDOM_H_INCLUDED#include <iostream>#include <time.h>#include <limits.h>#include <math.h>#include "Random.h"using namespace std;class Random { public: Random(bool pseudo = true); double random_real(); int random_integer(int low, int high); int poisson(double mean); private: int reseed(); // Re-randomize the seed. int seed, multiplier, add_on; // constants for use in arithmetic operations};/*
Post: The values of seed, add_on, and multiplier are
initialized. The seed is initialized randomly only if pseudo == false.
*/Random::Random(bool pseudo) { if (pseudo) seed = 1; else seed = time(NULL) % INT_MAX; multiplier = 2743; // 斜率 add_on = 5923; // 位移}// Post: The seed is replaced by a pseudorandom successor.int Random::reseed() { seed = seed * multiplier + add_on; return seed;}// Post: A random real number between 0 and 1 is returned.double Random::random_real() { double max = INT_MAX + 1.0; //INT_MAX = (2)31 -1 double temp = reseed(); if (temp < 0) temp = temp + max; return temp / max;}// 这个函数在泊松分布中没有用到int Random::random_integer(int low, int high) { double max = INT_MAX + 1.0; //INT_MAX = (2)31 -1 double temp = reseed(); if (temp < 0) temp = temp + max; return (int)(temp / (max / (high - low + 1.0) + low)); // 返回整数,且有规定范围}// 泊松分布的实现int Random::poisson(double mean) { double x = -1; double u; double log1, log2; log1 = 0; log2 = -mean; do { u = random_real(); log1 += log(u); x++; } while (log1 >= log2); return x;}#endif // RANDOM_H_INCLUDED