TimeFold员工调度:正确构建SolverManager及动态终止求解器咨询
问题描述
我正在定制TimeFold员工调度示例,原EmployeeScheduleResource类通过构造函数注入SolverManager对象,核心代码如下:
public class EmployeeScheduleResource { SolverManager<EmployeeSchedule, String> solverManager; SolutionManager<EmployeeSchedule, HardMediumSoftScore> solutionManager; @Inject public EmployeeScheduleResource(SolverManager<EmployeeSchedule, String> solverManager,SolutionManager<EmployeeSchedule, HardMediumSoftScore> solutionManager) { this.solverManager = solverManager; this.solutionManager = solutionManager; } }
现需SolverManager加载XML配置文件中的终止规则:
<termination> <unimprovedSecondsSpentLimit>30</unimprovedSecondsSpentLimit> <unimprovedScoreDifferenceThreshold>0hard/0medium/1soft</unimprovedScoreDifferenceThreshold> </termination>
我当前自行创建SolverManager的方式并不恰当,恳请提供正确实现方案。
另外,能否使用AbstractTermination<EmployeeRoster>类处理终止逻辑?此前在Optaplanner中可通过扩展该类实现自定义逻辑,但固定配置无法在运行时计算得分提升率,我需要根据一段时间内得分未提升的比例来判定终止时机。
解决方案
一、正确加载XML终止配置的SolverManager实现
TimeFold推荐通过依赖注入框架(如Spring Boot、Quarkus)管理SolverManager,确保配置自动加载;无DI场景下也可手动读取配置初始化。
1. 依赖注入场景(以Spring Boot为例)
- 将完整的Solver XML配置文件(如
employeeScheduleSolverConfig.xml)放在src/main/resources目录下,终止规则需作为<solver>节点的子节点存在 - 直接通过构造函数注入SolverManager,DI框架会自动读取XML中的终止规则:
@RestController public class EmployeeScheduleResource { private final SolverManager<EmployeeSchedule, String> solverManager; private final SolutionManager<EmployeeSchedule, HardMediumSoftScore> solutionManager; public EmployeeScheduleResource(SolverManager<EmployeeSchedule, String> solverManager, SolutionManager<EmployeeSchedule, HardMediumSoftScore> solutionManager) { this.solverManager = solverManager; this.solutionManager = solutionManager; } @PostMapping("/solve") public ResponseEntity<SolverJob<EmployeeSchedule, String>> solve(@RequestBody EmployeeSchedule problem) { SolverJob<EmployeeSchedule, String> job = solverManager.solve(UUID.randomUUID().toString(), problem); return ResponseEntity.accepted().body(job); } }
2. 手动创建场景(无DI框架)
显式加载SolverConfig并关联XML配置,确保终止规则被读取:
public class EmployeeScheduleResource { private final SolverManager<EmployeeSchedule, String> solverManager; private final SolutionManager<EmployeeSchedule, HardMediumSoftScore> solutionManager; public EmployeeScheduleResource() { // 读取XML配置文件,自动加载终止规则 SolverConfig solverConfig = SolverConfig.createFromXmlResource("employeeScheduleSolverConfig.xml"); SolverFactory<EmployeeSchedule> solverFactory = SolverFactory.create(solverConfig); this.solverManager = SolverManager.create(solverFactory); this.solutionManager = SolutionManager.create(solverFactory); } }
完整XML配置示例:
<?xml version="1.0" encoding="UTF-8"?> <solver xmlns="https://timefold.ai/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://timefold.ai/xsd/solver https://timefold.ai/xsd/solver/solver.xsd"> <solutionClass>com.yourpackage.EmployeeSchedule</solutionClass> <entityClass>com.yourpackage.EmployeeShiftAssignment</entityClass> <!-- 终止规则配置 --> <termination> <unimprovedSecondsSpentLimit>30</unimprovedSecondsSpentLimit> <unimprovedScoreDifferenceThreshold>0hard/0medium/1soft</unimprovedScoreDifferenceThreshold> </termination> <!-- 其他配置(评分器、启发式算法等) --> </solver>
二、自定义AbstractTermination实现动态终止逻辑
可以通过扩展AbstractTermination<EmployeeSchedule>(泛型需匹配你的解决方案类)实现基于得分提升率的动态终止,TimeFold完全兼容OptaPlanner的自定义终止逻辑。
1. 自定义Termination类
public class ScoreImprovementRateTermination extends AbstractTermination<EmployeeSchedule> { private final long checkWindowSeconds; private final double minImprovementRateThreshold; private Score initialScoreInWindow; private long windowStartTimeMillis; public ScoreImprovementRateTermination(long checkWindowSeconds, double minImprovementRateThreshold) { this.checkWindowSeconds = checkWindowSeconds; this.minImprovementRateThreshold = minImprovementRateThreshold; } @Override public boolean isTerminated(ScoreDirector<EmployeeSchedule> scoreDirector) { EmployeeSchedule solution = scoreDirector.getWorkingSolution(); Score currentScore = scoreDirector.calculateScore(); long currentTimeMillis = System.currentTimeMillis(); if (initialScoreInWindow == null) { initialScoreInWindow = currentScore; windowStartTimeMillis = currentTimeMillis; return false; } if (currentTimeMillis - windowStartTimeMillis >= checkWindowSeconds * 1000L) { int initialSoftScore = initialScoreInWindow.getSoftScore(); int currentSoftScore = currentScore.getSoftScore(); // 计算得分提升率,可按需调整计算逻辑 double improvementRate = initialSoftScore != 0 ? (double) (currentSoftScore - initialSoftScore) / Math.abs(initialSoftScore) : 0.0; boolean shouldTerminate = improvementRate < minImprovementRateThreshold; // 重置窗口,持续监控后续求解阶段 initialScoreInWindow = currentScore; windowStartTimeMillis = currentTimeMillis; return shouldTerminate; } return false; } @Override public double calculateTimeGradient(ScoreDirector<EmployeeSchedule> scoreDirector) { // 按需实现时间梯度(用于进度展示等场景) return 0.0; } }
2. 关联自定义Termination到SolverConfig
方式1:XML配置关联
替换XML中的<termination>节点为自定义类:
<termination class="com.yourpackage.ScoreImprovementRateTermination"> <checkWindowSeconds>60</checkWindowSeconds> <minImprovementRateThreshold>0.05</minImprovementRateThreshold> </termination>
方式2:代码动态配置
SolverConfig solverConfig = SolverConfig.createFromXmlResource("employeeScheduleSolverConfig.xml"); solverConfig.setTerminationConfig(new TerminationConfig() .withTerminationClass(ScoreImprovementRateTermination.class) .withTerminationProperty("checkWindowSeconds", 60) .withTerminationProperty("minImprovementRateThreshold", 0.05)); SolverFactory<EmployeeSchedule> solverFactory = SolverFactory.create(solverConfig); SolverManager<EmployeeSchedule, String> solverManager = SolverManager.create(solverFactory);
内容的提问来源于stack exchange,提问作者I. Ahmed
相关产品推荐
相关产品推荐

