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

如何在OptaPlanner中用EasyScoreCalculator记录扣分原因?

解决OptaPlanner中记录软分扣分说明的问题

核心问题分析

你之前的两种方案都存在OptaPlanner运行机制上的适配问题:

  • 实体属性存储:OptaPlanner在评估过程中会频繁克隆解决方案和实体,你的清空操作可能没对应到当前评估的方案实例,导致说明信息混乱或丢失。
  • 静态列表存储:OptaPlanner默认是多线程并行评估不同解决方案的,静态列表会被多个线程同时写入,必然混入其他非最优方案的信息。

正确实现方案

利用解决方案(Solution)类的自定义属性来存储当前方案的扣分说明,结合EasyScoreCalculator的无状态评估逻辑,确保每次评估只针对当前方案记录信息。

步骤1:修改Solution类

给你的Solution类添加扣分说明列表,并确保克隆时能正确复制该属性(OptaPlanner评估时会克隆方案):

public class TimetableSolution implements Solution<HardSoftScore> {
    // 原有属性(如lessonList、teacherList等)...
    private List<String> scoreExplanations = new ArrayList<>();

    // Getter & Setter
    public List<String> getScoreExplanations() {
        return scoreExplanations;
    }

    public void setScoreExplanations(List<String> scoreExplanations) {
        this.scoreExplanations = scoreExplanations;
    }

    // 实现DeepCloneable接口,确保克隆时复制说明列表
    @Override
    public TimetableSolution deepClone() {
        TimetableSolution clone = new TimetableSolution();
        // 复制原有属性...
        clone.setLessonList(new ArrayList<>(this.getLessonList()));
        // 复制扣分说明列表(新ArrayList避免引用共享)
        clone.setScoreExplanations(new ArrayList<>(this.scoreExplanations));
        return clone;
    }
}

步骤2:修改EasyScoreCalculator

在每次评估开头清空当前方案的说明列表,触发扣分逻辑时直接写入当前方案的列表:

public class TimetableEasyScoreCalculator implements EasyScoreCalculator<TimetableSolution, HardSoftScore> {

    @Override
    public HardSoftScore calculateScore(TimetableSolution solution) {
        // 关键:每次评估前清空当前方案的说明,避免残留历史数据
        solution.getScoreExplanations().clear();

        int hardScore = 0;
        int softScore = 0;

        // 遍历实体执行自定义扣分逻辑
        for (Lesson lesson : solution.getLessonList()) {
            // 示例:检查任务紧随逻辑
            Lesson precedingLesson = findPrecedingLesson(solution, lesson);
            if (precedingLesson != null && "Y".equals(precedingLesson.getTaskName())) {
                int deduction = 5;
                softScore -= deduction;
                // 写入当前方案的说明列表
                solution.getScoreExplanations().add(
                    String.format("因任务%s紧随任务%s之后,扣除%d分",
                        precedingLesson.getTaskName(), lesson.getTaskName(), deduction)
                );
            }
        }

        return HardSoftScore.of(hardScore, softScore);
    }

    // 辅助方法:根据你的业务逻辑找到当前lesson的前置任务
    private Lesson findPrecedingLesson(TimetableSolution solution, Lesson lesson) {
        // 实现你的排序/查找逻辑(比如按时间、教室等维度)
        return null;
    }
}

步骤3:获取最优方案的扣分说明

当OptaPlanner求解完成后,直接从最优解决方案中提取说明:

Solver<TimetableSolution> solver = SolverFactory.createFromXmlResource("timetableSolverConfig.xml").buildSolver();
TimetableSolution bestSolution = solver.solve(initialSolution);

// 输出所有扣分说明
for (String explanation : bestSolution.getScoreExplanations()) {
    System.out.println(explanation);
}

进阶建议

如果后续性能要求较高,建议改用Constraint Streams API代替EasyScoreCalculator,它原生支持ConstraintMatch功能,无需手动维护说明列表,可直接通过ScoreManager获取每个约束的匹配详情和扣分原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:27