Optaplanner增量计算合理性:手动调度下硬约束预计算效率优化问询
解决方案:利用Bavet增量评分优化手动课程校验效率
针对你的课程排课场景,完全可以利用OptaPlanner 9.37的Bavet约束流增量评分能力,大幅提升单个潜在课程的校验效率,分两种需求场景说明:
场景1:需要保留用户可读的违规提示
不需要全量调用SolutionManager.explainScore(),可以通过手动操作BavetScoreDirector实现增量约束匹配计算:
- 初始化增量评分环境
- 基于已完成评分的现有调度计划(原
PlanningSolution),创建BavetScoreDirector,确保复用原Solution的已有分数状态,避免全量重算:ConstraintProvider constraintProvider = new YourConstraintProvider(); ScoreDirectorFactory<YourPlanningSolution> scoreDirectorFactory = ScoreDirectorFactory.create(constraintProvider); BavetScoreDirector<YourPlanningSolution> scoreDirector = (BavetScoreDirector<YourPlanningSolution>) scoreDirectorFactory.buildScoreDirector(); scoreDirector.setWorkingSolution(originalSolution); // 如果原Solution未预先评分,先执行一次全量计算(仅需执行一次) if (!scoreDirector.isScoreCalculated()) { scoreDirector.calculateScore(); }
- 基于已完成评分的现有调度计划(原
- 增量校验潜在课程
- 临时添加潜在课程到工作Solution,并触发增量评分:
Lecture newLecture = ...; // 待校验的潜在课程 scoreDirector.beforeEntityAdded(newLecture); originalSolution.getLectureList().add(newLecture); scoreDirector.afterEntityAdded(newLecture);
- 临时添加潜在课程到工作Solution,并触发增量评分:
- 提取违规提示
- 从
ScoreDirector中获取仅与新增课程相关的约束匹配结果,转换为用户可读提示:Map<Constraint, ConstraintMatchTotal<YourPlanningSolution>> constraintMatchTotalMap = scoreDirector.getConstraintMatchTotalMap(); List<String> violationMessages = new ArrayList<>(); for (ConstraintMatchTotal<YourPlanningSolution> matchTotal : constraintMatchTotalMap.values()) { // 仅关注硬约束违规 if (matchTotal.getScore().getHardScore() < 0) { // 这里可以根据约束名称或自定义匹配信息生成提示 String message = convertConstraintMatchToMessage(matchTotal); violationMessages.add(message); } }
- 从
- 回滚操作
- 移除临时添加的课程,恢复原Solution状态:
scoreDirector.beforeEntityRemoved(newLecture); originalSolution.getLectureList().remove(newLecture); scoreDirector.afterEntityRemoved(newLecture);
- 移除临时添加的课程,恢复原Solution状态:
注意:定义约束流时,建议通过
Constraint.as("约束名称")给每个硬约束命名,方便后续映射到用户可读提示;如果需要更精细的匹配信息,可以在约束流中使用constraintMatch(...)方法传递自定义数据。
场景2:仅需判断硬约束是否合法(无需违规提示)
这种场景效率更高,只需检查增量评分后的硬分数是否未恶化:
- 复用已初始化的
ScoreDirector(同场景1的步骤1) - 增量更新并检查分数
boolean isValidLecture = true; Lecture newLecture = ...; scoreDirector.beforeEntityAdded(newLecture); originalSolution.getLectureList().add(newLecture); scoreDirector.afterEntityAdded(newLecture); Score score = scoreDirector.getScore(); // 假设原计划硬分数为0(合法状态),新增后硬分数≥0则合法 if (score.getHardScore() < 0) { isValidLecture = false; } // 回滚 scoreDirector.beforeEntityRemoved(newLecture); originalSolution.getLectureList().remove(newLecture); scoreDirector.afterEntityRemoved(newLecture);
关键注意事项
- 必须严格遵循
ScoreDirector的生命周期方法(beforeEntityAdded/afterEntityAdded等),否则增量计算会出现错误。 - 原Solution只需预先全量计算一次分数,后续所有增量校验都基于该状态,避免重复计算现有计划的约束。
- Bavet约束流在OptaPlanner 9.37版本完全支持实体新增/移除的增量评分,无需额外配置。
内容的提问来源于stack exchange,提问作者B Myers
相关产品推荐
相关产品推荐

