
optaplanner 的构造启发式(construction heuristics)默认仅在阶段结束时触发一次分数更新,而非每步都通知;该行为属设计使然以避免克隆开销,但旧版本存在结尾事件丢失的 bug(已在 timefold 0.8.39/0.9.39 修复)。过约束场景下可且应配置初始化分数趋势,以支持空值偏好型规划。
optaplanner 的构造启发式(construction heuristics)默认仅在阶段结束时触发一次分数更新,而非每步都通知;该行为属设计使然以避免克隆开销,但旧版本存在结尾事件丢失的 bug(已在 timefold 0.8.39/0.9.39 修复)。过约束场景下可且应配置初始化分数趋势,以支持空值偏好型规划。
在使用 OptaPlanner(及其演进版 Timefold)解决过约束规划问题(如允许 @PlanningVariable(nullable = true) 的任务未分配时间槽)时,理解构造启发式(CH)的分数更新机制与初始化分数趋势(Initializing Score Trend)的协同作用至关重要。
构造启发式阶段的分数更新:设计意图与历史 Bug
构造启发式阶段采用贪心式逐步构建初始解,为避免频繁克隆工作解决方案带来的性能损耗,OptaPlanner 默认仅在 CH 完全结束时派发一次 BestSolutionChanged 事件并更新分数——而非在每个实体赋值后都触发。这是明确的设计权衡,并非缺陷。
然而,历史版本中存在一个关键 Bug:CH 结束时未能正确触发最终分数更新事件,导致监听器(如 BestSolutionChangedEventListener)收不到完成信号,表现为“构造阶段始终无分数更新”。该问题已由 Timefold 在以下版本中修复:
- Timefold Solver 0.8.39(兼容 OptaPlanner 8.x 生态)
- Timefold Solver 0.9.39(最新稳定分支)
✅ 验证建议:升级至上述版本或更高,并通过日志或监听器确认 bestScore 在 CH 结束后是否正确输出:
solver.addBestSolutionChangedEventListener(event ->
System.out.println("CH finished → Best score: " + event.getNewBestScore())
);
过约束场景下必须启用 Initializing Score Trend
当规划实体(如 Task)允许 nullable = true,且硬约束(hard score)可能为负(例如未分配任务触发 -1hard),问题即为过约束(over-constrained):不存在满足全部硬约束的可行解。此时,若未配置初始化分数趋势,局部搜索(如 Late Acceptance、Tabu Search)将无法可靠估算分数改进方向——因为分数空间不连续,且软/中等约束权重未知。
✅ 正确做法:在 @PlanningSolution 类中显式声明趋势,例如:
@PlanningSolution
public class TaskSchedule {
@PlanningScore
public HardMediumSoftLongScore score;
@PlanningEntityCollectionProperty
public List<task> taskList;
// 必须提供初始化分数趋势(尤其对 nullable 变量)
@Override
public HardMediumSoftLongScore getInitializingScoreTrend() {
// 假设:所有任务未分配 → -N hard;全部分配 → 0 hard;中等/软分随分配质量提升而改善
return HardMediumSoftLongScore.parseScore("INIT [-1000..0]hard/[0..+1000]medium/[0..+10000]soft");
}
}</task>
⚠️ 注意事项:
- INIT [min..max] 中的区间需覆盖实际可能的分数范围(可通过小规模测试估算);
- 若使用 ConstraintStream,确保 ConstraintProvider 中的约束逻辑与趋势语义一致(例如:penalize(...) 对 null 分配产生负 hard 分);
- 不配置趋势会导致局部搜索早期误判“劣解为优”,显著降低求解效率与最终解质量。
总结
构造启发式不实时更新分数是性能优化设计,而非缺陷;但务必升级至修复 Bug 的版本以保障 CH 结束事件可靠性。对于含 nullable 规划变量的过约束问题,初始化分数趋势不是可选项,而是求解鲁棒性的前提——它为后续元启发式算法提供了必要的分数演化方向指引。结合合理约束建模与版本管理,即可高效处理“偏好分配、容忍未分配”的现实调度需求。











