OptaPlanner:如何实现指定次数匹配硬约束并正确使用ifNotExists
实现特定选项选中次数的硬约束(OptaPlanner)
核心实现思路
要确保cpId=1的选项被选中至少4次,无需使用ifNotExists()方法,直接统计选中的目标实体数量,当数量不足时计算对应硬惩罚即可。硬约束的惩罚值需设置为足够大的权重,确保不符合条件的解被直接排除。
约束代码示例
// 在你的ConstraintProvider实现类中编写该约束方法 public Constraint cpId1MinSelection(ConstraintFactory constraintFactory) { // 统计选中cpId=1的MealMenu实体数量,不足4次则触发硬惩罚 return constraintFactory.forEach(MealMenu.class) .filter(mealMenu -> mealMenu.getSelectedCpId() != null && mealMenu.getSelectedCpId() == 1) .count() .filter(selectedCount -> selectedCount < 4) .penalize("cpId=1选中次数未达标", HardSoftScore.ONE_HARD, selectedCount -> 4 - selectedCount); }
代码细节说明
forEach(MealMenu.class):遍历所有标注了@PlanningEntity的MealMenu实体filter(...):筛选出已选中cpId=1的实体count():统计符合筛选条件的实体总数filter(selectedCount <4):仅针对选中次数不足4次的场景触发惩罚penalize(...):设置硬约束惩罚,惩罚值为4 - selectedCount,每少选一次就增加1个硬惩罚权重,直到选中次数达标。
关于ifNotExists()的说明
ifNotExists()方法主要用于检查不存在某类特定实体的场景,并不适配本次「统计数量达标」的需求,直接统计计数的方式更直观且贴合业务逻辑。
结合CpMustUse类的扩展实现
如果需要从@ProblemFactCollectionProperty的CpMustUse类中读取要求的选中次数(比如动态配置次数),可以调整代码如下:
public Constraint cpIdMinSelection(ConstraintFactory constraintFactory) { return constraintFactory.forEach(CpMustUse.class) .filter(cpMustUse -> cpMustUse.getCpId() == 1) .join(MealMenu.class, Joiners.filtering((cpMustUse, mealMenu) -> mealMenu.getSelectedCpId() != null && mealMenu.getSelectedCpId() == cpMustUse.getCpId())) .groupBy((cpMustUse, mealMenu) -> cpMustUse, ConstraintCollectors.count()) .filter((cpMustUse, selectedCount) -> selectedCount < cpMustUse.getRequiredTimes()) .penalize("特定cp选中次数未达标", HardSoftScore.ONE_HARD, (cpMustUse, selectedCount) -> cpMustUse.getRequiredTimes() - selectedCount); }
注:需确保CpMustUse类包含getCpId()和getRequiredTimes()方法(后者返回要求的选中次数,比如4)。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

