You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:45:03