Timefold中如何基于实体与问题事实集计算扣分规则
问题描述
此前使用OptaPlanner + Drools时,我们通过insertLogical创建包含实体集合与问题事实集合的pointscalculateinstance实例,基于该实例的计算结果确定多个扣分点。相关示例如下:
Drools规则示例
rule "create pointscalculateinstance" when $requestList : ArrayList() from collect ( Request () ) $stockList : ArrayList() from collect ( Stock() ) $taskList : ArrayList() from collect ( Task (machine != null) ) then insertLogical(new pointscalculateinstance($requestList,$stockList,$taskList)); end rule "point1 rule" when $pointscalculateinstance : pointscalculateinstance ($point1 : point1) eval($point1 > 0) then scoreHolder.addSoftConstraintMatch(kcontext, $point1); end rule "point2 rule" when $pointscalculateinstance: pointscalculateinstance($point2 : point2) eval($point2 > 0) then scoreHolder.addSoftConstraintMatch(kcontext, $point2); end rule "point3 rule" when $pointscalculateinstance : pointscalculateinstance($point3 : point3) eval($point3 > 0) then scoreHolder.addHardConstraintMatch(kcontext, $point3); end
相关类定义
pointscalculateinstance类
public class pointscalculateinstance{ private ArrayList<Request> requestList; private ArrayList<Stock> stockList; private ArrayList<Task> taskList; private int point1; private int point2; private int point3; private int point4; private int point5; private int point6; private int point7; private int point8; private int point9; private int point10; }
Planning(Solution类)
@PlanningSolution public class Planning{ @PlanningScore private HardMediumSoftScore score; @PlanningEntityCollectionProperty @ValueRangeProvider(id = "taskRange") private ArrayList<Task> taskList; @ProblemFactCollectionProperty @ValueRangeProvider(id = "machineRange") private ArrayList<Machine> machineList; @ProblemFactCollectionProperty private ArrayList<Request> requestList; @ProblemFactCollectionProperty private ArrayList<Stock> stockList; }
这是一个链式模型,需基于任务列表的链式顺序计算每个方案的库存消耗利用率。重构时想用Timefold的Constraint Stream实现,但如果用group by + tolist()会重复编写大量约束,不是最优方案。请问如何在Timefold中创建含实体集合与问题事实集合参数的实例,基于其计算结果确定多个扣分点?
解决方案
在Timefold的Constraint Stream中,无需像Drools那样插入逻辑对象,可通过聚合所有需要的集合并复用计算逻辑,一次性推导所有扣分点,避免重复编写流程。
步骤1:完善计算类
先遵循Java规范将pointscalculateinstance重命名为PointsCalculateInstance,并补充计算所有扣分点的核心逻辑:
public class PointsCalculateInstance { private List<Request> requestList; private List<Stock> stockList; private List<Task> taskList; private int point1; private int point2; private int point3; // ... 其他point字段 // 构造方法:传入集合后自动计算所有扣分点 public PointsCalculateInstance(List<Request> requestList, List<Stock> stockList, List<Task> taskList) { this.requestList = requestList; this.stockList = stockList; this.taskList = taskList; calculateAllPoints(); } // 核心业务逻辑:按任务链式顺序计算库存消耗利用率,推导所有point值 private void calculateAllPoints() { // 实现你的业务逻辑:比如遍历链式任务、计算库存消耗、赋值point1-point10 this.point1 = ...; this.point2 = ...; this.point3 = ...; // ... 其他point的计算逻辑 } // Getter方法 public int getPoint1() { return point1; } public int getPoint2() { return point2; } public int getPoint3() { return point3; } // ... 其他point的Getter }
步骤2:在Constraint Stream中复用聚合与计算逻辑
在约束提供者类中,提取共享的集合聚合逻辑,所有扣分点约束复用同一计算实例,避免重复执行流程:
public class PlanningConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { return new Constraint[] { calculatePoint1(constraintFactory), calculatePoint2(constraintFactory), calculatePoint3(constraintFactory) // ... 其他point对应的约束 }; } // 提取共享的聚合逻辑:生成计算实例的流 private UniConstraintStream<PointsCalculateInstance> getCalculationInstanceStream(ConstraintFactory factory) { return factory.from(Request.class) .groupBy( Collectors.toList(), (request) -> factory.from(Stock.class).collect(Collectors.toList()), (request) -> factory.from(Task.class).filter(task -> task.getMachine() != null).collect(Collectors.toList()) ) .map((requestList, stockList, taskList) -> new PointsCalculateInstance(requestList, stockList, taskList)); } // Point1:软约束扣分 private Constraint calculatePoint1(ConstraintFactory factory) { return getCalculationInstanceStream(factory) .filter(instance -> instance.getPoint1() > 0) .penalize("Point1 penalty", HardMediumSoftScore.ONE_SOFT, PointsCalculateInstance::getPoint1); } // Point2:软约束扣分 private Constraint calculatePoint2(ConstraintFactory factory) { return getCalculationInstanceStream(factory) .filter(instance -> instance.getPoint2() > 0) .penalize("Point2 penalty", HardMediumSoftScore.ONE_SOFT, PointsCalculateInstance::getPoint2); } // Point3:硬约束扣分 private Constraint calculatePoint3(ConstraintFactory factory) { return getCalculationInstanceStream(factory) .filter(instance -> instance.getPoint3() > 0) .penalize("Point3 penalty", HardMediumSoftScore.ONE_HARD, PointsCalculateInstance::getPoint3); } // ... 其他point对应的约束方法,统一复用getCalculationInstanceStream的逻辑 }
关键说明
- 避免重复计算:通过
getCalculationInstanceStream方法共享集合聚合与实例计算逻辑,所有约束仅执行一次核心计算流程,兼顾性能与代码简洁性。 - 贴合流式编程风格:Timefold Constraint Stream无需插入逻辑对象,直接通过聚合+映射生成计算实例,更符合Java开发习惯。
- 满足链式任务需求:在
PointsCalculateInstance的calculateAllPoints方法中,可直接按任务链式顺序遍历处理,完全适配库存消耗利用率的计算需求。
内容的提问来源于stack exchange,提问作者heyif
相关产品推荐
相关产品推荐

