OptaPlanner/TimeFold中关联PlanningEntity列表的约束编写咨询
正确实现基于ShadowVariable列表的OptaPlanner约束
你的核心问题在于:直接遍历MyOtherPlanningEntity并访问其myPlanningEntities列表时,OptaPlanner的增量分数计算器无法感知列表中MyPlanningEntity实例的规划变量变更,导致分数计算不稳定。以下是两种正确的实现方式:
方式一:针对完整列表计算惩罚(保留父实体信息)
如果你的myFunction需要基于整个myPlanningEntities列表计算惩罚值,使用flattenLast展开列表后,通过groupBy聚合回原实体及其完整列表,这样既保留了MyOtherPlanningEntity的weight信息,又能让OptaPlanner追踪所有MyPlanningEntity的变更:
Constraint myConstraint(ConstraintFactory constraintFactory) { return constraintFactory.forEach(MyOtherPlanningEntity.class) // 展开列表,生成(MyOtherPlanningEntity, MyPlanningEntity)元组,让OptaPlanner追踪PlanningEntity变更 .flattenLast(MyOtherPlanningEntity::getMyPlanningEntities) // 按MyOtherPlanningEntity分组,聚合回对应的完整PlanningEntity列表 .groupBy((otherEntity, planningEntity) -> otherEntity, Collectors.toList()) // 使用父实体的weight和完整列表计算惩罚值 .penalize(HardSoftScore.ONE_HARD, (otherEntity, planningEntityList) -> myFunction(otherEntity.getWeight(), planningEntityList)) .asConstraint("My constraint"); }
方式二:针对单个实体计算惩罚后累加
如果myFunction可以拆解为针对每个MyPlanningEntity计算部分惩罚值,再累加得到总惩罚,可简化为直接遍历展开后的元组计算:
Constraint myConstraint(ConstraintFactory constraintFactory) { return constraintFactory.forEach(MyOtherPlanningEntity.class) .flattenLast(MyOtherPlanningEntity::getMyPlanningEntities) // 基于每个PlanningEntity和父实体的weight计算单次惩罚,自动累加 .penalize(HardSoftScore.ONE_HARD, (otherEntity, planningEntity) -> calculatePerEntityPenalty(otherEntity.getWeight(), planningEntity)) .asConstraint("My constraint"); }
关键说明
flattenLast是OptaPlanner约束流的官方API,会正确追踪所有涉及的规划实体变更:包括MyPlanningEntity的myPlanningVariable变化,以及MyOtherPlanningEntity的myPlanningEntities列表的变更(因为是ShadowVariable,列表内容更新时约束流会自动感知)。- 两种方式都无需针对不同列表长度编写单独约束,完全适配可变长度的列表场景。
内容的提问来源于stack exchange,提问作者marinier
相关产品推荐
相关产品推荐

