OptaPlanner:MultiConstraintStream的GroupBy多字段分组用法疑问
OptaPlanner 多字段GroupBy实现:每日作业产出上限约束
问题核心
需要按时间段(日期)和作业类型对活跃作业分组,汇总每日产出并判断是否超过该作业类型的日产出上限。
优化后的完整约束代码
private Constraint maximumDailyOutput(ConstraintFactory constraintFactory) { // 限制每种作业类型的每日最大产出量 return constraintFactory.forEach(TimeSlotOpta.class) // 关联当日活跃的作业 .join(JobOpta.class, Joiners.filtering((timeSlot, job) -> job.isActive(timeSlot.getDay()))) // 按【时间段】和【作业类型】分组,同时汇总该组的每日产出总和 .groupBy( // 第一个分组键:时间段(日期) (timeSlot, job) -> timeSlot, // 第二个分组键:作业类型 (timeSlot, job) -> job.getJobType(), // 聚合函数:汇总该组所有作业的每日产出 ConstraintCollectors.sum(job -> job.getDailyOutput()) ) // 仅对产出超过上限的分组进行惩罚 .filter((timeSlot, jobType, totalDailyOutput) -> totalDailyOutput > jobType.getMaximumDailyOutput()) // 计算惩罚值:超出上限的部分 .penalize("Maximum daily output exceeded", HardMediumSoftScore.ONE_HARD, (timeSlot, jobType, totalDailyOutput) -> totalDailyOutput - jobType.getMaximumDailyOutput()); }
关键细节说明
移除冗余的JobTypeOpta关联
原代码中额外join(JobTypeOpta.class)完全没必要,因为JobOpta已经通过getJobType()持有JobTypeOpta引用,直接提取即可,减少了不必要的关联和过滤逻辑,提升约束计算效率。多字段GroupBy的正确写法
OptaPlanner的groupBy支持同时指定多个分组键,配合聚合函数:- 前两个参数是分组键提取器,分别提取时间段和作业类型作为分组依据;
- 第三个参数是聚合函数,这里用
ConstraintCollectors.sum()汇总该组所有作业的dailyOutput。
添加过滤逻辑避免无效惩罚
在groupBy之后添加filter,只对totalDailyOutput超过jobType.getMaximumDailyOutput()的分组进行惩罚,避免对合规分组产生不必要的计算。方法名规范
修正了原方法名MaximumDailyOuput的拼写错误(应为maximumDailyOutput,符合Java驼峰命名规范)。
内容的提问来源于stack exchange,提问作者Rayamon
相关产品推荐
相关产品推荐

