2.7 分数计算

这是我参与更文挑战的第10天,活动详情查看: 更文挑战

内容概要

本章我们来学习的又是核心的一个内容,如何编写你的约束规则和分数,这块的难度不大,但是如果要掌握,坦率的说很困难,不过大家不要灰心,随着时间的推移模型学习的越来越多这方面会积累的很快。

分数计算的方式

支持以下几种方式:

  • Easy Java score calculation:简单的Java分数计算,在Java的一个方法中一起实现所有的约束。可扩展性差。
  • Constraint streams score calculation:约束流分数计算,在Java中把每个约束作为一个单独的ConstraintStream来实现。快速且可扩展。
  • Incremental Java score calculation :递增的Java分数计算(不推荐)。在Java(或另一种JVM语言)中实现多个低级方法。快速和可扩展。非常难以实现和维护。
  • Drools score calculation:Drools分数计算。在DRL中把每个约束条件作为一个单独的分数规则来实现。可扩展,推荐。

每个分数计算类型都可以与任何分数定义(如HardSoftScoreHardMediumSoftScore)一起使用。所有的分数计算类型都是面向对象的,可以重复使用现有的Java代码。

分数计算必须是只读的。它不能以任何方式改变规划实体或问题事实。例如,它不能在分数计算中调用规划实体的setter方法。

要在规划变量变化时更新规划实体,可使用影子变量来代替。(后续会讲解到)

简单的Java分数计算

用Java实现你的分数计算的简单方法。

  • 优势:
    • 没有学习曲线
  • 缺点:
    • 速度较慢
    • 不能扩展,因为没有增量的分数计算。

实现接口EasyScoreCalculator的一个方法:

public interface EasyScoreCalculator<Solution_, Score_ extends Score<Score_>> {

    Score_ calculateScore(Solution_ solution);

}
复制代码

例如课程表的例子:

public class TimeTableEasyScoreCalculator
    implements EasyScoreCalculator<TimeTable, HardSoftScore> {

    @Override
    public HardSoftScore calculateScore(TimeTable timeTable) {
        List<Lesson> lessonList = timeTable.getLessonList();
        int hardScore = 0;
        for (Lesson a : lessonList) {
            for (Lesson b : lessonList) {
                if (a.getTimeslot() != null && a.getTimeslot().equals(b.getTimeslot())
                        && a.getId() < b.getId()) {
                    // 一个房间最多可以同时安排一节课。
                    if (a.getRoom() != null && a.getRoom().equals(b.getRoom())) {
                        hardScore--;
                    }
                    // 一个教师在同一时间最多可以教一门课
                    if (a.getTeacher().equals(b.getTeacher())) {
                        hardScore--;
                    }
                    // 一个学生在同一时间最多只能上一节课
                    if (a.getStudentGroup().equals(b.getStudentGroup())) {
                        hardScore--;
                    }
                }
            }
        }
        int softScore = 0;
        return HardSoftScore.of(hardScore, softScore);
    }

}
复制代码

在xml内配置:

  <scoreDirectorFactory>
    <easyScoreCalculatorClass>org.optaplanner.examples.nqueens.optional.score.NQueensEasyScoreCalculator</easyScoreCalculatorClass>
  </scoreDirectorFactory>
复制代码

如果需要在求解器配置中动态地配置EasyScoreCalculator的值(以便基准测试器可以调整这些参数),请添加easyScoreCalculatorCustomProperties元素并使用自定义属性。

<scoreDirectorFactory>
    <easyScoreCalculatorClass>...MyEasyScoreCalculator</easyScoreCalculatorClass>
    <easyScoreCalculatorCustomProperties>
      <property name="myCacheSize" value="1000" />
    </easyScoreCalculatorCustomProperties>
  </scoreDirectorFactory>
复制代码

递增的Java分数计算

一种在Java中逐步实现你的分数计算的方法。

  • 优点:
    • 非常快且可扩展
    • 如果编写得当,目前是最快的
  • 缺点:
    • 难以编写
      • 一个可扩展的实现大量使用maps、indexs…(使用Drools规则引擎最方便)。
      • 必须自己学习、设计、编写和改进所有这些性能优化。
    • 难以阅读
      • 定期的分数约束变化会导致很高的维护成本

实现接口IncrementalScoreCalculator的所有方法:

public interface IncrementalScoreCalculator<Solution_, Score_ extends Score<Score_>> {

    void resetWorkingSolution(Solution_ workingSolution);

    void beforeEntityAdded(Object entity);

    void afterEntityAdded(Object entity);

    void beforeVariableChanged(Object entity, String variableName);

    void afterVariableChanged(Object entity, String variableName);

    void beforeEntityRemoved(Object entity);

    void afterEntityRemoved(Object entity);

    Score_ calculateScore();

}
复制代码

这个方式的实现,我们在这里就不多讲了,内容很深也很复杂,一是实现起来非常的困难,而来可维护性很差,是不受推荐的一种方式,大家了解一下就可以了。

总结

这一篇章主要讲解下,OptaPlanner中几种分数计算的方式,以及优点和缺点。今天只是让大家对分数计算有一个理解,后续会单独将这四种方式的实现,因为每一个实现都能讲很多天。

结束语

下一篇章,我们将来学习提高分数计算性能的一些窍门。

创作不易,禁止未授权的转载。如果我的文章对您有帮助,就请点赞/收藏/关注鼓励支持一下吧??????

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享