这是我参与更文挑战的第4天,活动详情查看: 更文挑战
内容概要
前面两个篇章,我们学习了两个例子,高中课程表以及云资源优化的问题。相信大家对OptaPlanner是什么,能解决什么有一个直观的感受。那在初步了解了这些之后,我们将来学习一下OptaPlanner求解时的步骤,它都做了什么。
概述
使用OptaPlanner解决一个Planning Problem规划问题包括以下步骤:
- 问题建模:将你的规划问题建模为一个带有
@PlanningSolution注解的类,例如CloudBalance类。 - 配置求解器:配置一个求解器,例如任何
CloudBalance实例的First Fit和Tabu Search求解器(不同的求解器对不同的规划问题,其性能和结果有着很大的差别,后续再讲解如何选择适合的求解器)。 - 组装问题数据:从数据层加载一个问题数据集,例如一个
CloudBalance实例。这就是规划问题。 - 调用求解器:用
Solver.solve(problem)来解决这个问题,返回找到的最佳解决方案。
求解器配置
求解器的配置OptaPlanner支持xml形式和API形式的配置。
XML配置
用SolverFactory构建一个Solver实例。用一个作为classpath资源(由ClassLoader.getResource()定义)提供的Solver配置XML文件配置SolverFactory。
SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource(
"org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
Solver<NQueens> solver = solverFactory.buildSolver();
在一个的项目中(遵循Maven目录结构),solverConfig XML文件位于$PROJECT_DIR/src/main/resources/org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml。
另外,也可以用SolverFactory.createFromXmlFile()从文件中创建一个SolverFactory。但是出于可移植性的考虑,推荐使用classpath资源。
Solver和SolverFactory都有一个叫做Solution_的通用类型,它是代表规划问题和解决方案的类,例如CloudBalance类。
一个求解器配置的XML文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">
<!-- 定义模型 -->
<solutionClass>org.optaplanner.examples.nqueens.domain.NQueens</solutionClass>
<entityClass>org.optaplanner.examples.nqueens.domain.Queen</entityClass>
<!-- 定义约束规则文件 -->
<scoreDirectorFactory>
<scoreDrl>org/optaplanner/examples/nqueens/solver/nQueensConstraints.drl</scoreDrl>
</scoreDirectorFactory>
<!-- 配置不同的参数和优化算法 -->
<termination>
...
</termination>
<constructionHeuristic>
...
</constructionHeuristic>
<localSearch>
...
</localSearch>
</solver>
注意其中的三个部分:
- 定义模型
- 定义约束规则或者分数计算类
- 可选择配置优化算法 后续会进一步解释配置的这些不同部分。
通过API配置
求解器配置也可以通过SolverConfig API进行配置。这对于在运行时动态地改变一些值特别有用。例如,在构建求解器之前,根据系统属性来改变运行时间。
SolverConfig solverConfig = SolverConfig.createFromXmlResource(
"org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
solverConfig.withTerminationConfig(new TerminationConfig()
.withMinutesSpentLimit(userInput));
SolverFactory<NQueens> solverFactory = SolverFactory.create(solverConfig);
Solver<NQueens> solver = solverFactory.buildSolver();
求解器配置XML中的每个元素都可以作为一个Config类或包命名空间org.optaplanner.core.config中的Config类的属性。这些*Config类是XML格式的Java表示。它们构建了运行时组件(属于包命名空间org.optaplanner.core.impl),并将它们组装成一个高效的Solver。
例如,我们在学习CloudBalance例子中就是通过API的方式进行配置的。
// Build the Solver
SolverFactory<CloudBalance> solverFactory = SolverFactory.create(new SolverConfig()
.withSolutionClass(CloudBalance.class)
.withEntityClasses(CloudProcess.class)
.withConstraintProviderClass(CloudBalancingConstraintProvider.class)
// .withTerminationSpentLimit(Duration.ofMinutes(2)));
.withTerminationSpentLimit(Duration.ofSeconds(30)));
Solver<CloudBalance> solver = solverFactory.buildSolver();
OptPlanner注解
OptaPlanner需要知道业务模型中哪些类是PlanningEntity规划实体,哪些属性是PlanningVariable规划变量等等。有几种方法:
- 在业务模型上添加类注解和JavaBean属性注解(推荐)。属性注解必须是在
getter方法上,而不是在setter方法上,这样的话getter不需要是公共的。 - 在业务模型上添加类注解和字段注解,这样的话字段也不需要是公共的。
- 使用XML:在一个XML文件中配置。但是部分属性还未支持。 OptaPlanner使用过程中重点使用第一种方式,例如:
@PlanningSolution
@XStreamAlias("CloudBalance")
public class CloudBalance extends AbstractPersistable {
private List<CloudComputer> computerList;
private List<CloudProcess> processList;
@XStreamConverter(HardSoftScoreXStreamConverter.class)
private HardSoftScore score;
public CloudBalance() {
}
public CloudBalance(long id, List<CloudComputer> computerList, List<CloudProcess> processList) {
super(id);
this.computerList = computerList;
this.processList = processList;
}
@ValueRangeProvider(id = "computerRange")
@ProblemFactCollectionProperty
public List<CloudComputer> getComputerList() {
return computerList;
}
public void setComputerList(List<CloudComputer> computerList) {
this.computerList = computerList;
}
@PlanningEntityCollectionProperty
public List<CloudProcess> getProcessList() {
return processList;
}
public void setProcessList(List<CloudProcess> processList) {
this.processList = processList;
}
@PlanningScore
public HardSoftScore getScore() {
return score;
}
public void setScore(HardSoftScore score) {
this.score = score;
}
}
自定义属性配置
Solver配置,支持自定义属性。自定义属性对于通过Benchmarker调整动态值非常有用。例如:假设EasyScoreCalculator有大量的计算(这些计算是缓存的),你想在一个基准中增加缓存的大小。
<scoreDirectorFactory>
<easyScoreCalculatorClass>...MyEasyScoreCalculator</easyScoreCalculatorClass>
<easyScoreCalculatorCustomProperties>
<property name="myCacheSize" value="1000"/><!-- Override value -->
</easyScoreCalculatorCustomProperties>
</scoreDirectorFactory>
每个自定义属性添加一个public的setter方法,在构建Solver时调用。
public class MyEasyScoreCalculator extends EasyScoreCalculator<MySolution, SimpleScore> {
private int myCacheSize = 500; // Default value
@SuppressWarnings("unused")
public void setMyCacheSize(int myCacheSize) {
this.myCacheSize = myCacheSize;
}
}
自定义属性支持大多数数值类型,包括boolean、int、double、BigDecimal、String和enums)。
总结
这一篇章主要学习了OptaPlanner的求解步骤,以及求解器的主要配置和方法,这对我们学习OptaPlanner求解过程是很有帮助的。
作业
在之前的两个例子当中,我们使用的都是API的方式,大家可以换成xml的方式来进行学习,其实xml中的元素都有对应的API方法,只不过xml可读性比较合适,但是使用的时候建议使用API形式。
结束语
下一篇章,我们会着重讲解业务模型中的概念,ProblemFact、ProblemEntity、ProblemSolution等。
创作不易,禁止未授权的转载。如果我的文章对您有帮助,就请点赞/收藏/关注鼓励支持一下吧❤❤❤❤❤❤