如何在时间折叠中获得连续的课程对(基于其时间段)?

问题描述 投票:0回答:1

基于Timefold提供的GitHub项目(https://github.com/TimefoldAI/timefold-quickstarts/tree/stable/technology/java-spring-boot)我想为可能发生的以下场景实现一个约束在大学:

假设对于特定的一天(星期一),我们有 7 个时间段(上午 8 点 - 上午 10 点、上午 10 点 - 上午 12 点、...、晚上 8 点到晚上 10 点)和 4 个不同的课程要在当天分配。我们希望确保特定日期的日程安排中没有任何间隙(例如超过 2 小时)。

我目前的尝试如下:

Constraint tooMuchGap(ConstraintFactory constraintFactory){
        // 4 hours gaps between lessons for students in the same day
        //todo solve this problem-> we receive pairs that are not consecutives
        return constraintFactory
                //select each 2 pair of different lessons
                .forEach(Lesson.class)
                .join(Lesson.class,
                        //with the same student group
                        Joiners.equal(Lesson::getStudentGroup),
                        //in the same day
                        Joiners.equal((lesson) -> lesson.getTimeslot().getDayOfWeek()),
                        Joiners.lessThan((lesson -> lesson.getTimeslot().getId())))
                .filter((lesson1, lesson2) -> {
                    
                    Duration between = Duration.between(lesson1.getTimeslot().getEndTime(),
                            lesson2.getTimeslot().getStartTime());
                    return !between.isNegative() && between.compareTo(Duration.ofHours(3)) > 0;
                })
                .penalize(HardSoftScore.ONE_SOFT)
                //.justifyWith()
                .asConstraint("Too much gap between the courses");
    }

对于这个测试用例:

@Test
    void tooMuchGap(){
        StudentGroup studentGroup = new StudentGroup(1L,"Group1");
        Lesson firstTuesdayLesson = new Lesson(1, "subject1", new Teacher(1L, "Teacher2"), studentGroup, TIMESLOT2, ROOM1);
        Lesson secondTuesdayLesson = new Lesson(2, "subject2", new Teacher(2L, "Teacher1"), studentGroup, TIMESLOT3, ROOM1);
        Lesson thirdTuesdayLesson = new Lesson(3, "subject3", new Teacher(3L, "Teacher3"), studentGroup, TIMESLOT6, ROOM1);
        Lesson fourthTuesdayLesson = new Lesson(4, "subject4", new Teacher(4L, "Teacher3"), studentGroup, TIMESLOT7, ROOM1);
        constraintVerifier.verifyThat(TimetableConstraintProvider::tooMuchGap)
                .given(firstTuesdayLesson, secondTuesdayLesson, thirdTuesdayLesson, fourthTuesdayLesson)
                .penalizesBy(1);
    }

我收到以下输出:

2024-03-13 22:45:12.697  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson1's timeslot: Timeslot(id=2, dayOfWeek=TUESDAY, startTime=12:00, endTime=14:00)
2024-03-13 22:45:12.707  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson2's timeslot: Timeslot(id=3, dayOfWeek=TUESDAY, startTime=14:30, endTime=16:30)
2024-03-13 22:45:12.707  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - ----------------------------------
2024-03-13 22:45:12.707  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson1's timeslot: Timeslot(id=2, dayOfWeek=TUESDAY, startTime=12:00, endTime=14:00)
2024-03-13 22:45:12.707  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson2's timeslot: Timeslot(id=6, dayOfWeek=TUESDAY, startTime=19:00, endTime=21:00)
2024-03-13 22:45:12.707  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - ----------------------------------
2024-03-13 22:45:12.709  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson1's timeslot: Timeslot(id=3, dayOfWeek=TUESDAY, startTime=14:30, endTime=16:30)
2024-03-13 22:45:12.709  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson2's timeslot: Timeslot(id=6, dayOfWeek=TUESDAY, startTime=19:00, endTime=21:00)
2024-03-13 22:45:12.709  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - ----------------------------------
2024-03-13 22:45:12.709  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson1's timeslot: Timeslot(id=2, dayOfWeek=TUESDAY, startTime=12:00, endTime=14:00)
2024-03-13 22:45:12.709  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson2's timeslot: Timeslot(id=7, dayOfWeek=TUESDAY, startTime=21:00, endTime=23:00)
2024-03-13 22:45:12.709  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - ----------------------------------
2024-03-13 22:45:12.710  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson1's timeslot: Timeslot(id=3, dayOfWeek=TUESDAY, startTime=14:30, endTime=16:30)
2024-03-13 22:45:12.710  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson2's timeslot: Timeslot(id=7, dayOfWeek=TUESDAY, startTime=21:00, endTime=23:00)
2024-03-13 22:45:12.710  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - ----------------------------------
2024-03-13 22:45:12.710  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson1's timeslot: Timeslot(id=6, dayOfWeek=TUESDAY, startTime=19:00, endTime=21:00)
2024-03-13 22:45:12.710  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - The lesson2's timeslot: Timeslot(id=7, dayOfWeek=TUESDAY, startTime=21:00, endTime=23:00)
2024-03-13 22:45:12.710  INFO [main           ] - c.t.t.s.TimetableConstraintProvider - ----------------------------------

java.lang.AssertionError: Broken expectation.
        Constraint: com.timetablealgo.testingtimetablealgo.solver/Too much gap between the courses
  Expected penalty: 1 (class java.lang.Integer)
    Actual penalty: 3 (class java.lang.Integer)

  Explanation of score (0hard/-3soft):
    Constraint matches:
        -3soft: constraint (Too much gap between the courses) has 3 matches:
            -1soft: justified with ([Lesson(id=2, subject=subject2, teacher=Teacher(id=2, name=Teacher2, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=2, dayOfWeek=TUESDAY, startTime=12:00, endTime=14:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null)), Lesson(id=5, subject=subject3, teacher=Teacher(id=3, name=Teacher3, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=6, dayOfWeek=TUESDAY, startTime=19:00, endTime=21:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null))])
            -1soft: justified with ([Lesson(id=2, subject=subject2, teacher=Teacher(id=2, name=Teacher2, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=2, dayOfWeek=TUESDAY, startTime=12:00, endTime=14:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null)), Lesson(id=6, subject=subject4, teacher=Teacher(id=3, name=Teacher3, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=7, dayOfWeek=TUESDAY, startTime=21:00, endTime=23:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null))])
            ...
    Indictments:
        -2soft: indicted with (Lesson(id=2, subject=subject2, teacher=Teacher(id=2, name=Teacher2, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=2, dayOfWeek=TUESDAY, startTime=12:00, endTime=14:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null))) has 2 matches:
            -1soft: constraint (Too much gap between the courses)
            -1soft: constraint (Too much gap between the courses)
        -2soft: indicted with (Lesson(id=6, subject=subject4, teacher=Teacher(id=3, name=Teacher3, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=7, dayOfWeek=TUESDAY, startTime=21:00, endTime=23:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null))) has 2 matches:
            -1soft: constraint (Too much gap between the courses)
            -1soft: constraint (Too much gap between the courses)
        -1soft: indicted with (Lesson(id=5, subject=subject3, teacher=Teacher(id=3, name=Teacher3, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=6, dayOfWeek=TUESDAY, startTime=19:00, endTime=21:00), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null))) has 1 matches:
            -1soft: constraint (Too much gap between the courses)
        -1soft: indicted with (Lesson(id=3, subject=subject1, teacher=Teacher(id=1, name=Teacher1, preferredTimeslot=null), studentGroup=StudentGroup(id=1, year=null, name=Group1, group=null, semiGroup=null, numberOfStudents=30), type=null, year=null, timeslot=Timeslot(id=3, dayOfWeek=TUESDAY, startTime=14:30, endTime=16:30), room=Room(id=1, name=Room1, capacity=0, type=null, building=null, dotari=null))) has 1 matches:
            -1soft: constraint (Too much gap between the courses)

我的问题是如何获得连续对(基于他们的时间段)进行评估?对于这种情况,我只想获取以下对:(Timeslot2 和 Timeslot3)、(Timeslot3 和 Timeslot6)、(Timeslot6 和 Timeslot7)。

java spring-boot constraints optaplanner timefold
1个回答
0
投票

由于

consecutive
的定义是“同一天分配给同一学生组的两个时间段,中间没有另一个时间段”,我会使用
ifNotExists
:

Constraint tooMuchGap(ConstraintFactory constraintFactory){
        // 4 hours gaps between lessons for students in the same day
        return constraintFactory
                //select each 2 pair of different lessons
                .forEach(Lesson.class)
                .join(Lesson.class,
                        //with the same student group
                        Joiners.equal(Lesson::getStudentGroup),
                        //in the same day
                        Joiners.equal((lesson) -> lesson.getTimeslot().getDayOfWeek()),
                        // First starts before second
                        Joiners.lessThan((lesson) -> lesson.getTimeslot().getStartTime())
                )
                .ifNotExists(Lesson.class,
                        //with the same student group
                        Joiners.equal((a,b) -> a.getStudentGroup(), Lesson::getStudentGroup),
                        //in the same day
                        Joiners.equal((a,b) -> a.getTimeslot().getDayOfWeek(), (lesson) -> lesson.getTimeslot().getDayOfWeek()),
                        //is between the two timeslots
                        Joiners.lessThan((a,b) -> a.getTimeslot().getStartTime(), (lesson) -> lesson.getTimeslot().getStartTime()),
                        Joiners.greaterThan((a,b) -> b.getTimeslot().getStartTime(), (lesson) -> lesson.getTimeslot().getStartTime())
                )
                .filter((lesson1, lesson2) -> {
                    
                    Duration between = Duration.between(lesson1.getTimeslot().getEndTime(),
                            lesson2.getTimeslot().getStartTime());
                    return !between.isNegative() && between.compareTo(Duration.ofHours(3)) > 0;
                })
                .penalize(HardSoftScore.ONE_SOFT)
                //.justifyWith()
                .asConstraint("Too much gap between the courses");
    }
© www.soinside.com 2019 - 2024. All rights reserved.