首页 > 解决方案 > 有分数的 VRP 依赖于 optaplanner 中的不止一项工作

问题描述

我们正在尝试使用 Optaplanner 解决 VRP。分数计算通过约束流运行。

现在我有两辆车(A 和 B),想安排两个工作(J1 和 J2)。构造启发式 (FIRST_FIT_DECREASING) 将 J1 调度到 A 并将 J2 调度到 B,到目前为止是正确的。

现在这两个工作也有一个属性“客户”,如果两个工作的客户相同但车辆不同,我想分配一个惩罚。

为此,我在 ConstraintProvider 中创建了一个约束,该约束通过 groupBy 过滤所有具有相同客户但不同车辆的作业。

如果我现在打开 FULL_ASSERT_MODE,在调度 J2 之后会发生 IllegalStateException,因为增量计算的分数与完整计算的分数不同。我怀疑这是因为重新计算作业时间的 VariableListener 只告诉 ScoreDirector 我的影子变量对 Job J2 的更改,因此只更改与其相关的分数部分。

我如何告诉 Optaplanner J1 的分数也必须重新计算?我无法通过 VariableListener 到达 Job J1 告诉 ScoreDirector 必须在此处更改分数。

还是这个问题需要不同的方法?

标签: optaplannervehicle-routing

解决方案


这是一个有点难以完全解释的问题。TLDR 版本:约束流仅对来自或from()的对象的更改做出反应。未通过这些语句的对象的更改将不会被捕获,因此会导致分数损坏。更长的解释如下。join()ifExists()

考虑这样一个假设的约束流:

constraintFactory.from(Shift.class)
    .join(Shift.class)
    .filter((shift1, shift2) -> shift1.getEmployee() == shift2.getEmployee())
    ...

这个约束流可以正常工作,因为如果您Shift通过设置不同的员工进行更改,Shifts 将被重新评估。from()它们通过and进入流join(),这就是 CS 知道在它们发生变化时重新评估的方式Shifts

现在考虑这个约束流:

constraintFactory.from(Shift.class)
    .filter(shift -> shift.getEmployee().getName() == "Lukas")
    ...

Shift如果发生更改,将重新评估此约束流。但是当nameofEmployee发生变化时,约束流不会被重新评估;Employee既不是 infrom()也不是 in join(),对 的更改Employee不会触发约束流的重新评估。

在您的特定情况下,您需要确保几件事:

  • 可变侦听器将所有实际更改的内容标记为已更改。
  • 如果你修改问题事实,你需要确保你的变量监听器也能处理它。
  • 您希望约束流对其做出反应的对象是通过from()join().

推荐阅读