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

如何在双实体模型中评估Constraint Stream总分并实现资源上限管控?

解决方案

方法1:合并约束流直接计算总分约束

既然目标是控制两个约束流的总和,最直接的方式是把两个逻辑合并到一个约束中,计算出实际总资源消耗后,直接和阈值对比,超出部分施加惩罚,无需再试调单个约束的权重。

总资源消耗公式:
总消耗 = 激活场景的运行总数 - 完全覆盖路线的(运行数-1)总和

对应的约束实现:

Constraint totalResourceLimit(ConstraintFactory constraintFactory) {
    // 定义资源上限阈值
    final long RESOURCE_THRESHOLD = 100;

    // 计算激活场景的总运行数
    UniConstraintStream<SelectedScenario> activeScenarios = constraintFactory.forEach(SelectedScenario.class)
            .filter(scenario -> scenario.getOnOff().isOnOff());
    ConstraintCollector<SelectedScenario, ?, Long> totalRunsCollector = ConstraintCollectors.sumLong(SelectedScenario::getRunCount);

    // 计算可抵扣的总量:完全覆盖路线的(runCount-1)之和
    UniConstraintStream<SelectedRoute> eligibleRoutes = constraintFactory.forEach(SelectedRoute.class)
            .filter(route -> route.getRouteOnOff().isOnOff())
            .join(RouteScenario.class, Joiners.equal(SelectedRoute::getRoute, RouteScenario::getRoute))
            .join(SelectedScenario.class, Joiners.equal((route, rs) -> rs.getScenario(), SelectedScenario::getScenario))
            .filter((route, rs, scenario) -> scenario.getOnOff().isOnOff())
            .groupBy((route, rs, scenario) -> route, countTri())
            .filter((route, count) -> route.getRunCount() == count);
    ConstraintCollector<SelectedRoute, ?, Long> totalCreditCollector = ConstraintCollectors.sumLong(route -> route.getRunCount() - 1);

    // 聚合两个值并判断是否超限
    return activeScenarios.collect(totalRunsCollector)
            .join(eligibleRoutes.collect(totalCreditCollector))
            .filter((totalRuns, totalCredit) -> (totalRuns - totalCredit) > RESOURCE_THRESHOLD)
            .penalize(BendableScore.ofHard(BENDABLE_SCORE_HARD_LEVELS_SIZE, BENDABLE_SCORE_SOFT_LEVELS_SIZE, 1, 0),
                    (totalRuns, totalCredit) -> (totalRuns - totalCredit) - RESOURCE_THRESHOLD)
            .asConstraint("totalResourceLimit");
}

方法2:自定义ScoreHolder监控总分

如果不想改动原有约束流结构,可以通过自定义ScoreHolder来记录两个约束的分数贡献,实时计算总和并触发超限惩罚:

  1. 实现自定义ScoreHolder:
public class CustomResourceScoreHolder extends BendableScoreHolder<BendableScore> {
    private long runsTotal;
    private long creditTotal;
    private final long resourceThreshold;

    public CustomResourceScoreHolder(boolean constraintMatchEnabled, int hardLevelsSize, int softLevelsSize, long resourceThreshold) {
        super(constraintMatchEnabled, hardLevelsSize, softLevelsSize);
        this.resourceThreshold = resourceThreshold;
    }

    public void addRunsContribution(long amount) {
        runsTotal += amount;
        checkThreshold();
    }

    public void addCreditContribution(long amount) {
        creditTotal += amount;
        checkThreshold();
    }

    private void checkThreshold() {
        long totalConsumption = runsTotal - creditTotal;
        if (totalConsumption > resourceThreshold) {
            // 移除之前的超限惩罚,避免重复累加
            removeConstraintMatch("totalResourceLimit");
            // 添加新的超限惩罚
            addHardConstraintMatch(null, "totalResourceLimit", totalConsumption - resourceThreshold);
        } else {
            removeConstraintMatch("totalResourceLimit");
        }
    }
}
  1. 修改原有约束,使用自定义ScoreHolder:
// 修改runsOnActiveScenarios约束
Constraint runsOnActiveScenarios(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(SelectedScenario.class)
            .filter(selectedScenario -> selectedScenario.getOnOff().isOnOff())
            .penalize((CustomResourceScoreHolder scoreHolder, SelectedScenario scenario) -> {
                scoreHolder.addRunsContribution(scenario.getRunCount());
                return BendableScore.ofSoft(BENDABLE_SCORE_HARD_LEVELS_SIZE, BENDABLE_SCORE_SOFT_LEVELS_SIZE, 2, 1);
            })
            .asConstraint("runsOnActiveScenarios");
}

// 修改creditRouteRunReduction约束
Constraint creditRouteRunReduction(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(SelectedRoute.class)
            .filter(selectedRoute -> selectedRoute.getRouteOnOff().isOnOff())
            .join(RouteScenario.class, Joiners.equal((selectedRoute) -> selectedRoute.getRoute(), RouteScenario::getRoute))
            .join(SelectedScenario.class, Joiners.equal((selectedRoute, routeScenario) -> routeScenario.getScenario(), SelectedScenario::getScenario))
            .filter((selectedRoute, routeScenario, selectedScenario) -> selectedScenario.getOnOff().isOnOff())
            .groupBy((selectedRoute, routeScenario, selectedScenario) -> selectedRoute, countTri())
            .filter((selectedRoute, count) -> selectedRoute.getRunCount() == count)
            .reward((CustomResourceScoreHolder scoreHolder, SelectedRoute route, Long count) -> {
                scoreHolder.addCreditContribution(route.getRunCount() - 1);
                return BendableScore.ofSoft(BENDABLE_SCORE_HARD_LEVELS_SIZE, BENDABLE_SCORE_SOFT_LEVELS_SIZE, 2, 1);
            })
            .asConstraint("creditRouteRunReduction");
}
  1. 在Solver配置中指定自定义ScoreHolder:
SolverFactory<YourSolution> solverFactory = SolverFactory.create(new SolverConfig()
        .withSolutionClass(YourSolution.class)
        .withEntityClasses(SelectedScenario.class, SelectedRoute.class)
        .withScoreDirectorFactory(new ScoreDirectorFactoryConfig()
                .withScoreDefinitionClass(BendableScoreDefinition.class)
                .withHardLevelsSize(BENDABLE_SCORE_HARD_LEVELS_SIZE)
                .withSoftLevelsSize(BENDABLE_SCORE_SOFT_LEVELS_SIZE)
                .withScoreHolderClass(CustomResourceScoreHolder.class)));

方法3:用全局约束聚合计算(推荐)

OptaPlanner的ConstraintCollector支持全局聚合操作,可以在一个约束中完成两个部分的计算和超限判断,完全符合约束流的设计范式:

Constraint totalResourceConstraint(ConstraintFactory constraintFactory) {
    final long RESOURCE_THRESHOLD = 100;

    // 聚合激活场景的总运行数
    UniConstraintStream<Long> totalRunsStream = constraintFactory.forEach(SelectedScenario.class)
            .filter(scenario -> scenario.getOnOff().isOnOff())
            .collect(ConstraintCollectors.sumLong(SelectedScenario::getRunCount));

    // 聚合完全覆盖路线的抵扣总和
    UniConstraintStream<Long> totalCreditStream = constraintFactory.forEach(SelectedRoute.class)
            .filter(route -> route.getRouteOnOff().isOnOff())
            .join(RouteScenario.class, Joiners.equal(SelectedRoute::getRoute, RouteScenario::getRoute))
            .join(SelectedScenario.class, Joiners.equal((route, rs) -> rs.getScenario(), SelectedScenario::getScenario))
            .filter((route, rs, scenario) -> scenario.getOnOff().isOnOff())
            .groupBy((route, rs, scenario) -> route, countTri())
            .filter((route, count) -> route.getRunCount() == count)
            .collect(ConstraintCollectors.sumLong((route, count) -> route.getRunCount() - 1));

    // 合并两个聚合结果,判断超限并惩罚
    return totalRunsStream.join(totalCreditStream)
            .filter((totalRuns, totalCredit) -> (totalRuns - totalCredit) > RESOURCE_THRESHOLD)
            .penalize(BendableScore.ofHard(BENDABLE_SCORE_HARD_LEVELS_SIZE, BENDABLE_SCORE_SOFT_LEVELS_SIZE, 1, 0),
                    (totalRuns, totalCredit) -> (totalRuns - totalCredit) - RESOURCE_THRESHOLD)
            .asConstraint("totalResourceLimit");
}

关键注意事项

  • 由于你的问题属于重叠集合覆盖问题(无凸性),建议使用线性惩罚(超出阈值多少就罚多少),避免非线性惩罚导致求解器陷入局部最优。
  • 根据需求选择约束类型:如果资源上限是必须遵守的硬规则,用ofHard;如果允许超限但需要优先规避,用ofSoft,匹配你已有的可弯曲分数配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:30:09