【发布时间】:2021-02-11 03:36:04
【问题描述】:
尝试将 1000 个观测(目前的持续时间相同)安排到 500 个时间段中,因此只有一半适合。使用带有可为空的计划变量“timeSlot”的计划实体“Observation”。以 ConstraintStream 表示的约束
-
observation1.timeSlot != observation2.timeSlot,如果不罚1hard -
observation.timeSlot != null,如果null处以 1medium
10 分钟后或找到可行解决方案时终止
调查结果
- 将 501 次观察调度到 500 个槽中会在 16 秒后以 0hard/-1medium 终止。这是意料之中的。
- 将 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