关于目的选层电梯的设计与优化方案

1.电梯采用LOOK算法,这是对SCAN算法的改进。假设现在电梯正在上行,到达某一层停靠后电梯轿厢里没有人并且当前楼层到顶层没有请求,则转向。这样就省去了从当前楼层到顶层,和从顶层回来的这一段折返的空载的路程(或者说载客可能性不大的的路程,因为不能保证转向后没有新的请求到来,但是综合考虑还是转向的运行时间期望更小,受益更大)。

  具体判断方法:

1 if (requests.isEmpty() && canPark()) {
2     if (!dispatcher.checkFurtherRequest(currentFloor, dir)) {
3         if (isOpen) {
4             closeDoor();
5        }
6        changeDir();
7     }
8 }

  其中checkFurtherRequest为:

 1 public synchronized boolean checkFurtherRequest(
 2     int currentFloor, ElevatorDirection dir) {
 3     if (dir == ElevatorDirection.UP) {
 4         for (int i = 0; i < requestQueue.size(); i++) {
 5             if (requestQueue.getFrom(i) > currentFloor) {
 6                 return true;
 7             }
 8         }
 9         return false;
10     } else {
11         for (int i = 0; i < requestQueue.size(); i++) {
12             if (requestQueue.getFrom(i) < currentFloor) {
13                 return true;
14             }
15         }
16         return false;
17     }
18 }

  其他函数根据名字可以大体推知其作用。

2.为了最大限度的不错过可以执行的请求,可以在关门前先遍历一遍请求列表,没有可执行的请求就sleep(200),之后再遍历一遍请求列表,若此时有可执行的请求就获取它并继续sleep(200)重复上述过程,若此时仍然没有可执行的请求就关门。虽然只有0.2s的差别,但是这样就可能让你不错过诸如下列的一系列请求:

  (虽然可能性极低,但实测确实有可能发生)

3.一开始我曾构思过写一个调度器线程,把请求动态分给三个电梯,如果有一个电梯不能完成的请求就动态分配中间停靠楼层,但这样需要线程之间频繁的通信,实现起来太过复杂,而且因为三个电梯的运行时间、限乘人数都不一样,要综合各种因素实现一个稳定的优化算法是极为困难的,且留给我们的时间也不允许。于是我的做法就是化繁为简,以不变应万变。找到了一个低付出高回报的方法。

  a.既然动态分配中间停靠楼层不好实现,就把一个电梯不能完成的所有请求枚举出来,固定他们的中间停靠楼层。我的具体实现如下表:

TO

FROM

-3

-2、-1

1

2

3

4~14

15

16~20

-3

直达

直达

直达

1-换乘

1-换乘

1-换乘

直达

直达

-2、-1

直达

直达

直达

直达

1-换乘

直达

直达

直达

1

直达

直达

直达

直达

直达

直达

直达

直达

2

1-换乘

直达

直达

直达

1-换乘

直达

直达

15-换乘

3

1-换乘

1-换乘

1-换乘

1-换乘

直达

奇数楼层直达

偶数楼层5-换乘

直达

15-换乘

4~14

1-换乘

直达

直达

直达

5-换乘

直达

直达

15-换乘

15

直达

直达

直达

直达

直达

直达

直达

直达

16~20

直达

直达

直达

15-换乘

15-换乘

15-换乘

直达

直达

  不能简简单单直接将请求拆成两部分放在请求列表里,这样可能会有后半段先执行的逻辑错误,我的方法是队请求做出特殊的标记

public class Request {
    private int id;
    private int from;
    private int to;
    private int destiny;
    private boolean in = false;
    private boolean out = false;
    
    public Request(int id, int from, int to) {
        this.id = id;
        this.from = from;
        this.to = to;
        destiny = 0;
    }
    
    public Request(int id, int from, int to, int destiny) {
        this.id = id;
        this.from = from;
        this.to = to;
        this.destiny = destiny;
    }
    //此处省略部分get和set方法
}

  设计了两种构造方法,单电梯可直达的用第一种构造方法,不能执行的用第二种构造方法,其中from不变,to为中转的楼层,destiny为最终的目的楼层。每次执行完一条请求后看看这个请求的destiny是否为0,若为0,该请求已经执行完成,删去;若不为0,则重新构造一个请求:

  request = new Request(formerRequest.get(i).getId(), currentFloor, formerRequest.get(i).getDestiny());

  以当前楼层为from,destiny为to,并立即插入请求列表中等待再一次被执行,这样可以保证逻辑的先后顺序不会出错。

  b.既然动态给三个电梯分配请求太困难,就让三个电梯来请求列表里“抢”请求,但这个“抢”是有约束的。电梯运行到某层后遍历请求列表,取得电梯自己可以执行的请求。什么是电梯自己可以执行的请求呢?就是currentFloor == requestFrom并且方向与电梯运行方向相同的请求。电梯每次到达某一层之后,先下人,然后再遍历请求列表来取得请求。

  这就是我们常说的Worker Thread模式,其类图如下:

 

  可以对比一下Worker Thread模式和普通的方法调用。

  在Worker Thread模式中,Client负责发送工作请求,它将工作内容封装为Request,然后传递给Channel,在普通的方法调用中,这部分相当于“设置参数并调用方法”。其中,“设置参数”与“创建Request”相对应,“调用方法”与“传递给Channel”相对应。

  Worker负责进行工作,它使用从Channel接受到的Request来执行实际的处理。在普通的方法调用中,这部分相当于“执行方法”。

  在进行普通的方法调用时,“调用方法”和“执行方法”是连续进行的。因为调用方法后,方法会立即执行,无法分开。但在Worker Thread模式中,方法调用和方法执行被特意被分开了。

  这种分离有什么意义呢?

  ①提高响应速度

  如果执行和调用不可分离,那么当执行需要花费很长时间时,就会拖调用处理的后腿。但是如果将调用和执行分离,那么即使执行需要花费很长时间也没有关系,因为执行完调用处理的一方可以先继续执行其他处理,这样就可以提高响应速度

  ②控制执行顺序,即调度

  如果调用和执行不可分离,那么在调用之后就必须开始执行。但如果将调用和执行分离,那么执行就可以不再受调用顺序的制约。我们可以通过设置Request的优先级,即控制Channel将Request传递给Worker的顺序来实现控制调用顺序,这就是请求调度(scheduling)。

  ③可以取消和反复执行

  将调用和执行分离后,还可以实现“即使调用了也不执行”和“即使调用了一次也可以反复执行”。

  ④实现分布式

  我们现在只是分开不同的线程来实现调用和执行的分离,但是利用这种思想,我们可以将负责调用的计算机和负责执行的计算机分开,让网络作为Channel来传递Request。

4.没有最好的优化,只有最好的架构,不能为了性能而破坏设计的架构,这样是得不偿失的,性能只是为你的架构锦上添花,不能为了性能而使架构变得不堪入目,本末倒置。对电梯类的建造我采用了抽象工厂模式生成了三个电梯类。其中抽象工厂为:

 1 public interface ElevatorFactory {
 2 
 3     static final int OPEN_TIME = 200;
 4     static final int CLOSE_TIME = 200;
 5     
 6     public abstract void runElevator();
 7     
 8     public abstract void walkIn(Request request);
 9     
10     public abstract void walkOut(Request request);
11     
12     public abstract void openDoor();
13     
14     public abstract void closeDoor();
15     
16     public abstract void moveOneFloor(int sign);
17     
18     public abstract int getSign();
19     
20     public abstract void changeDir();
21     
22     public abstract boolean canPark();
23 }

  我的做法是创建了三个不同的电梯类,分别实现这个接口,这样会造成类的数目过多且三个类之间的相似度特别高。其实可以创建一个电梯类并实现这个接口,然后把三个电梯之间不同的部分通过参数传进来,实现一个类的三个不同对象即可,这一点是需要改进的地方。

  以上几点只是个人抛砖引玉的愚见,一定有不周和不对之处,望读者多多包涵并批评指正。

转载于:https://www.cnblogs.com/hyc2026/p/10764478.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计一个电梯模拟系统。这是一个离散的模拟程序,由随机事件驱动,以模拟时钟决定乘客或电梯的动作发生的时刻和顺序,系统在某个模拟瞬间处理有待完成的各种事情,然后把模拟时钟推进到某个动作预定要发生的下一时刻。 要求: (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、付费专栏及课程。

余额充值