OptaPlanner约束流引发分数损坏问题排查求助
问题分析与解决方案
你的约束流实现存在两个核心问题,直接导致了分数损坏和初始分数不符合预期:
1. 内连接遗漏无关联Standstill的Project
默认的join是内连接,只有存在匹配Standstill的Project才会进入后续的groupBy流程。这意味着没有任何Standstill关联的Project会被完全排除在约束逻辑之外,既不会触发惩罚,也不会被增量计算正确跟踪——这就是初始构建时soft分数为0(而非预期的-150000)的根本原因。
2. 增量分数计算的状态跟踪错误
当执行Standstill分配车辆的动作时,增量计算逻辑无法正确处理“从无匹配Standstill到有匹配Standstill”的状态变化:初始时该Project未进入约束流,增量更新时错误地生成了多余的约束匹配(即错误的惩罚),导致workingScore与完全重算的uncorruptedScore不一致,触发分数损坏。
修正后的约束流实现
要解决这个问题,需要确保所有Project都被纳入约束逻辑(包括无关联Standstill的情况),同时让增量计算能正确跟踪总重量的变化。以下是两种可行的修正方案:
方案一:使用aggregate聚合关联Standstill的重量
protected Constraint projectMinimumWeightDelivered(ConstraintFactory constraintFactory) { // 确保所有Project都被处理,无关联Standstill时总重量为0 return constraintFactory .forEach(Project.class) .filter(project -> project.getMinimumWeightToDeliver() != null) // 聚合当前Project下所有符合条件的Standstill,计算总重量 .aggregate(Standstill.class, equal(Project::getId, standstill -> standstill.getProject().getId()), (project, standstill) -> standstill.isPickup() && standstill.getVehicle() != null && standstill.getVehicle().isReal(), sum(Standstill::getWeight)) // 判断是否未达到最低配送重量 .filter((project, totalDeliveredWeight) -> project.getMinimumWeightToDeliver() > totalDeliveredWeight) // 按差额惩罚 .penalizeConfigurable((project, totalDeliveredWeight) -> project.getMinimumWeightToDeliver() - totalDeliveredWeight) .asConstraint("Project minimum weight delivered"); }
方案二:使用左连接(Left Join)+ GroupBy
protected Constraint projectMinimumWeightDelivered(ConstraintFactory constraintFactory) { return constraintFactory .forEach(Project.class) .filter(project -> project.getMinimumWeightToDeliver() != null) // 左连接保留所有Project,即使无匹配Standstill .leftJoin(Standstill.class, equal(project -> project, Standstill::getProject), (project, standstill) -> standstill == null || (standstill.isPickup() && standstill.getVehicle() != null && standstill.getVehicle().isReal())) // 按Project分组,计算有效Standstill的总重量(无Standstill时为0) .groupBy((project, standstill) -> project, sum((project, standstill) -> standstill != null ? standstill.getWeight() : 0)) .filter((project, totalDeliveredWeight) -> project.getMinimumWeightToDeliver() > totalDeliveredWeight) .penalizeConfigurable((project, totalDeliveredWeight) -> project.getMinimumWeightToDeliver() - totalDeliveredWeight) .asConstraint("Project minimum weight delivered"); }
额外检查点
- 确认
hasNotDeliveredMinimumWeight和amountUnderMinimumWeight方法逻辑正确,尤其是当总重量为0时(无关联Standstill的场景),能正确判断差额并返回对应惩罚值。 - 确保Project与Standstill的关联关系(
Standstill::getProject)是稳定的,不会在求解过程中意外变更(除非是规划变量的一部分)。
内容的提问来源于stack exchange,提问作者Nuclear_Wizard
相关产品推荐
相关产品推荐

