今天为大家带来一个超级好玩的电梯模拟系统
·题目概要
模拟某校九层教学楼的电梯系统。该楼有一个自动电梯,能在每层停留,其中第一层是大楼的进出层,即是电梯的“本垒层”,电梯“空闲”时,将来到该层候命。
电梯一共有七个状态,即正在开门(Opening)、已开门(Opened)、正在关门(Closing)、已关门(Closed)、等待(Waiting)、移动(Moving)、减速(Decelerate)。
乘客可随机地进出于任何层。对每个人来说,他有一个能容忍的最长等待时间,一旦等候电梯时间过长,他将放弃。
模拟时钟从0开始,时间单位为0.1秒。人和电梯的各种动作均要消耗一定的时间单位(简记为t),比如:
有人进出时,电梯每隔40t测试一次,若无人进出,则关门;
关门和开门各需要20t;
每个人进出电梯均需要25t;
电梯加速需要15t;
上升时,每一层需要51t,减速需要14t;
下降时,每一层需要61t,减速需要23t;
如果电梯在某层静止时间超过300t,则驶回1层候命。
电梯调度规则:
1)就近原则:电梯的主要调度策略是首先响应沿当前行进方向上最近端的请求直到满足最远端请求。若该方向上无请求时,就改变移动方向;
2)在就近原则无法满足的情况下,首先满足更高层的请求;
3)电梯的最大承载人数为13人,电梯人数达到13人后,在有人出电梯之前,不接受进入电梯的请求;
4)乘客上下电梯时先出后进。进电梯时乘客是按发出乘坐请求的顺序依次进入,每次只能进入一人且每个人花费的时间都为25t;
5)电梯在关门期间(电梯离开之前)所在层提出请求的乘客同样允许进入。
要求:
按时序显示系统状态的变化过程,即发生的全部人和电梯的动作序列。
扩展要求:
实现电梯模拟的可视化界面。用动画显示电梯的升降,人进出电梯。设计有下列对象:电梯、人、电梯控制板及其上各种按钮、定时器等。
·思路
电梯运行的原则,依据单向最大请求原则。
即:在电梯类中设定两个分别记录上和下的最远请求,初始值分别设为(上)0和(下)10。最大请求即为某一方向上运动的最远楼层,例如:如果是向上,则最大请求最大为9,如 果是向下,则最大请求最远为1
注:此记录量不能合并为一个,即不能只记录当前运行方向的最远层数(最大请求)。因为会出现这种情况:当电梯正在向下,而你只记录了当前的运动方向(下)的最大请求,如 果该值为3,当你运动到楼之后,如果没有记录反向(即向上)的最大请求,那么此时就要遍历3楼以上的所有楼层的请求情况,以确定是否向上,此时就需要遍历!!(而我们 应该尽量避免遍历),如果那样,可想而知,我们要遍历多少次!
将这个大前提说明完之后,我们来规定几个原则
1)最大请求包括外面乘客搭乘需求以及电梯里面的人的出门请求
2)先完成当前方向的运送任务再反过来完成反方向的任务
3)进门的乘客一定是他的需求方向与电梯当前运动方向一致的,最大请求层的乘客情况除外(因为走到最远处了,无论乘客去哪里都应该接,无论其想上楼还是想下楼)
电梯能够顺利运行所依照的机制为状态转换。
即:电梯的运行逻辑全都在各个状态中,通过状态的切换而使电梯能够正常运行。
这样的话,就把电梯的逻辑分成几个部分去单独思考,出错也很容易找到位置,并进行局部的整改。
即:每一个状态下应该完成什么样的操作,例如:closing状态,依据题意,电梯正在关门的时候是可以允许有人请求进入的,所以,该状态首先要判断是否要有人进入,也就是是否 要重新开门。如果没有,到适合的时间顺利转换为closed状态,然后接下来的动作就交给closed状态模块进行处理了。
其次就是一个状态转换时间点的问题
总的思路就是记录下一个状态的触发时间,从而合理地控制状态转换时间点
即:在进入某一个状态的时候,通过给出的每个状态的延续时间,计算出下一个状态的触发时间点(开始时间),当总的时间点打到此触发时间点,那么就进行相应的状态转换。
这是一个总的思路,当然在写的时候要根据实际情况去合理地控制触发时间点,比如说closing状态,当你转换到此状态的时候,就应该算出下一个状态(closed)的触发时间点(第一次进入closing的时间点+closing的延续时间长度),但是有一个问题,就是closing状态下是可以允许乘客请求进入的,所以当这种情况发生时,目标状态(下一个状态)和时间点都要做改变,以保证程序逻辑的正确性。
整个程序,电梯运行过程只有当有人放弃时,才会整个数据进行遍历,重新刷新请求,除此之外,都不会进行遍历的操作。
·程序优化
1)存储结构:
运用类似开散列表的结构,有时候也叫纵向链表。就是一个二维的存储结构,高维是一个数组,低维是一个链表。
即 LinkNode* table[10];
用此结构来存储所有的等待乘客,即 9层楼,每层楼都对应一个链表存储其乘客信息。
而针对电梯里面的乘客的出门信息,则是用一个一维数组来统计。我们可以想一下,只要乘客进入电梯内,那么只有他的目标层这一个数据是有用信息,其余的所有的信息均为无用信息,所以,针对电梯内部的乘客,并不需要存储他的完整信息,即只需要知道他去哪一层即可,反过来再想一下,电梯到达某一层,要把乘客送出去,即有几个人就出去几个即可。所以,我们只需要存9个数,即每个楼对应的出门人数即可。到此,针对电梯内部信息的优化,我觉得已经差不多了
尽量避免了不必要的遍历,在时间上进行了优化,尽量减少不必要的信息存储,在内存优化上达到了不错的效果
2)语法:
应用了Qt有关C++11语法中的常量表达式进行优化程序,以及内联等的一些语法技巧。
·程序代码
关于每个文件的作用,在注释中做了说明
objectors.h
#ifndef OBJECTORS_H #define OBJECTORS_H /************************************************************************ //! [ objectors 头文件 ] //! [ 定义了一些 最基础的 枚举类型 以及 自定义类型 ] //! [ 枚举类型包括: 电梯的状态常量 时间控制常量 ] //! [ 最基础的自定义类型 电梯类 乘客类 ] //! [ 基础的类中的成员函数绝大部分为内联函数 为了各方面的方便考虑 统一将声明写在类内 ] //! [ 将其实现以内联形式写在本文件的类外 ] ************************************************************************/ #include<QtCore/qmargins.h> //! [枚举类型声明] enum Ele_Statue { Waiting , Opening , Opened , Closing , Closed , Moving , Decelerate //! [减速] , Short_Wait //! [暂时停留300t] }; enum Time_List { T_v_Udece = 14 //! [上升时减速] , T_v_up = 15 , T_close_open = 20 , T_v_Ddece = 23 //! [下降时减速] , T_in_out = 25 , T_test = 40 //! [测试有无人进出的时间] , T_ele_up = 51 , T_ele_down = 61 , T_wait = 300 }; //! [ 电梯类 ] class _elevator_ { private: Ele_Statue m_statue; //! [电梯的状态] bool m_curDire; //! [当前方向,上为真] short m_curfloor, in_number; //! [当前层,电梯内人数] Q_DECL_CONSTEXPR short m_call[2]{10, 0}; //! [下、上的最大请求] Q_DECL_CONSTEXPR short m_out[10]{0, }; //! [出门人数记录] public: Q_DECL_CONSTEXPR explicit _elevator_(const Ele_Statue& statue = Waiting) :m_statue(statue), m_curDire(true), m_curfloor(1), in_number(0) { } Q_DECL_CONSTEXPR static constexpr short MAX_people{ 13 }, floor_num{ 9 }; Q_DECL_CONSTEXPR inline const Ele_Statue& get_statue()const; //! [获取状态] Q_DECL_CONSTEXPR inline short& get_call(const bool); //! [获取最大请求] Q_DECL_CONSTEXPR inline const short get_curfloor()const; //! [获取当前层] Q_DECL_CONSTEXPR inline const short getnum_in()const; //! [获取人数] Q_DECL_CONSTEXPR inline short& get_out(const short); //! [设置某层出门人数] const short get_Dire(const bool = true); //! [获取下一步的方向] void num_change(bool); //! [电梯内人数变化] void set_statue(const Ele_Statue&); //! [设定状态] void update_call(const int); //! [更新最大请求] void set_curfloor(const short); }; //! [ 乘客类 ] class _people_ { private: int m_wait; //! [等待时间] int m_appear; //! [出现的时间点] short m_from, m_to; //! [进出楼层] public: Q_DECL_CONSTEXPR explicit _people_(const int t = 0, const int wait = 500) :m_wait(wait) ,m_appear(t) ,m_from(0) ,m_to(0) { } Q_DECL_CONSTEXPR inline const short get_wait()const; Q_DECL_CONSTEXPR inline const short get_from()const; Q_DECL_CONSTEXPR inline const short get_to()const; Q_DECL_CONSTEXPR inline const int get_appear()const; Q_DECL_CONSTEXPR inline const bool IsGiveUp(const int)const; void set_wait(const int); void set_from(const short); void set_to(const short); void set_time(const int); }; //! [ 电梯类的内联函数实现 ] Q_DECL_CONSTEXPR inline const short _elevator_::getnum_in() const { return in_number; } Q_DECL_CONSTEXPR inline short &_elevator_::get_out(const short floor) { return m_out[floor]; } inline void _elevator_::num_change(bool add) { add ? in_number++ : in_number--; } Q_DECL_CONSTEXPR inline const Ele_Statue& _elevator_::get_statue()const { return m_statue; } Q_DECL_CONSTEXPR inline short &_elevator_::get_call(const bool dir) { return m_call[dir]; } Q_DECL_CONSTEXPR inline const short _elevator_::get_curfloor() const { return m_curfloor; } inline void _elevator_::set_statue(const Ele_Statue& s) { m_statue = s; } inline void _elevator_::update_call(const int call) { if(call > m_call[true] && call > m_curfloor)m_call[true] = call; if(call < m_call[false] && call < m_curfloor)m_call[false] = call; } inline void _elevator_::set_curfloor(const short f) { m_curfloor = f; } inline const short _elevator_::get_Dire(const bool change) { if(m_call[1] == 0 && m_call[0] == 10) //! [如果当前在一层,且没有上请求,则返回-1] return -1; //! [如果正在上升,最大上请求不大于当前层,反向;如果正在下降,存在向上请求,反向] if(change) if(m_curDire ? m_call[1] <= m_curfloor : (m_call[1] && m_call[0] == 10)) m_curDire = !m_curDire; return m_curDire; } //! [ 乘客类的内联函数实现 ] Q_DECL_CONSTEXPR inline const short _people_::get_wait() const { return m_wait; } Q_DECL_CONSTEXPR inline const short _people_::get_from() const { return m_from; } Q_DECL_CONSTEXPR inline const short _people_::get_to() const { return m_to; } Q_DECL_CONSTEXPR inline const int _people_::get_appear() const { return m_appear; } Q_DECL_CONSTEXPR inline const bool _people_::IsGiveUp(const int t) const { return m_appear + m_wait <= t; } inline void _people_::set_wait(const int w) { m_wait = w; } inline void _people_::set_from(const short f) { m_from = f; } inline void _people_::set_to(const short t) { m_to = t; } inline void _people_::set_time(const int t) { m_appear = t; } #endif // OBJECTORS_H