上下文是OptaPlanner的员工轮班分配,使用Drools规则计算分数。我的员工不能连续工作三天以上而没有休息日。我非常愚蠢地实现了这样一个约束:
rule "No more than three consecutive working days"
when
ShiftAssignment(
$id1 : id,
$empoloyee : empoloyee != null,
$shift1 : shift
)
ShiftAssignment(
id > $id1,
empoloyee == $empoloyee,
shift.isConsecutiveDay($shift1),
$id2 : id,
$shift2 : shift
)
ShiftAssignment(
id > $id2,
empoloyee == $empoloyee,
shift.isConsecutiveDay($shift2),
$id3 : id,
$shift3 : shift
)
ShiftAssignment(
id > $id3,
empoloyee == $empoloyee,
shift.isConsecutiveDay($shift10)
)
then
scoreHolder.penalize(kcontext);
end
我希望方法/变量的名称能清楚地揭示它们的作用/含义。有没有更方便、更聪明的方法来执行这样的规则?请记住,上面的三天可能需要更改为一个更大的数字(我使用三天是为了避免规则中出现更现实的十行或更多的代码(。谢谢
如果我们可以假设一名员工每天只上一个班,shift.isConsecutiveDay()
可能会被shift.day == $shift1.day + 1
之类的东西取代,则可以使用exists
:
when
ShiftAssignment($employee : empoloyee != null, $shift1 : shift)
exists ShiftAssignment(employee == $employee, shift.day == $shift1.day + 1)
exists ShiftAssignment(employee == $employee, shift.day == $shift1.day + 2)
exists ShiftAssignment(employee == $employee, shift.day == $shift1.day + 3)
then
如果不能做出这样的假设,您的解决方案应该有效,需要考虑一个潜在的角落案例:
该规则试图通过条件id > $id1
过滤掉相同移位的组合。此条件有效,但ID必须在轮班时递增生成,否则,它将与shift.isConsecutiveDay(...)
冲突。如果无法保证此属性,则最好检查ID不等式。
我使用了规则组合来实现这一点。第一条规则设置连续工作序列的开始,第二条规则设置结束,第三条规则创建";"工作顺序";适合在开始和结束之间。最后;最大连续天数";规则实际上检查您的";"工作顺序";对连续天数的限制。
这种模式实际上存在于护士名册示例中:https://github.com/kiegroup/optaplanner/blob/master/optaplanner-examples/src/main/resources/org/optaplanner/examples/nurserostering/solver/nurseRosteringConstraints.drl