数据结构与算法设计——模拟电梯运行

问题

模拟电梯的运行. 以下为基本假设和调度策略,可以根据需要添加其他假设或对现有策略进行修改:

电梯最初从一楼开始运行
如果没有呼叫,则停在原地
乘客先下后上,如果满员,不能超载
如果电梯已经满员,且中间层无乘客出电梯,则直达目的层
请分别模拟只有一部电梯和有两部电梯(联动)的系统,并讨论如何优化其调度策略.在这里插入图片描述

假设和约定

  1. 乘客自觉 乘客呼叫电梯的行为是自身意愿的准确反映:乘客在电梯外的某一楼层呼叫时,要上楼的话,那么他会按上楼键而不是下楼键。
  2. 乘客出入电梯理想 电梯移动过程中可能停靠在某一楼层,乘客出入电梯不需要损耗时间。因此电梯移动过程的时间开销只与电梯路径有关,与是否停靠、停靠时出入乘客的流量无关。
  3. 电梯状态理想 电梯只有停止状态和移动状态,移动时电梯匀速运动,而不是像现实中要加速、均速、减速来回变换。

实现代码

抽象

乘客类 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时电梯停靠在二楼让乘客出入,之后电梯停止
在这里插入图片描述

不足

写了两天,问题很多,不过再没时间耽误在这上面了,后面有空回头来优化吧。

  1. 电梯运行策略 这是给的是电梯直上直下,当乘客运行与电梯运行方向一致且在电梯前方,电梯就会停靠,比较符合现实直观,当然有待改进。
  2. 电梯数量 后面SimBuilding类中的运行过程的所有函数都是针对e1(写死了这块),导致无法声明更多的电梯,无法模拟一栋楼内多部电梯的合理调度。
  3. 模拟乘客 这里乘客的给定在代码中需要自己定义,要模拟大量乘客的话,效率极低。两种改进方法:1、加随机数 随机时刻生成随机状态乘客(所在楼层,目的楼层均随机)2、文件交互 将所有模拟乘客按指定格式记录在一个文本中,代码中添加与该外部文本交互的功能。
  4. 输出画面 输出窗口给出了所有特殊事件的明细(发生时刻,乘客行为,电梯状态),但是给人一种感觉,输出是对整个过程的记录而不是模拟运行(类似于动画那样)。当然这个实现动态画面要求就比较高了。
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
八、 实验内容和要求: 要求根据下面的功能说明描述实现模拟电梯控制软件 (一)电梯配置 1. 共有1个电梯 2. 共有maxfloor层楼层。maxfloor=9。 3. 中间层每层有上下两个按钮,最下层只有上行按钮,最上层只有上行按钮。每层都有相应的指示灯,灯亮表示该按钮已经被按下,如果该层的上行或者下行请求已经被响应,则指示灯灭 4. 电梯内共有maxfloor个目标按钮,表示有乘客在该层下电梯。有指示灯指示按钮是否被按下。乘客按按钮导致按钮指示灯亮,如果电梯已经在该层停靠则该按钮指示灯灭 5. 另有一启动按钮(GO)。当电梯停在某一楼层后,接受到GO信息就继续运行。如果得不到GO信息,等待一段时间也自动继续运行。 6. 电梯内设有方向指示灯表示当前电梯运行方向。 说明:由于本次实验不使用可视化框架,所以无法作到从图形界面上获取按钮请求。因此电梯按钮的设计,不在图形界面上体现,仅用来设计键盘的模拟输入。 (二)电梯运行控制 1.电梯的初始状态是电梯位于第一层处,所有按钮都没有按下。 2.乘客可以在任意时刻按任何一个目标钮和呼叫钮。呼叫和目标对应的楼层可能不是电梯当前运行方向可达的楼层。 3. 如果电梯正在向I层驶来,并且位于I层与相邻层(向上运行时是I-1层或者向下运行时是I+1层)之间,则因为安全考虑不响应此时出现的I层目标或者请求。如果电梯正好经过了I楼层,运行在I楼层和下一楼层之间,则为了直接响应此时出现的I层目标或者请求,必须至少到达运行方向上的下一楼层然后才能掉头到达I楼层(假设掉头无须其它额外时间),如果I楼层不是刚刚经过的楼层则可以在任意位置掉头,此时掉头后经过的第一个楼层不可停。 4. 电梯系统依照某种预先定义好的策略对随机出现的呼叫和目标进行分析和响应。 5. 乘客数量等外界因素(可能导致停靠时间的长短变化)不予考虑。假设电梯正常运行一层的时间是5S,停靠目标楼层、上下乘客和电梯继续运行的时间是5S。 6. 当电梯停靠某层时,该层的乘客如果错误的按目标或呼叫按钮都不予响应。 7. 电梯停要某一层后,苦无目标和呼叫,则电梯处于无方向状态,方向指示灯全灭,否则电梯内某个方向的指示灯亮,表示电梯将向该方向运行。等接到“GO”信号后电梯立即继续运行。若无GO信号,则电梯在等了上下乘客和电梯继续运行时间后也将继续运行。 8. 当一个目标(呼叫)已经被服务后,应将对应的指示灯熄灭。 (三)电梯运行的控制策略 以下是几个候选策略: 1.先来先服务策略: 将所有呼叫和目标按到达时间排队,然后一一完成。这是相当简单的策略,只需要设计一个将呼叫和目标排队的数据结构。因为该策略效率也很低,所以没有实际的电梯采用这种策略。 2. 顺便服务策略: 顺便服务是一种最常见的简单策略。这种策略在运行控制中所规定的安全前提下,一次将一个方向上的所有呼叫和目标全部完成。然后掉转运行方向完成另外一个方向上的所有呼叫和目标。 可以采用设定目标楼层的办法来实现这个策略,即电梯向一个目标楼层运行,但这个楼层可以修改。具体策略如下: 1) 修改目标楼层的策略: a.如果电梯运行方向向上,那么如果新到一个介于当前电梯所处楼层和目标楼层之间,又可以安全到达的向上呼叫或者目标,将目标楼层修改为这个新的楼层。 b.如果电梯运行方向向下,那么如果新到一个介于当前电梯所处楼层和目标楼层之间,又可以安全到达的向下呼叫或者目标,将目标楼层修改为这个新的楼层。 2)确定新的目标楼层: 如果电梯向上运行,当它到达某个目标楼层后,则依照以下顺序确定下一个目标楼层: a.如果比当前层高的楼层有向上呼叫或者目标,那么以最低的高于当前楼层的有向上呼叫或者目标的楼层为目标。 b.如果无法确定目标楼层,那么以最高的向下呼叫或者目标所在楼层为电梯当前目标楼层。 c.如果无法确定目标楼层,那么以最低的向上呼叫所在楼层为电梯当前的目标楼层。 d.如果仍然不能确定目标楼层(此时实际上没有任何呼叫和目标),那么电梯无目标,运行暂停。 如果电梯向下运行,依照以下顺序确定下一目标楼层: a.如果比当前层低的楼层有向下呼叫或者目标,那么以最高的低于当前楼层的有向下呼叫或者目标的楼层为目标。 b.如果无法确定目标楼层,那么以最低的向上呼叫或者目标所在楼层为电梯当前目标楼层。 c.如果无法确定目标楼层,那么以最高的向下呼叫楼层为目标楼层。 d.如果仍然不能确定目标楼层(此时实际上没有任何呼叫和目标),那么电梯无目标,运行暂停。 3)最快响应策略: 响应所有的现在存在的所有呼叫和目标所需时间(采用不同方案电梯停靠时间相同,所以不必考虑)最短的策略。 可选方案一是电梯先向上运行响应经过各层的目标和向上呼叫,再向下运行响应所有向下呼叫以及途经各层的目标,最后再向上响应剩余的向上呼叫。二是恰好相反,
设计一个电梯模拟系统。这是一个离散的模拟程序,由随机事件驱动,以模拟时钟决定乘客或电梯的动作发生的时刻和顺序,系统在某个模拟瞬间处理有待完成的各种事情,然后把模拟时钟推进到某个动作预定要发生的下一时刻。 要求: (1)模拟某校九层教学楼的电梯系统。该楼有一个自动电梯,能在每层停留,其中第一层是大楼的进出层,即是电梯的“本垒层”,电梯“空闲”时,将来到该层候命。 电梯一共有八个状态,即正在开门(Opening)、已开门(Opened)、正在关门(Closing)、已关门(Closed)、等待(Waiting)、移动(Moving)、加速(Accelerate)、减速(Decelerate)。 (2)乘客可随机地进出于任何层。对每个人来说,他有一个能容忍的最长等待时间,一旦等候电梯时间过长,他将放弃。最后一个人放弃能不能取消按键? (3)模拟时钟从0开始,时间单位为0.1秒。人和电梯的各种动作均要消耗一定的时间单位(简记为t),比如: 有人进出时,电梯每隔40t测试一次,若无人进出,则关门; 关门和开门各需要20t; 每个人进出电梯均需要25t; 电梯加速需要15t; 下行时要不要加速? 上升时,每一层需要51t,减速需要14t;每一层和减速? 下降时,每一层需要61t,减速需要23t; 如果电梯在某层静止时间超过300t,则驶回1层候命。驶回本垒层间接到消息? (4)电梯调度规则如下: ①就近原则:电梯的主要调度策略是首先响应沿当前行进方向上最近端的请求直到满足最远端请求。若该方向上无请求时,就改变移动方向; ②在就近原则无法满足的情况下,首先满足更高层的请求; ③电梯的最大承载人数为13人,电梯人数达到13人后,在有人出电梯之前,不接受进入电梯的请求; ④乘客上下电梯时先出后进。进电梯时乘客是按发出乘坐请求的顺序依次进入,每次只能进入一人且每个人花费的时间都为25t; ⑤电梯在关门期间(电梯离开之前)所在层提出请求的乘客同样允许进入。 (5)按时序显示系统状态的变化过程,即发生的全部人和电梯的动作序列。 扩展要求: 实现电梯模拟的可视化界面。用动画显示电梯的升降,人进出电梯设计有下列对象:电梯、人、电梯控制板及其上各种按钮、模拟时钟等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值