Optaplanner规划时错误分配规划实体问题及解决方法
销售人员预约分配的OptaPlanner可行解处理方案
问题背景
业务场景为将销售人员分配至预约列表,需考虑销售人员的行程移动,采用OptaPlanner实现调度功能。现有硬约束可正常检查同一销售人员同一时间最多只能有一个预约,但当不存在可行分配方案时,规划器仍会为预约随机分配销售人员,导致最终方案无法使用。需求明确:若某预约无可用销售人员可分配,则不为该预约分配任何人。
原有冲突约束代码
Constraint repConflict(ConstraintFactory constraintFactory) { // 同一销售人员同一时间最多只能有一个预约 return constraintFactory // 选取每一对不同的预约... .forEachUniquePair(Appointment.class, Joiners.equal(Appointment::getRepUuid)) .filter((appt1, appt2) ->{ if(appt1.getStartTime().before(appt2.getStartTime()) && appt1.getEndTime().before(appt2.getStartTime()) && appt1.getStartTime().before(appt2.getEndTime()) && appt1.getEndTime().before(appt2.getEndTime())) { return false; } return true; }) // 每出现一对冲突就施加硬惩罚 .penalize(HardSoftScore.ONE_HARD) .asConstraint("SalesRep conflict"); }
尝试过的无效配置
曾修改SolverFactory配置,设置终止条件为找到可行解,但未解决问题:
SolverFactory<RepRoutingSolution> solverFactory = SolverFactory.create(new SolverConfig() .withSolutionClass(RepRoutingSolution.class) .withEntityClasses(Appointment.class) .withConstraintProviderClass(RepSchedulerConstraintProvider.class) .withTerminationConfig(new TerminationConfig() .withBestScoreFeasible(true) ) // 小数据集下仅运行5秒,建议实际场景至少运行5分钟("5m") .withTerminationSpentLimit(Duration.ofSeconds(5))); // 加载问题数据 RepRoutingSolution problem = generateDemoData(); // 求解 Solver<RepRoutingSolution> solver = solverFactory.buildSolver(); RepRoutingSolution solution = solver.solve(problem); // 可视化结果 printRepVisits(solution);
有效解决方法:新增奖励约束
新增一个奖励约束后,问题得到解决。该约束对同一销售人员名下无时间冲突的预约组合给予中等奖励,引导规划器优先保留可行的分配关系,对于无法找到可行分配的预约,规划器会自动不分配销售人员:
Constraint repRewardForAppointment(ConstraintFactory constraintFactory) { return constraintFactory // 选取每一对不同的预约... .forEachUniquePair(Appointment.class, Joiners.equal(Appointment::getRepUuid)) .filter((appt1, appt2) -> { if (appt1.getStartTime().before(appt2.getStartTime()) && appt1.getEndTime().before(appt2.getStartTime()) && appt1.getStartTime().before(appt2.getEndTime()) && appt1.getEndTime().before(appt2.getEndTime())) { return true; } return false; }) // 每出现一对无冲突的组合就给予中等奖励 .reward(HardMediumSoftScore.ONE_MEDIUM) .asConstraint("SalesRep reward for Appointments"); }
内容的提问来源于stack exchange,提问作者user101
相关产品推荐
相关产品推荐

