You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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");
}

额外检查点

  1. 确认hasNotDeliveredMinimumWeight和amountUnderMinimumWeight方法逻辑正确,尤其是当总重量为0时(无关联Standstill的场景),能正确判断差额并返回对应惩罚值。
  2. 确保Project与Standstill的关联关系(Standstill::getProject)是稳定的,不会在求解过程中意外变更(除非是规划变量的一部分)。

内容的提问来源于stack exchange,提问作者Nuclear_Wizard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 15:47:07