我正在使用 OptaPlanner 开发员工排班系统,我面临着这样一种情况:我需要确保优先级 1 班次在优先级 2 班次之前被填补。轮班由 Shift 类中的优先级字段标识,该类充当规划实体。
这就是我想要实现的目标:
任何有关如何构建此场景的约束配置的帮助、示例代码片段或指导将不胜感激。预先感谢您的协助!
OptaPlanner 版本:8.29.0.Final
我尝试创建这样的约束
Constraint constraint = constraintFactory.forEach(Shift.class)
.groupBy(shift -> shift.getWorksite(), ConstraintCollectors.toList())
.filter((worksite, shiftList) -> filterAlternateShifts(shiftList)).penalizeConfigurable((u, s) -> 20)
.asConstraint(CONSTRAINT_ALTERNATES_SHOULD_NOT_BE_ASSIGNED_BEFORE_SCHEDULED);
过滤方法定义为:
private boolean filterAlternateShifts(List<Shift> shiftList) {
List<Shift> alternates = shiftList.stream().filter(s -> s.getRank().equals(SchedulingConstants.ALTERNATE_SHIFT))
.collect(Collectors.toList());
List<Shift> scheduled = shiftList.stream().filter(s -> s.getRank().equals(SchedulingConstants.SCHEDULED_SHIFT))
.collect(Collectors.toList());
boolean unassignedScheduledShiftExists = scheduled.stream().anyMatch(s -> s.getClinician() == null);
boolean assignedAlternateExists = alternates.stream().anyMatch(s -> s.getClinician() != null);
return unassignedScheduledShiftExists && assignedAlternateExists;
}
但这不起作用。我尝试了另一种类似于已经定义的 allocateEveryShift 约束的方法
Constraint assignEveryShift(ConstraintFactory constraintFactory) {
LOG.trace(SchedulingConstants.METHOD_ENTRY);
Constraint constraint = constraintFactory.forEachIncludingNullVars(Shift.class)
.filter(shift -> shift.getClinician() == null).penalizeConfigurable()
.asConstraint(CONSTRAINT_ASSIGN_EVERY_SHIFT);
LOG.trace(SchedulingConstants.METHOD_EXIT);
return constraint;
}
Constraint prioritizeScheduledRankShift(ConstraintFactory constraintFactory) {
LOG.trace(SchedulingConstants.METHOD_ENTRY);
Constraint constraint = constraintFactory.forEachIncludingNullVars(Shift.class).filter(
shift -> shift.getClinician() == null && shift.getRank().equals(SchedulingConstants.SCHEDULED_SHIFT))
.penalizeConfigurable().asConstraint(CONSTRAINT_SHIFT_ALLOCATION_PREFERENCE_TO_SCHEDULED_RANK);
LOG.trace(SchedulingConstants.METHOD_EXIT);
return constraint;
}
不同的是我用硬分来惩罚它。这在一定程度上有效,但并非在所有情况下都有效,我担心它会破坏某些东西。
在 Timefold 中,你不能真正说求解器应该只在完成另一个约束后才处理一个约束。求解器永远不会完成任何约束,并且它总是一起评估所有约束。
获得优先权的方法是惩罚不太重要的转变更多。要么分数水平较低,要么分数水平相同但分数更高;甚至可能显着更高的数量,
10x
,100x
,x^2
...求解器将努力提高分数,并且当它找到分数越来越好的解决方案时,实现您期望的目标的可能性正在增加。
在这种情况下,我建议将较高优先级的转变设置为硬约束,因为我假设未填充的较高优先级的转变是一个问题,因此该解决方案是不可行的。
但您最需要了解一件事 - 通过本地搜索,不能保证您会找到任何特定的解决方案。如果求解器只能找到打破硬约束的解决方案,那么这就是您将得到的。最终解决方案的质量将是移动多样性、分数计算速度和解决问题所花费的时间的因素。您可以将这些变量推得越高,您的解决方案质量就越好。