首页 > 解决方案 > 覆盖所有班次的约束

问题描述

在一个简单的排班问题中,我希望 OptaPlanner 在我的日程安排中返回“漏洞”,以便在没有具备所需技能的员工时发现某些班次。

假设 3 个基本课程,并且我只想用HardMediumSoftScore.ONE_SOFT.

我该如何编写这样的约束?

雇员.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;

日程安排.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);
    }

标签: optaplanner

解决方案


除非我弄错了,否则你的代码会抛出NullPointerExceptions. 如果一个班次没有被覆盖,Shift.employeenull。您的约束假设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 变量为 nullable。这种方法在文档中被称为过度约束规划


推荐阅读