问题
模拟电梯的运行. 以下为基本假设和调度策略,可以根据需要添加其他假设或对现有策略进行修改:
电梯最初从一楼开始运行
如果没有呼叫,则停在原地
乘客先下后上,如果满员,不能超载
如果电梯已经满员,且中间层无乘客出电梯,则直达目的层
请分别模拟只有一部电梯和有两部电梯(联动)的系统,并讨论如何优化其调度策略.
假设和约定
- 乘客自觉 乘客呼叫电梯的行为是自身意愿的准确反映:乘客在电梯外的某一楼层呼叫时,要上楼的话,那么他会按上楼键而不是下楼键。
- 乘客出入电梯理想 电梯移动过程中可能停靠在某一楼层,乘客出入电梯不需要损耗时间。因此电梯移动过程的时间开销只与电梯路径有关,与是否停靠、停靠时出入乘客的流量无关。
- 电梯状态理想 电梯只有停止状态和移动状态,移动时电梯匀速运动,而不是像现实中要加速、均速、减速来回变换。
实现代码
抽象
乘客类 cpassenger
成员 | 含义 |
---|---|
from_floor | 所在楼层 |
to_floor | 要去楼层 |
isin | 状态:是否在特定组 |
id | 编号 |
电梯类 Elevator
成员 | 含义 |
---|---|
nowFloor | 电梯当前所在楼层 |
pin[maxEleNum] | 电梯里的乘客列表 |
run_status | 电梯运行状态 |
clockBegin | 开始运行时刻 |
clockExpectAim | 最近停靠的时刻 |
inButton | 电梯内楼层按钮 |
exButton | 电梯外楼层按钮 |
大楼类(模拟过程)SimBuilding
成员 | 含义 |
---|---|
e1 | 电梯1( |
pwaitting | 大楼中正在等电梯的乘客 |
phav | 大楼中已经坐过电梯的乘客 |
timeRunaFloor | 电梯运行时经过一层楼所需时间 |
simulationLength | 模拟时长 |
Simulation.h
#define maxEleNum 10
#define BudfloorNum 9
enum isInElevator{in,out};
class cpassenger
{
public:
int from_floor;//所在楼层
int to_floor;//要去楼层
isInElevator isin;//是否在特定组中,1在,0不在
char id;//编号
cpassenger();
cpassenger(int from, int to);
};
enum runStatus{up, down, stop};
enum floorStatus{aim, pass};
class Elevator
{
public:
int nowFloor;//电梯当前所在楼层
cpassenger pin[maxEleNum];
runStatus run_status;//电梯运行状态
int clockBegin;
int clockExpectAim;
floorStatus inButton[BudfloorNum+1];//楼层按钮,1按下,0未按(目的楼层)
floorStatus exButton[BudfloorNum+1];//消息队列,(外面等待人所在的楼层)
Elevator();//构造函数,默认9层
int CurCount();
};
class SimBuilding
{
private:
Elevator e1;
cpassenger pwaitting[100]; ///正在等电梯的乘客
cpassenger phav[100]; ///坐过电梯的乘客(人次)
int timeRunaFloor;
int simulationLength;
RandomNumber rnd; // use for arrival and service times
public:
SimBuilding(int mf=9);
bool Havpag(int fl, int fh); //某层楼是否有乘客
int WaitCount();//整栋楼等电梯的乘客数(用于遍历乘客)
int HavCount();//坐过电梯的乘客数(用于总结)
void Call(int clock, cpassenger wp);
void UpdateAim(int clock);
int Enter(int clock);
int Depart(int clock);
void Sort();
void Move(int clock);
void RunSimulation(void); // execute study
void PrintCurStats(); // print stats
void PrintSimulationResults(void); // print stats
};
Simulation.cpp
cpassenger::cpassenger() {
from_floor = 1;
to_floor = BudfloorNum;
isin = in;
id = '?';
}
cpassenger::cpassenger(int from, int to) {
from_floor = from;
to_floor = to;
isin = in;
id = '?';
}
Elevator::Elevator()
{
run_status=stop;//初始停止
nowFloor=1;//电梯初始状态在第一层
for(int i=1; i<=BudfloorNum; i++)//消息数组的初始化
{
inButton[i]=pass;
exButton[i]=pass;
}
for (int j = 0; j < maxEleNum; j++)
pin[j].isin = out;
}
int Elevator::CurCount() {
int curCount=0;
for (int j = 0; j < maxEleNum; j++)
if (pin[j].isin == in)
curCount++;
return curCount;
}
SimBuilding::SimBuilding(int mf) {
Elevator e1=Elevator();
for (int j = 0; j < 100; j++)
pwaitting[j].isin = out; //没有等电梯的人
for (int j = 0; j < 100; j++)
phav[j].isin = out; //没有坐过电梯的人
// cout << "shuru runTimepFloor";
// cin >> timeRunaFloor;
timeRunaFloor = 3;
// cout << "shuru simulationLength";
// cin >> simulationLength;
simulationLength =1000;
}
void SimBuilding::RunSimulation(void) {
cpassenger newPassenger;
int clock = 0;
while (clock < simulationLength){
Move(clock);
if (clock == 0 || clock == 25 || clock == 4 || clock == 7 || clock == 40) { // || clock == 4
newPassenger =cpassenger(); ///之后随机楼层
if (clock == 4) newPassenger=cpassenger(4,2);
if (clock == 7) newPassenger=cpassenger(3,6);
if (clock == 40) newPassenger=cpassenger(9,7);
cout << endl;
PrintCurStats();
cout << clock <<"'s: A pag is calling at F"<< newPassenger.from_floor<< endl;
Call(clock, newPassenger);
}
if (clock == e1.clockExpectAim ){
cout <<endl << clock <<"'s: Arrive at F" << e1.nowFloor<< " please get on and off" << endl;
int nd = Depart(clock);
int ne = Enter(clock);
UpdateAim(clock);
cout <<" "<< nd<<" pags get off ___ "<< ne <<" pags get on "<<endl;
PrintCurStats();
}
clock++;
}
}
void SimBuilding::Call(int clock, cpassenger wp) {
pwaitting[WaitCount()] = wp;
电梯停止,呼叫直接过去
if (e1.run_status == stop){
e1.clockBegin = clock;
正好在同一层
if (wp.from_floor == e1.nowFloor ){
//人要上
if ( wp.to_floor > wp.from_floor){
e1.run_status = up;
e1.clockExpectAim = clock + timeRunaFloor*(wp.from_floor-e1.nowFloor);
}
//人要下
else {
e1.run_status = down;
e1.clockExpectAim = clock + timeRunaFloor*(e1.nowFloor-wp.from_floor);
}
}
///人在上面叫
else if (wp.from_floor > e1.nowFloor ){
e1.run_status = up;
e1.clockExpectAim = clock + timeRunaFloor*(wp.from_floor-e1.nowFloor);
}
///人在下面叫
else{
e1.run_status = down;
e1.clockExpectAim = clock + timeRunaFloor*(e1.nowFloor-wp.from_floor);
}
}
电梯运动,有效呼叫改变clockExpectAim
else{
与电梯同向且在电梯行进方向前
if (e1.run_status == up && wp.from_floor > e1.nowFloor && wp.to_floor > wp.from_floor){
e1.exButton[wp.from_floor] = aim;
///根据最近停靠,改变clockExpectAim
for (int i=e1.nowFloor; i<=BudfloorNum;i++)
if (e1.exButton[i] == aim || e1.exButton[i] == aim){
e1.clockExpectAim = (clock + e1.clockBegin + (e1.nowFloor-wp.from_floor)*timeRunaFloor)
-((clock - e1.clockBegin)%timeRunaFloor);
break;
}
}
if (e1.run_status == up && wp.from_floor==BudfloorNum)
e1.exButton[wp.from_floor] = aim;
e1.exButton[wp.from_floor] = aim;
if (e1.run_status == down && wp.from_floor < e1.nowFloor && wp.to_floor < wp.from_floor ){
e1.exButton[wp.from_floor] = aim;
for (int i=e1.nowFloor; i>=1;i--)
if ( e1.exButton[i] == aim){
e1.clockExpectAim = (clock + e1.clockBegin + (e1.nowFloor-wp.from_floor)*timeRunaFloor)
-((clock - e1.clockBegin)%timeRunaFloor);
break;
}
}
if (e1.run_status == down && wp.from_floor==1)
e1.exButton[wp.from_floor] = aim;
}
}
void SimBuilding::UpdateAim(int clock) {
///电梯掉头,并选择同方向等待者,更新exButton
if (e1.nowFloor == BudfloorNum || e1.CurCount()==0 && !Havpag(e1.nowFloor+1,BudfloorNum) ) {
e1.run_status = down;
e1.clockBegin =clock;
for (int k=0;k<100;k++)
if (pwaitting[k].isin == in &&pwaitting[k].to_floor < pwaitting[k].from_floor)
e1.exButton[pwaitting[k].from_floor] = aim;
}
else if (e1.nowFloor == 1 || e1.CurCount()==0 && !Havpag(1,e1.nowFloor-1)) {
e1.run_status = up;
e1.clockBegin =clock;
for (int k=0;k<100;k++)
if (pwaitting[k].isin == in && pwaitting[k].to_floor > pwaitting[k].from_floor)
e1.exButton[pwaitting[k].from_floor] = aim;
}
///(是否Stop)
if ( e1.CurCount()==0 && !Havpag(1,BudfloorNum))
e1.run_status=stop;
///更改clockExpectAim(只要停靠就会更新)
if (e1.run_status != stop){
if (e1.run_status == down)
for (int i=e1.nowFloor; i>=1;i--)
if (e1.inButton[i] == aim || e1.exButton[i] == aim){
int aimF = i;
e1.clockExpectAim = clock + (e1.nowFloor-aimF)*timeRunaFloor
-(clock - e1.clockBegin)%timeRunaFloor;
break;
}
if (e1.run_status == up)
for (int i=e1.nowFloor; i<=BudfloorNum;i++)
if (e1.inButton[i] == aim || e1.exButton[i] == aim){
int aimF = i;
e1.clockExpectAim = clock + (aimF-e1.nowFloor)*timeRunaFloor
-(clock - e1.clockBegin)%timeRunaFloor;
break;
}
}
}
int SimBuilding::Enter(int clock) {
int numEnter = 0;
if (clock == e1.clockExpectAim){
//遍历整栋楼等待的pag
cpassenger wp;
for (int i=0; i<100; i++){
if (pwaitting[i].isin == in && pwaitting[i].from_floor == e1.nowFloor && e1.CurCount()<maxEleNum){
numEnter++;
wp = pwaitting[i];
e1.exButton[wp.from_floor] = pass;
e1.pin[e1.CurCount()]=pwaitting[i]; //该乘客上电梯
e1.inButton[wp.to_floor]=aim; //按目的楼层
pwaitting[i].isin = out; //相当于在等待队列中移除该乘客
}
}
}
Sort();
return numEnter;
}
int SimBuilding::Depart(int clock) {
int numDepart=0;
if (clock == e1.clockExpectAim ) {
//遍历电梯里的pag
for (int i = 0; i < maxEleNum; i++) {
if (e1.pin[i].isin == in && e1.pin[i].to_floor == e1.nowFloor) {
numDepart++;
phav[HavCount()] = e1.pin[i];
e1.pin[i].isin = out; //相当于在电梯中移除该乘客
e1.inButton[e1.nowFloor] = pass;
}
}
}
Sort();
return numDepart;
}
bool SimBuilding::Havpag(int fl, int fh) { //某楼层有人返回true
bool flag= false;
for (int i=0; i< 100;i++)
if (pwaitting[i].isin==in && (fl<=pwaitting[i].from_floor&&pwaitting[i].from_floor<=fh) ){
flag = true;
break;
}
return flag;
}
int SimBuilding::WaitCount() {
int waitcount = 0;
for (int i=0; i<100; i++)
if (pwaitting[i].isin == in)
waitcount++;
return waitcount;
}
int SimBuilding::HavCount() {
int havcount = 0;
for (int i=0; i<100; i++)
if (phav[i].isin == in)
havcount++;
return havcount;
}
void SimBuilding::PrintCurStats() {
if (e1.run_status==stop) cout << "stop "<<endl;
else if (e1.run_status==up) cout << "up "<<endl;
else if (e1.run_status==down) cout << "down "<<endl;
for (int i=BudfloorNum; i>=1; i--){
if (i == e1.nowFloor){
cout << "|$" ;
for (int i=0;i<5-e1.CurCount()/2;i++) cout <<" ";
for (int i=0;i<e1.CurCount();i++) cout <<"*";
if (e1.CurCount()%2==0) for (int i=0;i<5-e1.CurCount()/2;i++) cout <<" ";
else for (int i=0;i<4-e1.CurCount()/2;i++) cout <<" ";
cout <<"$|"<<endl;
}
else cout << "| " <<i<<" |"<<endl;
}
}
void SimBuilding::Move(int clock) {
if (e1.run_status != stop && (clock - e1.clockBegin)%timeRunaFloor == 0 && (clock - e1.clockBegin)!=0){
if (e1.run_status == up) e1.nowFloor++;
else if (e1.run_status == down) e1.nowFloor--;
}
}
void SimBuilding::Sort() {
cpassenger wp,ip;
for (int i=0;i<100;i++)
if (pwaitting[i].isin == in ){
wp=pwaitting[i];
for (int j=0;j<100;j++){
if (pwaitting[j].isin==out && j<i){
pwaitting[i]=pwaitting[j];
pwaitting[j] = wp;
break;
}
}
}
for (int i=0;i<10;i++)
if (e1.pin[i].isin == in ){
ip=e1.pin[i];
for (int j=0;j<100;j++){
if (e1.pin[i].isin==out && j<i){
e1.pin[i]=pwaitting[j];
e1.pin[i] = ip;
break;
}
}
}
}
main.cpp
#include "Elevator.h"
int main() {
SimBuilding sim;
sim.RunSimulation();
}
效果
假设电梯运行时经过一层楼所用时间为3s
(自定义的测试乘客,便于说明效果)
0s时电梯停止,有人(在一楼,要去九楼)呼叫
0s时电梯停靠在一楼让乘客出入,并开始上行
4s时电梯上行,有人(在四楼,要去二楼)呼叫
24s时电梯停靠在九楼让乘客出入,并开始下行
39s时电梯停靠在四楼让乘客出入,并继续下行
45s时电梯停靠在二楼让乘客出入,之后电梯停止
不足
写了两天,问题很多,不过再没时间耽误在这上面了,后面有空回头来优化吧。
- 电梯运行策略 这是给的是电梯直上直下,当乘客运行与电梯运行方向一致且在电梯前方,电梯就会停靠,比较符合现实直观,当然有待改进。
- 电梯数量 后面SimBuilding类中的运行过程的所有函数都是针对e1(写死了这块),导致无法声明更多的电梯,无法模拟一栋楼内多部电梯的合理调度。
- 模拟乘客 这里乘客的给定在代码中需要自己定义,要模拟大量乘客的话,效率极低。两种改进方法:1、加随机数 随机时刻生成随机状态乘客(所在楼层,目的楼层均随机)2、文件交互 将所有模拟乘客按指定格式记录在一个文本中,代码中添加与该外部文本交互的功能。
- 输出画面 输出窗口给出了所有特殊事件的明细(发生时刻,乘客行为,电梯状态),但是给人一种感觉,输出是对整个过程的记录而不是模拟运行(类似于动画那样)。当然这个实现动态画面要求就比较高了。