Я хочу, чтобы группы студентов из одной серии студентов находились в одном временном интервале и в одной комнате для типа курса.
Это моя попытка:
Constraint seminarsGroupedInTheSameTimeslot(ConstraintFactory constraintFactory){
return constraintFactory
//select each 2 pair of different lessons
.forEachUniquePair(Lesson.class,
//with the same group
Joiners.equal((lesson) -> lesson.getStudentGroup().getGroup()),
//with the same series
Joiners.equal((lesson) -> lesson.getStudentGroup().getName()),
//with the same subject
Joiners.equal(Lesson::getSubject),
//with the same Seminar type
Joiners.equal((lesson) -> lesson.getType().equals(LessonType.SEMINAR))
)
//check if the lessons have different timeslots and rooms
.filter(((lesson, lesson2) -> {
return !(lesson.getTimeslot().equals(lesson2.getTimeslot()) &&
lesson.getRoom().equals(lesson2.getRoom()));
}))
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("Seminars should be grouped in the same timeslot and room");
}
Я знаю, что это не лучшее решение, поскольку оно не масштабируемо и дает штраф за каждую пару уроков, нарушающих ограничение (т. е. в случае, если анализируются 10 уроков и только один экземпляр уроков нарушает ограничение, оценка будет быть -9 вместо -1).
Я попытался добавить метод ifNotExists(), чтобы учитывать только идентификатор последовательных уроков, но получил следующее предупреждение: Непроверенное создание массива дженериков для параметра varargs.
Как мне лучше написать такое ограничение?
Самое простое, что я мог придумать, было бы примерно так:
forEach(Lesson.class)
.filter(lesson -> lesson.getType() == SEMINAR)
.groupBy(
Lesson::getStudentGroup,
Lesson::getSubject,
countDistinct(lesson -> Pair.of(lesson.getTimeslot(), lesson.getRoom())))
.filter((group, subject, timeslotAndRoomCount) -> timeslotAndRoomCount > 1)
.penalize(ONE_HARD,
(group, subject, timeslotAndRoomCount) -> timeslotAndRoomCount - 1)
...
Ключевая часть, которую нужно понять, это groupBy
— ваши studentGroup
и subject
вместе образуют групповой ключ. Все уроки с одинаковым ключом попадают в одну группу. Внутри этой группы сборщик countDistinct
вычислит, сколько существует различных экземпляров пар timeslot
и room
. (Примечание: вам придется реализовать Pair
самостоятельно. Это может быть просто однострочный Java record
.)
У вас может возникнуть соблазн заменить countDistinct
на toList
, что даст вам доступ к реальным парам. Я бы не стал. Коллекции не являются инкрементными, а инкрементальность является ключом к производительности решателя.
Я добавил реализованную пару (простую запись) в ограничение, протестировал ограничение и заметил значительное улучшение решения Solver. Большое спасибо!