如何为TimeFold求解器实现无1%分数提升即终止的动态逻辑
TimeFold 实现连续无分数提升终止器指引
核心思路
实现自定义终止器,跟踪最佳分数变化:当连续30秒内分数提升未达1%时触发求解终止;若分数提升达标,则重置计时继续计算。如需保留原30秒总时长限制,可将两个终止器组合为OR逻辑。
步骤1:编写自定义终止器类
创建ScoreImprovementThresholdTermination类,继承AbstractTermination处理分数跟踪与终止判断:
import org.optaplanner.core.api.score.hardsoft.HardSoftScore; import org.optaplanner.core.impl.termination.AbstractTermination; import org.optaplanner.core.impl.solver.scope.SolverScope; import java.time.Duration; public class ScoreImprovementThresholdTermination extends AbstractTermination<EmployeeSchedule> { // 配置参数:连续无提升时长、分数提升阈值(1%即0.01) private Duration noImprovementSpentLimit; private double scoreImprovementThreshold; // 状态变量:最后一次分数提升时间、当前最佳分数 private long lastImprovementTimeMillis; private HardSoftScore bestScore; @Override public void solvingStarted(SolverScope<EmployeeSchedule> solverScope) { super.solvingStarted(solverScope); // 初始化求解开始状态 lastImprovementTimeMillis = System.currentTimeMillis(); bestScore = (HardSoftScore) solverScope.getBestScore(); } @Override public boolean isTerminated(SolverScope<EmployeeSchedule> solverScope) { HardSoftScore currentBestScore = (HardSoftScore) solverScope.getBestScore(); long currentTimeMillis = System.currentTimeMillis(); // 判断分数是否有1%以上提升(示例以软分数为优化目标,可按需调整) boolean hasQualifiedImprovement = false; int currentSoft = currentBestScore.getSoftScore(); int bestSoft = bestScore.getSoftScore(); if (currentSoft > bestSoft) { double improvementRate = (double) (currentSoft - bestSoft) / Math.abs(bestSoft); if (improvementRate >= scoreImprovementThreshold) { hasQualifiedImprovement = true; } } // 更新状态或判断终止 if (hasQualifiedImprovement) { lastImprovementTimeMillis = currentTimeMillis; bestScore = currentBestScore; return false; // 继续求解 } else { long timeElapsed = currentTimeMillis - lastImprovementTimeMillis; return timeElapsed >= noImprovementSpentLimit.toMillis(); } } // 配置参数注入方法(TimeFold自动绑定配置值) public void setNoImprovementSpentLimit(Duration noImprovementSpentLimit) { this.noImprovementSpentLimit = noImprovementSpentLimit; } public void setScoreImprovementThreshold(double scoreImprovementThreshold) { this.scoreImprovementThreshold = scoreImprovementThreshold; } }
步骤2:配置终止器(两种方式选其一)
方式1:编程式配置(灵活可控)
创建Solver配置类,注册自定义终止器并组合原有总时长限制:
import io.quarkus.timefold.solver.SolverConfigurer; import org.optaplanner.core.config.solver.SolverConfig; import org.optaplanner.core.config.termination.TerminationConfig; import org.optaplanner.core.config.termination.TerminationCompositionStyle; import jakarta.enterprise.context.ApplicationScoped; import java.util.List; @ApplicationScoped public class EmployeeScheduleSolverConfigurer implements SolverConfigurer<EmployeeSchedule> { @Override public void configure(SolverConfig solverConfig) { // 组合终止器:总时长30秒 OR 连续30秒无1%分数提升 TerminationConfig combinedTermination = new TerminationConfig() .withTerminationCompositionStyle(TerminationCompositionStyle.OR) .withTerminationList(List.of( new TerminationConfig().withSpentLimit("30s"), new TerminationConfig() .withTerminationClass(ScoreImprovementThresholdTermination.class) .withParameter("noImprovementSpentLimit", "30s") .withParameter("scoreImprovementThreshold", "0.01") )); solverConfig.setTerminationConfig(combinedTermination); } }
方式2:配置文件(application.properties)
直接通过配置绑定自定义终止器:
# 可选:保留总时长限制 quarkus.timefold.solver.termination.spent-limit=30s quarkus.timefold.solver.termination.composition-style=OR # 自定义终止器配置 quarkus.timefold.solver.termination.termination-class=com.yourpackage.ScoreImprovementThresholdTermination quarkus.timefold.solver.termination.no-improvement-spent-limit=30s quarkus.timefold.solver.termination.score-improvement-threshold=0.01
关键注意事项
- 分数类型适配:若使用
SimpleScore、HardMediumSoftScore等其他分数类型,需修改代码中的分数转换与计算逻辑。 - 提升率逻辑调整:示例以软分数为优化目标,若业务关注硬分数或综合分数,可修改
isTerminated方法中的分数对比逻辑。 - 组合终止器逻辑:使用
OR时任一条件满足即终止;需同时满足两个条件才终止可改为AND。
内容的提问来源于stack exchange,提问作者I. Ahmed
相关产品推荐
相关产品推荐

