谨以此文纪念一周的心血历程
在上一周完成的基础上,大家觉得这电梯是不是有点low啊,现在都智能化了,哪还有这么蠢的电梯啊,所以我们要让电梯的调度智能化,怎么个智能化呢?我们这里也只是简单的考虑以下捎带问题,所谓捎带,就是当我在电梯去往目的地的途中,按下一个可以在途中解决的按钮,那么这次请求就可以被捎带了,具体怎么说呢?主要以下说明:
- (e.sta = UP→10>=e.n>e.e_n) || (e.sta = DOWN1<=e.n<e.e_n)1 注释:本段所述的可捎带条件可以这样理解:①电梯状态向上运行时,发出向上请求的楼层处于当前楼层和10层之间;②电梯状态向下运行时,发出向下请求的楼层处于当前楼层和1层之间。
- 对于任意一个楼层请求r=(FR, n, dir, t),如果电梯当前是运动状态,则顺路捎带请求一定有: (r.dir=e.sta) && ((r.dir=UP(r.n <= e.n)&&(r.n>e.e_n)) || (r.dir=DOWN(r.n>=e.n)&&(r.n<e.e_n))) 注释:本段所述的可捎带条件可以这样理解:电梯状态向上或向下运行时,新楼层请求的运动方向与当前方向一致,而且新请求发生的楼层在当前所处楼层和目标楼层之间。
- 对于任意一个电梯内运行请求r=(ER,n,t),如果是电梯当前运动状态下的顺路捎带请求,则一定有: (e.sta=UP(10=>r.n > e.e_n)) || (e.sta=DOWN (1<=r.n<e.e_n)) 注释:本段所述的可捎带状态可以这样理解:电梯内请求的目标楼层在电梯的前进方向上
- 对于e.sta = STILL状态分为2种情况。一是在电梯处于停留状态(WFS状态),此种情况没有“顺路捎带”请求,因为此时请求队列为空;(注意STILL状态仅用于输出表达,输入只有UP/DOWN两个状态)
- 可捎带请求的条件是,该请求时非同质请求
当时我看的时一脸懵逼啊,可谓是一点思路都没有。现在才发现,当老师也不容易啊,写完了讲不清…………
待我慢慢道来,我们分为以下几步;
如何解决捎带问题
我们可以将捎带分为两类:
- 第一类,在电梯途中的电梯外的请求,该请求必须在电梯的当前楼层与目标楼层之间,且方向与电梯运动方向相同
- 第二类,电梯内的请求,这一类请求意味着只要请求在电梯当前楼层之外,并且方向与运动方向相同,都可以捎带
那么如何捎带了,相信大家到这时思路都差不多了,构建捎带队列,而捎带队列的构建,小编我是想了整整一晚上啊,硬是没开始写代码。当时构建的方法主要有两种想法
- 按照时间构建捎带队列,将请求队列中符合稍带条件的请求按时间直接装入捎带队列,执行时按楼层执行,这样就不可避免的在执行的时候需要遍历一遍捎带队列,找出最接近的楼层捎带,更多的细节在于捎带请求完成后需要清空这一条捎带请求,需要将捎带队列改变,又会构成麻烦
- 按照楼层直接存储捎带队列,由于捎带有一个特点,那就是捎带会沿着电梯前进方向执行,举例来说,从一楼到十楼的过程中,被捎带的请求一定是从中间楼层,且方向向上的请求,或者电梯内请求。按照这种方式存储电梯也会带来麻烦,那就是同一楼层不止一条请求,且同一楼层的请求要按照输入的顺序输出,也就意味这在楼层中的请求也需要排序,不过相对于第一种,这个时间就不算什么了,所以我们在调度器中构建三维数组表示捎带请求队列
这里给出上行途中电梯内请求的捎带的例子:
if(this.request_n[k][0] > this.station[0] && this.pr_instr[0]>this.station[0] && this.request_n[k][2]==2 && this.request_n[k][4]==0) { for(floor=this.station[0];floor < this.request_n[k][0];floor++) { if(this.on_way[floor][0][0]!=0) { arrive++; } arrive+=0.5; } //计算出从当前楼层到队列中一层的时间 if(this.occupy[(int) this.request_n[k][0]][1]!=0 && (arrive+this.stop_time)>this.request_n[k][1]) //表示该层楼电梯内的按钮已经被占用 { this.request_n[k][4] = 1; System.out.printf("#SAME"); output2(this.request_n[k]); } /*import*/ else if((arrive+this.stop_time) > this.request_n[k][1] && this.request_n[k][4]==0) { //判断时间问题,运行时有没有已经到达这一层 chose = 0; while(this.on_way[(int) this.request_n[k][0]][chose][0]!=0) { //表示按钮还没有被按,可以捎带,不是同质请求 chose++; } if(chose==1){ //如果改楼层有多条请求被捎带,按照输入顺序排好顺序 if((k+1)<this.on_way[(int) this.request_n[k][0]][0][4]) { temp = this.on_way[(int) this.request_n[k][0]][0]; this.on_way[(int) this.request_n[k][0]][0] = this.request_n[k]; this.on_way[(int) this.request_n[k][0]][chose] = temp; } else { this.on_way[(int) this.request_n[k][0]][chose] = this.request_n[k];//表示向上运行的电梯内请求捎带 } } else{ this.on_way[(int) this.request_n[k][0]][chose] = this.request_n[k];//表示向上运行的电梯内请求捎带 } if(this.occupy[(int) this.request_n[k][0]][2]==0 || this.occupy[(int) this.request_n[k][0]][3]==0) { j++; } this.occupy[(int) this.request_n[k][0]][1]=1; //表示电梯内的按钮被按下 this.request_n[k][4] = k+1; //表示该指令放入捎带队列中将被执行 } }
主请求起到关键的作用
主请求作为该次电梯执行的目标楼层,就是运行的最后一层,决定了电梯的运行方向,以及会捎带那些请求,在我们采用第二种方法后,每次执行的时整个请求队列,也就意味着,我们每次都将主请求执行结束,才意味着一次执行的结束,所以每次执行后都需要更新主请求,更新的主请求有两个来源,捎带队列中还未执行的请求,请求队列中还未执行的请求。主请求被确定后该请求就会被加入到捎带队列中,同时改楼层按钮被按下;代码实例如下
if(this.station[1]==2){ //表示电梯之前是向下运行的,所以找出楼层最低的作为主请求 for(num=1;num<=10;) { if(this.on_way[num][0][0]==0) num++; else break; } //判断捎带队列中的哪一个作为下一个主请求 if(num==11) { //num==11.表示捎带队列已经空了,所以将请求队列中的下一个作为主请求,并加入到捎带队列中 while(this.request_n[i][4]!=0) i++; //表示在请求队列中将已经执行的队列过滤掉 this.on_way[0][0] = this.request_n[i]; this.pr_instr = this.request_n[i]; this.on_way[(int) this.request_n[i][0]][0] = this.request_n[i];//表示主请求的哪一个楼层的请求 this.request_n[i][4] = i+1; } else { this.on_way[0][0] = this.on_way[num][0]; //第0个表示主请求 this.pr_instr = this.on_way[num][0]; } }//如果是下行就找到最远的哪一个作为主请求,也就是楼层最小的那个作为朱请求 // ② else if(this.station[1]==1){ //表示电梯之前是向上运行的 for(num=10;num>=1;) { if(this.on_way[num][0][0]==0) num--; else break; } //判断捎带队列中的哪一个作为下一个主请求 if(num==0) { //num==0.表示捎带队列已经空了,所以将请求队列中下一个作为主请求,并加入到捎带队列中 while(this.request_n[i][4]!=0) i++; //表示在请求队列中将已经执行的队列过滤掉 this.on_way[0][0] = this.request_n[i]; this.pr_instr = this.request_n[i]; this.on_way[(int) this.request_n[i][0]][0] = this.request_n[i];//表示主请求的哪一个楼层的请求 this.request_n[i][4] = i+1; } else { this.on_way[0][0] = this.on_way[num][0]; //表示上行的时候将楼层最大的作为主请求 this.pr_instr= this.on_way[num][0]; } } // ③,表示之前电梯是静止的 else { while(this.request_n[i][4]!=0) i++; //表示在请求队列中将已经执行的队列过滤掉 this.on_way[0][0] = this.request_n[i]; this.pr_instr = this.request_n[i]; this.on_way[(int) this.request_n[i][0]][0] = this.request_n[i];//表示主请求的哪一个楼层的请求 this.request_n[i][4] = i+1; } if(this.pr_instr[3]==1) { //表示上行的 this.occupy[(int) this.pr_instr[0]][2]=1; } else if(this.pr_instr[3]==2) { //表示下行 this.occupy[(int) this.pr_instr[0]][3]=1; } else if(this.pr_instr[2]==2) { //表示在电梯内 this.occupy[(int) this.pr_instr[0]][1]=1; } }
判断同质请求会很麻烦
同质请求的判断稍不留神就出错了,小编也是打了好几次补丁才最终完成同质请求的判断,因为同质请求牵涉到的属性有点多;楼层,时间,请求队列,捎带队列,这些都会影响到同质请求的判断,那么到底什么样的请求才算是同质请求呢?以及判断为什么会如此的麻烦呢?
- 同质请求意味着在请求到达目标楼层之前,对于目标楼层发出了多个相同请求,小编在室友的指导下,采用了他的方法,楼层的按钮。同一楼层只有三种按钮,当该楼层的按钮被按下以后,在该楼层的请求完成之前,按钮都处于按下的状态,也就意味着继续按同一按钮是没有意义的,所以这样来判断同质
- 关于同质牵涉到的时间问题是,电梯在运动过程中,途中电梯有捎带请求执行的时候,他需要执行捎带请求,所以时间会向后延长1s,所以在计算同质请求的时候,时间要计算到判断同质请求的那一楼层
见代码:
for(floor=this.station[0];floor < this.request_n[k][0];floor++) { if(this.on_way[floor][0][0]!=0) { arrive++; } arrive+=0.5; } //计算出从当前楼层到队列中一层的时间 if(this.occupy[(int) this.request_n[k][0]][1]!=0 && (arrive+this.stop_time)>this.request_n[k][1]) //表示该层楼电梯内的按钮已经被占用 { this.request_n[k][4] = 1; System.out.printf("#SAME"); output2(this.request_n[k]); //在时间允许内,按钮已经被按下了,所以判断为同质请求 }
如何解决请求时间与执行时间
关于请求时间与执行时间的计算,有几点感觉需要注意,重要的是,时间与请求有关,还与主请求有关。对于当前时间,如果主请求时间晚于当前时间,那么电梯就要停止运行,等待主请求的到来,因为没有主请求,电梯是不会运行的。而电梯运行的途中,对于请求时间,需要计算从此次运行的开始时刻到达该楼层的时间。用来判断是否需要等待,如果需要等待,还需要在计算一次运行的时间,代码如下
if(state_in[0]==move[0]) { if(this.in_way[this.state_in[0]][0][1]>this.st_time) { this.st_time = this.in_way[this.state_in[0]][0][1]; } this.st_time = this.st_time + this.mo_time; } else { this.st_time = this.st_time + this.mo_time; if(this.in_way[this.state_in[0]][0][1]>this.st_time) { this.st_time = this.in_way[this.state_in[0]][0][1]; this.st_time = this.st_time + this.mo_time; } } this.mo_time = 0; //判断是否是主指令
总结
对于本题我的类图如下:
小小收获
对于本课程收获,前三周虽然过的十分十分的辛苦,但是收获还是有的。感觉从来没有de过这么久的bug,没有写过这么久的bug,没有从头速成写代码。这些都是第一次。还有就是在计组课程之上对于设计理解了更多。关于设计思路,方法等,所以一般会在写代码之前写readme。对于整体的设计还是要更加的注重细节的处理,多在细节上花功夫,会使debug的过程简单很多。另一方面,互测虽然不是很“友好”,但是在看别人的代码中,收获了很多,代码的风格,处理问题的方式,等等。