OptaPlanner ProblemFact引用的规划实体反向关系影子变量未更新问题
问题根因
OptaPlanner的默认解决方案克隆机制遵循以下规则:仅克隆标注了@PlanningEntity的类实例,@ProblemFact类的实例会被直接复用。你的场景中:
Agent是规划实体,求解过程中会生成克隆副本,所有的影子变量shiftSet更新都作用在这个克隆副本上AgentAvailability是问题事实,实例直接复用,其内部存储的agent引用指向初始输入的原始Agent实例,该实例的shiftSet从未被更新,因此永远为空
可行解决方案
方案1:调整约束写法(最推荐)
完全不需要修改现有类结构,仅调整约束逻辑即可,通过@PlanningId匹配关联正确的克隆Agent实例,示例代码如下:
return constraintFactory.from(Agent.class) // 关联匹配同ID的AgentAvailability .join(AgentAvailability.class, Joiners.equal(Agent::getRegistrationNumber, availability -> availability.getAgent().getRegistrationNumber())) // 按Agent分组统计可用时段数 .groupBy((agent, availability) -> agent, count()) .filter((agent, availabilities) -> availabilities - agent.getShiftSet().size() > 0) .penalizeConfigurableLong(ShiftSchedulingConstraintConfiguration.PRO_HAS_SHIFT, (agent, availabilities) -> availabilities - agent.getShiftSet().size());
该方案无需改动核心模型,也不会引入额外风险,是最优选择。
方案2:自定义解决方案克隆器
如果你必须保留原有约束写法,可以为Schedule实现自定义克隆器,在克隆完成后手动修正AgentAvailability中的Agent引用:
- 实现
SolutionCloner<Schedule>接口,克隆完成后遍历所有AgentAvailability,通过registrationNumber匹配克隆后的Agent实例,替换原有引用 - 在
@PlanningSolution注解上指定cloner = 你自定义的克隆器类.class
注意该方案需要你自行保证克隆逻辑的正确性,避免破坏OptaPlanner的求解流程。
禁止操作
不要尝试同步原始Agent实例和克隆Agent实例的shiftSet值,OptaPlanner求解过程是多线程运行的,修改原始实例会引发线程安全问题,直接导致求解结果错误。
内容的提问来源于stack exchange,提问作者minioim
相关产品推荐
相关产品推荐

