这是我参与更文挑战的第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中把每个约束条件作为一个单独的分数规则来实现。可扩展,推荐。
每个分数计算类型都可以与任何分数定义(如HardSoftScore
或HardMediumSoftScore
)一起使用。所有的分数计算类型都是面向对象的,可以重复使用现有的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中几种分数计算的方式,以及优点和缺点。今天只是让大家对分数计算有一个理解,后续会单独将这四种方式的实现,因为每一个实现都能讲很多天。
结束语
下一篇章,我们将来学习提高分数计算性能的一些窍门。
创作不易,禁止未授权的转载。如果我的文章对您有帮助,就请点赞/收藏/关注鼓励支持一下吧??????