覆盖所有班次的约束
Constraint for covering all shifts
在一个简单的排班问题中,我希望 OptaPlanner return "holes" 在我的日程安排中,以便在没有员工具备所需技能的情况下发现某些班次。
假设 3 个基本 类 并且我只想用 HardMediumSoftScore.ONE_SOFT
来惩罚未覆盖的班次。
如何写这样的约束?
Employee.java
public class Employee {
private long id = 0L;
private List<Skill> skills;
Shift.java
@PlanningEntity
public class Shift {
private RequiredSkills
@PlanningVariable(valueRangeProviderRefs = "employeeRange")
private Employee employee;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime start;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime end;
Schedule.java
@PlanningSolution
public class Schedule {
@PlanningEntityCollectionProperty
private List<Shift> shifts;
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "employeeRange")
private List<Employee> employees;
进一步假设一个简单的 ConstraintProvider
public class ScheduleConstraints implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
requiredSkill(constraintFactory),
};
}
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.filter(shift -> {
return !shift.getEmployee().hasSkills(shift.getSkills());
}).penalize("Required skill", HardMediumSoftScore.ONE_HARD);
}
这是我的尝试。好吃吗?
private Constraint allShiftsMustBeCovered(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Shift.class)
.filter(shift-> {
return shift.getEmployee().getId() == 0L;
}).penalize("All shifts must be covered", HardMediumSoftScore.ONE_SOFT);
}
除非我弄错了,否则您的代码将抛出 NullPointerException
s。如果不包括班次,则 Shift.employee
为 null
。您的约束假定 Employee
永远不会 null
- 您必须解决这个问题,也许是这样的:
// I added a null check as from() only excludes uninitialized entities.
// If employee is nullable, from() will still return Shifts with null employees.
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.filter(shift -> shift.getEmployee() != null)
.filter(shift -> {
return !shift.getEmployee().hasSkills(shift.getSkills());
}).penalize("Required skill", HardMediumSoftScore.ONE_HARD);
}
private Constraint allShiftsMustBeCovered(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Shift.class)
.filter(shift-> shift.getEmployee() == null ||
shift.getEmployee().getId() == 0L
).penalize("All shifts must be covered", HardMediumSoftScore.ONE_SOFT);
}
由于 null
是此处的有效值,请确保 Shift.employee
variable is nullable。这种方法在文档中称为 过度约束规划。
我为此使用 "fake employee"。允许假员工在任何班次工作,但对这些班次进行惩罚,以便始终在假员工之前选择真正的员工。
在一个简单的排班问题中,我希望 OptaPlanner return "holes" 在我的日程安排中,以便在没有员工具备所需技能的情况下发现某些班次。
假设 3 个基本 类 并且我只想用 HardMediumSoftScore.ONE_SOFT
来惩罚未覆盖的班次。
如何写这样的约束?
Employee.java
public class Employee {
private long id = 0L;
private List<Skill> skills;
Shift.java
@PlanningEntity
public class Shift {
private RequiredSkills
@PlanningVariable(valueRangeProviderRefs = "employeeRange")
private Employee employee;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime start;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime end;
Schedule.java
@PlanningSolution
public class Schedule {
@PlanningEntityCollectionProperty
private List<Shift> shifts;
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "employeeRange")
private List<Employee> employees;
进一步假设一个简单的 ConstraintProvider
public class ScheduleConstraints implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
requiredSkill(constraintFactory),
};
}
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.filter(shift -> {
return !shift.getEmployee().hasSkills(shift.getSkills());
}).penalize("Required skill", HardMediumSoftScore.ONE_HARD);
}
这是我的尝试。好吃吗?
private Constraint allShiftsMustBeCovered(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Shift.class)
.filter(shift-> {
return shift.getEmployee().getId() == 0L;
}).penalize("All shifts must be covered", HardMediumSoftScore.ONE_SOFT);
}
除非我弄错了,否则您的代码将抛出 NullPointerException
s。如果不包括班次,则 Shift.employee
为 null
。您的约束假定 Employee
永远不会 null
- 您必须解决这个问题,也许是这样的:
// I added a null check as from() only excludes uninitialized entities.
// If employee is nullable, from() will still return Shifts with null employees.
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.filter(shift -> shift.getEmployee() != null)
.filter(shift -> {
return !shift.getEmployee().hasSkills(shift.getSkills());
}).penalize("Required skill", HardMediumSoftScore.ONE_HARD);
}
private Constraint allShiftsMustBeCovered(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Shift.class)
.filter(shift-> shift.getEmployee() == null ||
shift.getEmployee().getId() == 0L
).penalize("All shifts must be covered", HardMediumSoftScore.ONE_SOFT);
}
由于 null
是此处的有效值,请确保 Shift.employee
variable is nullable。这种方法在文档中称为 过度约束规划。
我为此使用 "fake employee"。允许假员工在任何班次工作,但对这些班次进行惩罚,以便始终在假员工之前选择真正的员工。