OptaPlanner软约束未优化求助:硬约束生效但软约束无得分
问题分析与解决方案
核心问题:两个约束均未触发,得分全0
你的求解器返回0Hard/0Medium/0Soft,说明硬约束和软约束的逻辑都没有匹配到任何符合条件的对象,导致惩罚逻辑从未执行。以下是具体问题和修复方案:
一、软约束逻辑错误(完全未触发)
1. 统计范围错误
当前软约束用filter(wd -> wd.isFreeToWork())筛选可工作的日期,再结合!hasBreakDayBySolver过滤休息日,这会遗漏员工可工作且被安排工作的核心数据。应该直接筛选实际被安排工作的日期:
// 替换原filter逻辑 .from(WorkingDay.class) .filter(WorkingDay::hasWork) // 直接统计已安排工作的日期
2. 惩罚逻辑反向
你的软约束目标是「尽可能多安排工作时长」,但当前逻辑对超过66小时的情况也会惩罚,这会引导求解器尽量让时长等于66,而非最大化工作时长。修正为仅惩罚不足66小时的情况:
.penalizeConfigurable(AVERAGE_HOURS_WORKING, ((agent, count) -> { int totalHours = count * 7; // 仅当总时长不足66时惩罚,超过则不惩罚(鼓励多安排) return totalHours < 66 ? (66 - totalHours) * 2 : 0; }))
3. 冗余分组条件
WorkingDay::hasBreakDayBySolver的分组条件是冗余的,因为hasWork()已经能判断是否为工作日,可移除该分组条件,简化逻辑:
.groupBy(WorkingDay::getAgent, ConstraintCollectors.count()) // 移除后续的filter(!hasBreakDayBySolver)逻辑
二、硬约束逻辑错误(未正确识别连续工作天数)
原硬约束的join条件无法准确统计「连续5天工作」的情况,wd2.getDayJava() >= wd1.getDayJava().minusDays(4)会把5天窗口内的所有工作日都统计进去,而非连续的日期。改用OptaPlanner的sequence统计连续序列:
private Constraint fiveConsecutiveWorkingDaysMax(ConstraintFactory constraintFactory) { return constraintFactory .from(WorkingDay.class) .filter(WorkingDay::hasWork) // 按员工分组,将日期按顺序排列,判断连续天数 .groupBy(WorkingDay::getAgent, ConstraintCollectors.sequence(WorkingDay::getDayJava, (prevDay, currDay) -> ChronoUnit.DAYS.between(prevDay.getDayJava(), currDay.getDayJava()) == 1)) // 连续工作天数≥5时触发惩罚 .filter((agent, workSequence) -> workSequence.size() >= 5) // 每多1天连续工作,惩罚1(可根据需求调整权重) .penalizeConfigurable(FIVE_CONSECUTIVE_WORKING_DAYS_MAX, (agent, workSequence) -> workSequence.size() - 4); }
三、验证得分配置
确保在求解器配置中正确绑定约束权重:
SolverFactory<YourSolution> solverFactory = SolverFactory.create(new SolverConfig() .withSolutionClass(YourSolution.class) .withEntityClasses(WorkingDay.class) .withScoreDirectorFactory(new ScoreDirectorFactoryConfig() .withScoreDefinitionClass(HardSoftScoreDefinition.class) .withConstraintProviderClass(YourConstraintProvider.class) // 配置约束权重(硬约束100,软约束100) .withConstraintConfiguration(new ConstraintConfiguration() .addConstraintWeight("FIVE_CONSECUTIVE_WORKING_DAYS_MAX", HardSoftScore.ofHard(100)) .addConstraintWeight("AVERAGE_HOURS_WORKING", HardSoftScore.ofSoft(100)))));
四、调试建议
- 在约束的
filter方法中添加日志,确认是否有数据进入后续逻辑; - 检查
hasWork()、hasBreakDayBySolver等方法的返回值是否符合预期,避免逻辑反转; - 用OptaPlanner的调试工具(如SolverManager的日志、ScoreDirector的解释功能)查看得分计算过程,定位未触发的约束。
内容的提问来源于stack exchange,提问作者Raphawel
相关产品推荐
相关产品推荐

