如何通过SolverConfigOverride让用户自定义Timefold求解器各阶段终止时长?
解决方案与最佳实践
一、实现阶段时长自定义覆盖
要实现按用户设定总时长分配各阶段运行时间,你需要通过SolverConfigOverride不仅覆盖全局终止规则,还要针对性配置构造启发式(CH)和每个局部搜索(LS)阶段的终止时长,同时保留XML中原有的算法参数配置。
步骤1:计算各阶段时长
先从用户输入获取总时长,再按比例计算各阶段的分钟数:
// 示例:从用户输入获取总时长,替换为实际获取逻辑 Long userTotalMinutes = 305L; // 构造启发式占总时长10% Long chMinutes = Math.round(userTotalMinutes * 0.1); // 每个局部搜索阶段占总时长45% Long eachLsMinutes = Math.round(userTotalMinutes * 0.45);
步骤2:构造覆盖配置
在SolverConfigOverride中配置全局终止、CH阶段和两个LS阶段的终止规则,同时保留XML中的算法参数(如acceptor、forager):
SolverConfigOverride<Timetable> configOverride = new SolverConfigOverride<Timetable>() // 全局终止:确保总运行时长不超过用户设定值 .withTerminationConfig(new TerminationConfig().withMinutesSpentLimit(userTotalMinutes)) // 配置构造启发式阶段的终止时长及原有配置 .withConstructionHeuristicConfig(new ConstructionHeuristicConfig() .withTerminationConfig(new TerminationConfig().withMinutesSpentLimit(chMinutes)) .withConstructionHeuristicType(ConstructionHeuristicType.ALLOCATE_ENTITY_FROM_QUEUE) .withEntitySorterManner(EntitySorterManner.DECREASING_DIFFICULTY_IF_AVAILABLE) .withValueSorterManner(ValueSorterManner.DECREASING_STRENGTH_IF_AVAILABLE)) // 配置两个局部搜索阶段的终止时长及原有算法参数 .withLocalSearchConfigList(List.of( new LocalSearchConfig() .withTerminationConfig(new TerminationConfig().withMinutesSpentLimit(eachLsMinutes)) .withAcceptorConfig(new AcceptorConfig().withLateAcceptanceSize(900)) .withForagerConfig(new ForagerConfig().withAcceptedCountLimit(10)), new LocalSearchConfig() .withTerminationConfig(new TerminationConfig().withMinutesSpentLimit(eachLsMinutes)) .withAcceptorConfig(new AcceptorConfig().withEntityTabuSize(10)) .withForagerConfig(new ForagerConfig().withAcceptedCountLimit(1200)) ));
步骤3:应用覆盖配置
将上述配置传入solveBuilder,替换原有的withConfigOverride逻辑:
solverManager.solveBuilder() .withProblemId(jobId) .withConfigOverride(configOverride) .withProblemFinder(jobId_ -> jobIdToJob.get(jobId).timetable) .withBestSolutionConsumer(solution -> jobIdToJob.put(jobId, Job.ofTimetable(solution))) .withExceptionHandler((jobId_, exception) -> { jobIdToJob.put(jobId, Job.ofException(exception)); LOGGER.error("Failed solving jobId ({}).", jobId, exception); }) .run();
二、XML配置最佳实践:是否需要给构造启发式设置耗时限制?
默认推荐:不需要手动设置
构造启发式的核心目标是快速生成可行初始解,通常几秒到几分钟就能完成(取决于问题规模)。让求解器自动终止CH(即所有实体分配完成后结束),能保证初始解的质量,为后续局部搜索打下更好的基础,最终得到更优的结果。特殊场景:建议设置
如果你的总时间预算非常紧张(比如总时长仅几分钟),或者CH在极端场景下运行时间过长,这时设置CH的时间限制是合理的,避免它占用过多时间导致局部搜索没有足够时长优化解。配置建议
XML中保留基础的阶段结构和算法参数(如CH类型、LS的acceptor/forager配置),把动态调整的参数(如终止时长)放到代码中通过SolverConfigOverride覆盖,这样既保证算法稳定性,又能灵活响应用户需求。
内容的提问来源于stack exchange,提问作者Diallo Francis Patrick
相关产品推荐
相关产品推荐

