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

如何记录OptaPlanner求解历史?获取Top N最优解方法咨询

问题原因分析

你遇到的SolverEventListener仅捕获2条记录的核心原因是:OptaPlanner默认的BestSolutionRecaller仅在新解的得分严格优于当前已记录的最优解时,才会触发bestSolutionChanged事件。调试日志中的移动操作大多是求解过程中的试探性优化,并未产生比当前最优更优的解;或是10秒内早期就锁定了最优解,后续移动无法突破得分上限,因此事件触发次数极少。

实现Top N最优解的两种方案

方案一:基于SolverEventListener+自定义Top N列表

通过配置BestSolutionRecaller放宽触发条件,结合自定义监听器维护有序的Top N解集合:

  1. 修改Solver配置,允许记录与最优解得分接近的候选解(根据你的得分类型调整阈值):
<solver>
    <!-- 其他配置(终止策略、求解器算法等) -->
    <bestSolutionRecaller>
        <!-- 设置得分差值阈值,例如允许比最优解差100的解触发事件 -->
        <bestSolutionScoreDifferenceThreshold>100</bestSolutionScoreDifferenceThreshold>
    </bestSolutionRecaller>
</solver>
  1. 自定义监听器实现Top N收集:
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverEventListener;
import org.optaplanner.core.api.solver.event.BestSolutionChangedEvent;

import java.util.PriorityQueue;
import java.util.List;
import java.util.stream.Collectors;

public class TopNSolutionListener<Solution_> implements SolverEventListener<Solution_> {

    private final int topN;
    private final PriorityQueue<Solution_> topSolutions;
    private final java.util.Comparator<Solution_> solutionComparator;

    public TopNSolutionListener(int topN, java.util.Comparator<Solution_> solutionComparator) {
        this.topN = topN;
        this.solutionComparator = solutionComparator;
        // 优先队列:头部为当前Top N中最差的解,方便溢出时移除
        this.topSolutions = new PriorityQueue<>(topN, solutionComparator);
    }

    @Override
    public void bestSolutionChanged(BestSolutionChangedEvent<Solution_> event) {
        Solution_ newSolution = event.getNewBestSolution();
        // 必须克隆解,避免OptaPlanner后续修改覆盖已记录的解
        Solution_ clonedSolution = event.getSolver().getSolutionManager().cloneSolution(newSolution);

        if (topSolutions.size() < topN) {
            topSolutions.add(clonedSolution);
        } else {
            // 若新解比队列中最差的解更优,则替换
            if (solutionComparator.compare(clonedSolution, topSolutions.peek()) > 0) {
                topSolutions.poll();
                topSolutions.add(clonedSolution);
            }
        }
    }

    // 获取从最优到最差排序的Top N解
    public List<Solution_> getTopNSolutions() {
        return topSolutions.stream()
                .sorted(solutionComparator.reversed())
                .collect(Collectors.toList());
    }
}
  1. 使用监听器:
// 构建求解器
Solver<MySolution> solver = SolverFactory.create(solverConfig).buildSolver();
// 实例化监听器(以Top 10为例,按得分降序比较)
TopNSolutionListener<MySolution> topNListener = new TopNSolutionListener<>(10,
        (sol1, sol2) -> sol1.getScore().compareTo(sol2.getScore()));
solver.addEventListener(topNListener);

// 启动求解
solver.solve(initialSolution);

// 获取Top 10最优解
List<MySolution> top10Solutions = topNListener.getTopNSolutions();

方案二:基于StepListener收集所有候选解

如果你需要更全面的候选解(每个求解步骤的解都纳入筛选),可以使用StepListener监听每个步骤的结束事件,再筛选Top N:

import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.impl.solver.event.StepListenerAdapter;
import org.optaplanner.core.impl.solver.scope.StepScope;

import java.util.PriorityQueue;
import java.util.List;
import java.util.stream.Collectors;

public class TopNStepListener<Solution_> extends StepListenerAdapter<Solution_> {

    private final int topN;
    private final PriorityQueue<Solution_> topSolutions;
    private final java.util.Comparator<Solution_> solutionComparator;
    private final Solver<Solution_> solver;

    public TopNStepListener(int topN, java.util.Comparator<Solution_> solutionComparator, Solver<Solution_> solver) {
        this.topN = topN;
        this.solutionComparator = solutionComparator;
        this.solver = solver;
        this.topSolutions = new PriorityQueue<>(topN, solutionComparator);
    }

    @Override
    public void stepEnded(StepScope<Solution_> stepScope) {
        Solution_ currentSolution = stepScope.getWorkingSolution();
        Solution_ clonedSolution = solver.getSolutionManager().cloneSolution(currentSolution);

        if (topSolutions.size() < topN) {
            topSolutions.add(clonedSolution);
        } else {
            if (solutionComparator.compare(clonedSolution, topSolutions.peek()) > 0) {
                topSolutions.poll();
                topSolutions.add(clonedSolution);
            }
        }
    }

    public List<Solution_> getTopNSolutions() {
        return topSolutions.stream()
                .sorted(solutionComparator.reversed())
                .collect(Collectors.toList());
    }
}

使用方式:

Solver<MySolution> solver = SolverFactory.create(solverConfig).buildSolver();
TopNStepListener<MySolution> topNListener = new TopNStepListener<>(10,
        (sol1, sol2) -> sol1.getScore().compareTo(sol2.getScore()), solver);
solver.addStepListener(topNListener);

solver.solve(initialSolution);
List<MySolution> top10Solutions = topNListener.getTopNSolutions();

关键注意点

  • 必须克隆解:OptaPlanner在求解过程中会复用同一个Solution实例进行修改,若不克隆,后续的优化操作会覆盖已记录的解。
  • 得分比较:确保你的Solution类能通过getScore()获取有效得分,且比较器逻辑符合你的优化目标(如最大化/最小化得分)。

内容的提问来源于stack exchange,提问作者Zhetao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:20:55