电梯调度问题,第一次博客作业 (1)

电梯调度问题

——第一次blog作业

  1. 前言

在经过几个月的java学习后,终于迎来了自己第一个迭代性质并且和实际相结合的大作业。确实难度不低,不过在其中还是有很多地方值得一说的,例如这次代码最主要的look算法。

我认为,这三次题目集的题量不大,知识点的话也就第一次的代码构建知识点多,难度很大,其他两次迭代的题目集在发布当日就可以AC。

(当然因为第一次的题目集我测试了至少150遍)

(顺带一提,这个blog第一次没保存,这是重制版)

  1. 设计与分析

首先我们要知道这次电梯调度的核心是基于look算法完成的,look算法是磁盘经典调度的五个调度之一。而其他四个分别是 FCFS, SSTF, SCAN, C-SCAN。

首先我们来看一下FCFS 调度(先来先服务),这是最简单的磁盘调度算法,按照请求到达的顺序依次处理,公平但效率低下。当然如今已被淘汰。其次是SSTF 调度(最短寻道时间优先), 每次选择与当前磁头位置距离最近的请求,以最小化寻道时间。这个设计思路已经比FCFS好多了,处理效率也比它高了不少,但这个调度有一个致命的问题,就是会出现饥饿现象:例如一个较远的请求和许多较近的请求同时出现时,这个算法会优先处理近处的,那这个较远的请求就会一直挂起,从而导致卡死,这很明显不是我们想要的,所以也被淘汰了。第三个是SCAN 调度(电梯算法),SCAN 算法将磁臂从一端移动至另一端,在沿途处理所有请求,然后反向继续处理,如电梯般来回移动。这个和本题的算法已经比较接近了,同样的现在还有一些电梯采用的这个算法。不过只有磁头方向反转后才会处理另一侧的请求,可能增加等待时间。第四个是C-SCAN 调度(循环扫描), C-SCAN(Circular SCAN)与 SCAN 类似,但在到达一端后不反向,而是直接返回起始端再开始新一轮扫描。(当然你电梯不可能瞬移到第一层)。最后就是LOOK 调度(按需扫描),与 SCAN/C-SCAN 不同,LOOK 调度不会将磁头移动到磁盘两端,而是仅移动至当前方向上最远的请求位置再折返。它避免了不必要的极端移动,进一步提高了磁头效率和调度性能。

我们这道电梯所采用的是look算法的简化版本,就是对两个请求队列的队头进行对比。注意是队头,而不是全部队列

例如:

内请求

2

7

8

6

2

4

外请求

5上

4下

8上

4下

7下

2上

首先会对内请求和外请求的队头“2”和“5上”对比,假如选择了“2”,那么就会比“2”这个内请求删除,从而内请求的队头为“2”,外部请求同理。我那150次测试中至少140次考虑了整个队列。这就是许多同学不能AC的原因。

我这次只贴出第三次迭代的数据和UML类图

其实不难看出,我的代码还没有符合MVC架构要求,至少我的电梯调度核心(getNextfloor)和move_printf都在Controller类,还可以再进行改进(如果还有下一次迭代作业的话)

而我的代码中的核心调度函数中也存在大量if_else结构,这也是一个改进点,我之前试过用相对距离算出一个权值进行比较做出选择,这样可以直接通过一个比较选出下一个目的地。

接下来分析我的类设计

  1. Direction(方向)
    • 属性:
      • sign:方向标志,可以是1(上)、-1(下)或0(停止)。
    • 方法:
      • getSign():获取当前方向。
      • setSign(int sign):设置当前方向。
    • 构造函数:
      • Direction(int sign):根据传入的参数设置方向。
  2. Elevator(电梯)
    • 属性:
      • floor_now:当前楼层。
      • floor_min:最小楼层。
      • floor_max:最大楼层。
      • direction:电梯的当前方向。
    • 方法:
      • getDirection():获取电梯的当前方向。
      • setDirection(Direction direction1):设置电梯的当前方向。
      • getfloorNow():获取当前楼层。
      • setfloorNow(int floor_now):设置当前楼层。
      • getFloor_max():获取最大楼层。
      • setFloor_max(int floor_max):设置最大楼层。
      • getFloor_min():获取最小楼层。
      • setFloor_min(int floor_min):设置最小楼层。
      • movefloor():根据当前方向移动电梯。
    • 构造函数:
      • Elevator():默认构造函数。
      • Elevator(int floor_max, int floor_min, int sign):根据传入的参数初始化电梯。
  3. Passenger(乘客)
    • 属性:
      • floor_now:当前楼层。
      • floor_destination:目的地楼层。
    • 方法:
      • getFloor_now():获取当前楼层。
      • setFloor_now(int floor_now):设置当前楼层。
      • getFloor_destination():获取目的地楼层。
      • setFloor_destination(int floor_destination):设置目的地楼层。
      • getDirection():根据当前楼层和目的地楼层计算乘客的方向。
  4. RequestList(请求列表)
    • 属性:
      • list_in:进入请求列表。
      • list_out:离开请求列表。
    • 方法:
      • getList_in():获取进入请求列表。
      • getList_out():获取离开请求列表。
      • setList_in(ArrayList<Passenger> list_in):设置进入请求列表。
      • setList_out(ArrayList<Passenger> list_out):设置离开请求列表。
    • 构造函数:
      • RequestList():默认构造函数。
      • RequestList(ArrayList<Passenger> list_in, ArrayList<Passenger> list_out):根据传入的请求列表初始化。
  5. Controller(控制器)
    • 属性:
      • dianti:电梯对象。
      • requestlist:请求列表对象。
    • 方法:
      • getElevator():获取电梯对象。
      • getRequestlist():获取请求列表对象。
      • setElevator(Elevator dianti):设置电梯对象。
      • setRequestlist(Requestlist requestlist):设置请求列表对象。
      • open_printf():打印电梯门打开信息。
      • move_printf():打印电梯移动信息。
      • move(int floor_next):移动电梯到指定楼层。
      • getNextfloor():获取下一个目标楼层。
    • 构造函数:
      • Controller():默认构造函数。
      • Controller(Elevator dianti, Requestlist requestlist):根据传入的电梯和请求列表初始化。
  6. Main(主类)
    • 方法:
      • main(String[] args):程序入口,启动电梯系统。

类之间的关系

  • Elevator 类与 Direction 类有关联关系,用于设置电梯的方向。
  • Controller 类与 Elevator 类和 RequestList 类有关联关系,控制器管理电梯的操作和请求列表。
  • RequestList 类与 Passenger 类有聚合关系,包含乘客请求。
  • Main 类是程序的入口,通过控制器启动电梯系统。
  1. 踩坑心得

在我那150多次的尝试中,我有一个我认为很完美的代码

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

028

029

030

031

032

033

034

035

036

037

038

039

040

041

042

043

044

045

046

047

048

049

050

051

052

053

054

055

056

057

058

059

060

061

062

063

064

065

066

067

068

069

070

071

072

073

074

075

076

077

078

079

080

081

082

083

084

085

086

087

088

089

090

091

092

093

094

095

096

097

098

099

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

import java.util.Scanner;

import java.util.regex.Pattern;

import java.util.regex.Matcher;

import java.util.ArrayList;

 

public class Main {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        int floor_min = sc.nextInt();

        int floor_max = sc.nextInt();

        ArrayList<Integer> list_in = new ArrayList<>();

        ArrayList<Integer> list_out = new ArrayList<>();

        ArrayList<Integer> list2 = new ArrayList<>();

        Node dianti = new Node(floor_max, floor_min, 1);

        String line = sc.nextLine();

        while (line.trim().isEmpty())

            line = sc.nextLine();

        Pattern patterns = Pattern.compile("<(-?[1-9][0-9]*)>");

        Pattern patterny = Pattern.compile("<(-?[1-9][0-9]*),(UP|DOWN)>");

        while (!line.equalsIgnoreCase("end")) {

            int number_int = 0;

            String number = "";

            Matcher matcher = patterns.matcher(line);

            Matcher matcher2 = patterny.matcher(line);

            int sign = 0;

            if (matcher.find()) {

                number = matcher.group(1);

                number_int = Integer.parseInt(number);

                if (number_int >= dianti.getFloor_min() && number_int <= dianti.getFloor_max()) {

                    list_in.add(number_int);

                }

            } else if (matcher2.find()) {

                number = matcher2.group(1);

                if (matcher2.group(2).equalsIgnoreCase("UP"))

                    sign = 1;

                else

                    sign = -1;

                number_int = Integer.parseInt(number);

                if (number_int >= dianti.getFloor_min() && number_int <= dianti.getFloor_max()) {

                    list_out.add(number_int);

                    list2.add(sign);

                }

            }

            line = sc.nextLine();

            while (line.trim().isEmpty())

                line = sc.nextLine();

        }

        int i = 0;

        int j = 0;

 

        if(list_out.get(0) > list_in.get(0))

        {

            dianti.move(list_in.get(0));

            i++;

        }

        else{

            dianti.move(list_out.get(0));

            dianti.setsign(list2.get(0));

            j++;

        }

        while (j < list_out.size() && i < list_in.size()) {

            int x = list2.get(j) *(list_out.get(j)-dianti.getfloorNow());

            int y = dianti.getSign() *(list_in.get(i)-dianti.getfloorNow());

            if(x>0 &&y>0)

            {

                if(x>y)

                {

                    dianti.move(list_in.get(i));

                    i++;

                }

                else{

                    dianti.move(list_out.get(j));

                    dianti.setsign(list2.get(j));

                    j++;

                }

            }

            else if(x>=0){

                dianti.move(list_out.get(j));

                    dianti.setsign(list2.get(j));

                    j++;

            }

            else if(y>=0)

            {

                dianti.move(list_in.get(i));

                    i++;

            }

            else if(x<y)

            {

                dianti.move(list_in.get(i));

                    i++;

            }

            else{

                dianti.move(list_out.get(j));

                    dianti.setsign(list2.get(j));

                    j++;

            }

             

        }

        if (j == list_out.size()) {

            for (; i < list_in.size(); i++)

                dianti.move(list_in.get(i));

        } else if (i == list_in.size()) {

            for (; j < list_out.size(); j++){

                dianti.move(list_out.get(j));

            }

        }

        return;

    }

}

 

class Node {

    private int floor_now;

    private final int floor_min;

    private final int floor_max;

    private int sign;

    private boolean just_one = false;

 

    public Node() {

        this.floor_now = 1;

        this.floor_min = 1;

        this.floor_max = 1;

        this.sign = 0;

    }

 

    public Node(int floor_max, int floor_min, int sign) {

        this.floor_now = 1;

        this.floor_min = floor_min;

        this.floor_max = floor_max;

        this.sign = sign;

    }

 

    public void setsign(int sign) {

        this.sign = sign;

    }

 

    public int getfloorNow() {

        return this.floor_now;

    }

 

    public int getFloor_max() {

        return this.floor_max;

    }

 

    public int getFloor_min() {

        return this.floor_min;

    }

 

    public int getSign() {

        return this.sign;

    }

 

    public void open_printf() {

        System.out.println("Open Door # Floor " + this.floor_now);

        System.out.println("Close Door");

    }

 

    public void move_printf() {

        System.out.println("Current Floor: " + this.floor_now + " Direction: " + (this.sign == 1 ? "UP" : "DOWN"));

    }

 

    public void move(int floor_next) {

        boolean move = false;

        if (floor_next > floor_now)

            this.sign = 1;

        else if (floor_next < floor_now)

            this.sign = -1;

        if (!just_one) {

            just_one = true;

            this.move_printf();

        }

        while (this.floor_now != floor_next) {

            move = true;

            this.floor_now += this.sign;

            if (this.floor_now == 0)

                this.floor_now += this.sign;

            this.move_printf();

        }

        if (move) this.open_printf();

    }

}

(这个代码块导致我丢失了第一版的内容,看着就痛)

这段代码是第一次题目集的代码,当时没有类设计,只是在试核心思路,所以没有符合SVC和MVC。(别在意,当时能做对就行了)而我的思路其实就是计算权值,然后进行权值的对比选出下一个目的地,当然bug也不少,例如在计算相对距离时候外层请求和电梯顺序不同时应该确保为负数,但和之前的算法重了,所以会出BUG。

而我前面的问题在于匹配和格式输入的内容,这一块我用了两个库Pattern和Matcher。我设计了两个正则<(-?[1-9][0-9]*)>和<(-?[1-9][0-9]*),(UP|DOWN)>,这两个分别对应了内部请求和外部请求,通过正则我可以准确的识别输入内容并且排除错误格式的内容。而这个输入方式我一直延用至今。

综上而言,我踩过最大的坑就是对全部队列进行对比而不是进行队头的对比,这一跨越我跨了一周

  1. 改进建议

在我迭代三代的最终代码中,我认为还有几个遗憾。

一:输出的一层移动状态,这个我是单独拎出来输出的,而不是存放在move_printf里面,如果带上这个第一层,那么后续的输出都会出问题,于是我把他单拎出来,并且move_printf不会输出当层的状态来解决这个问题。

二:改进为MVC结构,我的代码中main类是输入块,而处理和输出都在Controller里面,所以下次可以写一个子类输出。

三:在我第三次迭代中电梯的调度核心还是一堆if else,而我觉得我那个权值比较的方法更好一点。

四:没有单独调试的入口和观察者模式,不过这个貌似有点遥远了,先实现前面三个会更好点

  1. 总结

综上所述,这次的迭代作业我完成的还算相当不错的,虽然第一次的完成历程比较艰辛,但还是顺利AC了,而后面的迭代中是当日就AC了,可以说完成的比较不错了。

这次迭代作业让我学到了很多东西,不仅是ArrayList和正则,还有在面对实际问题时设计的思路,构建过程和实际的考虑。

接下来附上核心调度思路代码

 

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

public int getNextfloor(){

        if(this.requestlist.getList_out().isEmpty() && this.requestlist.getList_in().isEmpty())

            return 0;

        if(this.requestlist.getList_out().isEmpty())

        {

            int sign = this.requestlist.getList_in().get(0).getFloor_destination();

            this.requestlist.getList_in().remove(0);

            return sign;

        }

        else if(this.requestlist.getList_in().isEmpty())

        {

            int sign = this.requestlist.getList_out().get(0).getFloor_now();

            this.requestlist.getList_in().add(new Passenger(0,this.requestlist.getList_out().get(0).getFloor_destination()));

            this.requestlist.getList_out().remove(0);

            return sign;

        }

        int floor_now = this.dianti.getfloorNow();

        int floor_in = this.requestlist.getList_in().get(0).getFloor_destination();

        int floor_out = this.requestlist.getList_out().get(0).getFloor_now();

        int floor_sign = this.requestlist.getList_out().get(0).getDirection().getSign();

        while(true) {

            if (this.dianti.getDirection().getSign() > 0) {

                if (floor_in >= floor_now && floor_in < floor_out) {

                    this.requestlist.getList_in().remove(0);

                    return floor_in;

                } else if (floor_in > floor_out && floor_out >= floor_now && floor_sign > 0) {

                    this.requestlist.getList_in().add(new Passenger(0, this.requestlist.getList_out().get(0).getFloor_destination()));

                    this.requestlist.getList_out().remove(0);

                    return floor_out;

                }

                else if(floor_in  >= floor_now )

                {

                    this.requestlist.getList_in().remove(0);

                    return floor_in;

                }else if(floor_out >= floor_now )

                {

                    this.requestlist.getList_in().add(new Passenger(0, this.requestlist.getList_out().get(0).getFloor_destination()));

                    this.dianti.setDirection(this.requestlist.getList_out().get(0).getDirection());

                    this.requestlist.getList_out().remove(0);

                    return floor_out;

                }

                else{

                    this.dianti.getDirection().setSign(-1);

                }

            }else{

                if(floor_in<=floor_now && floor_in>floor_out)

                {

                    this.requestlist.getList_in().remove(0);

                    return floor_in;

                }

                else if(floor_out <= floor_now && floor_out > floor_in && floor_sign < 0)

                {

                    this.requestlist.getList_in().add(new Passenger(0, this.requestlist.getList_out().get(0).getFloor_destination()));

                    this.requestlist.getList_out().remove(0);

                    return floor_out;

                }

                else if(floor_in <= floor_now )

                {

                    this.requestlist.getList_in().remove(0);

                    return floor_in;

                }

                else if(floor_out <= floor_now )

                {

                    this.requestlist.getList_in().add(new Passenger(0, this.requestlist.getList_out().get(0).getFloor_destination()));

                    this.dianti.setDirection(this.requestlist.getList_out().get(0).getDirection());

                    this.requestlist.getList_out().remove(0);

                    return floor_out;

                }

                else{

                    this.dianti.getDirection().setSign(1);

                }

            }

        }

posted @ 2025-04-20 23:46  Geonstar  阅读(35)  评论(0)    收藏  举报