【问题标题】:Strategies for heavily over-constrained scheduling严重过度约束调度的策略
【发布时间】:2021-02-11 03:36:04
【问题描述】:

尝试将 1000 个观测(目前的持续时间相同)安排到 500 个时间段中,因此只有一半适合。使用带有可为空的计划变量“timeSlot”的计划实体“Observation”。以 ConstraintStream 表示的约束

  • observation1.timeSlot != observation2.timeSlot,如果不罚1hard
  • observation.timeSlot != null,如果 null 处以 1medium

10 分钟后或找到可行解决方案时终止

调查结果

  1. 将 501 次观察调度到 500 个槽中会在 16 秒后以 0hard/-1medium 终止。这是意料之中的。
  2. 将 1000 个观测值调度到 500 个槽中会在 10m 后以 -499hard/-1medium 终止。这完全出乎意料。我预计 0hard/-500medium,或者至少朝着这个方向发展。

有哪些必要措施让 optaplanner 做正确的事?

约束

    Constraint notSameSlot(ConstraintFactory constraintFactory) {
        constraintFactory.fromUniquePair(OB.class)
            .filter({ ob1, ob2 -> ob1.start == ob2.start })
            .penalize("overlap", HardMediumSoftScore.ONE_HARD)
    }

    Constraint notAssignable(ConstraintFactory constraintFactory) {
        constraintFactory.fromUnfiltered(OB.class)
            .filter(ob -> ob.start == null)
            .penalize("not assignable", HardMediumSoftScore.ONE_MEDIUM)
    }

规划实体

@PlanningEntity
public class OB {
    @PlanningVariable(nullable = true, valueRangeProviderRefs = "timeslotRange")
    Timeslot start
    @PlanningId
    int obId
    OB(int obId) {this.obId = obId }
    OB() {} // required by Optaplanner, not sure why
}

时间段

class Timeslot {
    @PlanningId
    int slot

    Timeslot(int slot) {
        this.slot = slot
    }
}

规划解决方案

@PlanningSolution
class Schedule {
    @ValueRangeProvider(id = "timeslotRange")
    @ProblemFactCollectionProperty 
    List<Timeslot> timeslots

    @PlanningEntityCollectionProperty
    List<OB> observations

    @PlanningScore
    HardMediumSoftScore score
}

更新:向notSameSlot 约束添加了空检查,我不知道fromUniquePairs 传递了未分配的实体。这会改变结果,但不会变得更好。现在,根本没有分配任何插槽。现在的结果是 0hard/-1000medium/0soft

Constraint notSameSlot(ConstraintFactory constraintFactory) {
     constraintFactory.fromUniquePair(OB.class)
            .filter({ ob1, ob2 ->
                ob1.start !== null &&
                ob2.start !== null &&
                ob1.start.slot == ob2.start.slot
            })
            .penalize("overlap", HardMediumSoftScore.ONE_HARD)
    }

【问题讨论】:

  • 我会用 optaplanner-benchmark 运行它,并查看这两个结果的最佳分数图。然后问这样的问题:发现 1 是在构造启发式之后打破硬约束开始的吗?本地搜索是否解决了这些问题。如果 CH 打破了硬约束,为什么它会分配值,而它可以选择不分配它们呢? (请参阅后者的 TRACE 日志记录)
  • 感谢 Geoffrey,我确实尝试过基准测试。但它只显示空图,因为只找到一个解决方案。这相当于选择“从 1000 个随机事物中挑选 500 个,然后将它们中的每一个扔到 500 个桶中的一个中,每个桶一个”。我希望这更容易做和理解。
  • 如果只绘制一个点(并且它早于您的终止时间,因此您的分数中没有init),这意味着没有开箱即用的移动选择器(更改和交换移动)会产生任何改进,这很奇怪。在 TRACE 日志中运行,看看在 LS 跟踪期间 CH 结束后会发生什么。
  • 乍一看,这听起来像是你的约束中的一个错误:一些不应该受到惩罚的东西。您是否使用 ConstraintVerifier 进行了单元测试(请参阅文档)?
  • @GeoffreyDeSmet 我在上面添加了相关的源代码,包括相当琐碎的约束。如果您能快速了解一下这些限制,我将不胜感激。这有什么问题?

标签: optaplanner


【解决方案1】:

发现问题。我有

<termination>
    <unimprovedSpentLimit>PT2M30S</unimprovedSpentLimit>
    <bestScoreFeasible>true</bestScoreFeasible>
</termination>

作为终止标准。我不明白 bestScoreFeasible 意味着只要不违反硬约束,求解器就会终止,这会导致没有分配槽,因为这仍然被认为是可行的解决方案。删除第二个标准即可解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-25
    • 2013-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多