OptaPlanner优化算法优化算法
文章目录
- 1. 现实世界中的搜索空间大小
- 2. OptaPlanner能否找到最优解?
- 3. 架构概述
- 4. 优化算法概述
- 5. 我应该使用哪些优化算法?
- 6. 功率调整或默认参数值
- 7. 求解器阶段
- 8. 范围概述
- 9. 终止条件
- 10. SolverEventListener
- 11. 自定义求解器阶段
- 12. 无变化求解器阶段
- 13. 多线程求解
- 14.[参考链接](https://www.optaplanner.org/docs/optaplanner/latest/optimization-algorithms/optimization-algorithms.html#asynchronousTermination)
1. 现实世界中的搜索空间大小
对于规划问题来说,可能的解的数量令人难以置信。
例如:
-
四皇后问题有256个可能的解(4^4)和两个最优解。
-
五皇后问题有3125个可能的解(5^5)和一个最优解。
-
八皇后问题有16777216个可能的解(8^8)和92个最优解。
-
64皇后问题有超过10115个可能的解(6464)。
-
大多数现实生活中的规划问题都有巨大数量的可能解,但只有一个或少数几个最优解。
作为比较:已知宇宙中最小的原子数量是10^80。随着规划问题的增大,搜索空间往往会迅速扩大。添加一个额外的规划实体或规划值可能会大大增加某些算法的运行时间。
计算可能解的数量取决于领域模型的设计:
注意:此搜索空间大小计算包括不可行解(如果可以用模型表示),因为:
-
最优解可能是不可行的。
-
有许多类型的硬约束无法在公式中实际地结合起来。例如,在云平衡中,尝试将CPU容量约束纳入公式中。
即使在公式中实际上加入了一些硬约束的情况下(例如,课程排课),得到的搜索空间仍然非常庞大。
一个检查每个可能解的算法(即使进行修剪,如分支界限法)在单个现实生活中的规划问题上也可以运行数十亿年。目标是在可用时间内找到最佳解决方案。规划比赛(如国际排课比赛)表明,在现实世界的时间限制下,局部搜索变体(禁忌搜索、模拟退火、晚期接受等)通常表现最好。
2. OptaPlanner能否找到最优解?
企业希望获得最优解,但他们还有其他要求:
-
扩展:大型生产数据集不能崩溃,并且结果也要好。
-
优化正确的问题:约束条件必须与实际业务需求相匹配。
-
可用时间:必须在执行变得无用之前及时找到解决方案。
-
可靠性:每个数据集至少必须具有体面的结果(优于人类规划者)。
考虑到这些要求,尽管有些销售员的承诺,对于任何人或任何事物来说,找到最优解通常是不可能的。因此,OptaPlanner专注于在可用时间内找到最佳解决方案。在“现实独立的竞争”中,它通常是最好的可重用软件。
NP完全问题的性质使得扩展成为一个主要问题。
**注意:**从小数据集中得到的结果的质量无法说明从大数据集中得到的结果的质量。
规模问题不能通过以后购买硬件来缓解。尽快使用生产大小的数据集进行测试。不要根据小数据集评估质量(除非生产只遇到此类数据集)。相反,解决一个生产大小的数据集,并比较更长时间运行、不同算法以及(如果有的话)人工规划者的结果。
3. 架构概述
OptaPlanner是第一个将优化算法(元启发式算法等)与规则引擎(如Drools)结合使用的框架。这种组合非常高效,因为:
-
像Drools这样的规则引擎非常适合计算规划问题的解的得分。它可以轻松地添加额外的软约束或硬约束,而且具有良好的可扩展性。它可以进行增量得分计算(增量),而无需任何额外的代码。然而,它往往不适合找到新的解决方案。
-
优化算法非常擅长在不必要地穷尽每种可能性的情况下找到改进的解决方案。然而,它需要知道一个解决方案的得分,并且在高效计算得分方面没有提供支持。
4. 优化算法概述
OptaPlanner支持三类优化算法:穷举搜索、构造启发式算法和元启发式算法。在实践中,元启发式算法(结合构造启发式算法进行初始化)是推荐的选择:
这些算法类别中
每一类都有多个优化算法:
表1. 优化算法概述
算法 | 可扩展 | 最优解 | 易于使用 | 可调整 | 需要CH |
---|---|---|---|---|---|
穷举搜索(ES) | |||||
Brute Force | 0/5 | 5/5 | 5/5 | 0/5 | No |
Branch And Bound | 0/5 | 5/5 | 4/5 | 2/5 | No |
构造启发式算法(CH) | |||||
First Fit | 5/5 | 1/5 | 5/5 | 1/5 | No |
First Fit Decreasing | 5/5 | 2/5 | 4/5 | 2/5 | No |
Weakest Fit | 5/5 | 2/5 | 4/5 | 2/5 | No |
Weakest Fit Decreasing | 5/5 | 2/5 | 4/5 | 2/5 | No |
Strongest Fit | 5/5 | 2/5 | 4/5 | 2/5 | No |
Strongest Fit Decreasing | 5/5 | 2/5 | 4/5 | 2/5 | No |
Cheapest Insertion | 3/5 | 2/5 | 5/5 | 2/5 | No |
Regret Insertion | 3/5 | 2/5 | 5/5 | 2/5 | No |
元启发式算法(MH) | |||||
局部搜索(LS) | |||||
Hill Climbing | 5/5 | 2/5 | 4/5 | 3/5 | Yes |
Tabu Search | 5/5 | 4/5 | 3/5 | 5/5 | Yes |
Simulated Annealing | 5/5 | 4/5 | 2/5 | 5/5 | Yes |
Late Acceptance | 5/5 | 4/5 | 3/5 | 5/5 | Yes |
Great Deluge | 5/5 | 4/5 | 3/5 | 5/5 | Yes |
Step Counting Hill Climbing | 5/5 | 4/5 | 3/5 | 5/5 | Yes |
Variable Neighborhood Descent | 3/5 | 3/5 | 2/5 | 5/5 | Yes |
进化算法(EA) | |||||
Evolutionary Strategies | 3/5 | 3/5 | 2/5 | 5/5 | Yes |
Genetic Algorithms | 3/5 | 3/5 | 2/5 | 5/5 | Yes |
要了解有关元启发式算法的更多信息,请参阅《元启发式算法基础》或《聪明的算法》。
5. 我应该使用哪些优化算法?
最佳的优化算法配置取决于您的用例。然而,以下基本步骤提供了一个良好的起始配置,可以产生高于平均水平的结果。
-
首先使用快速配置,其中包含很少或没有配置和优化代码:参见首次适应法。
-
接下来,实现规划实体的难度比较,并将其转换为递减的首次适应法。
-
然后,在其后面添加晚期接受:
a.递减的首次适应法。
b.晚期接受。
此时,投资时间的回报降低,结果可能已经足够了。
**但是,**这可以在更低的投资回报率下改进。使用Benchmarker尝试一些不同的禁忌搜索、模拟退火和晚期接受配置,例如:
递减的首次适应法:禁忌搜索。
使用Benchmarker来改进大小参数的值。
还可以运行其他实验。例如,可以将以下多个算法组合在一起:
-
递减的首次适应法
-
晚期接受(相对较长时间)
-
禁忌搜索(相对较短时间)
6. 功率调整或默认参数值
许多优化算法具有影响结果和可扩展性的参数。OptaPlanner应用例外配置,因此所有优化算法都具有默认参数值。这与JVM中的垃圾回收参数非常相似:大多数用户无需调整它们,但高级用户经常需要。
默认参数值对于许多情况(尤其是原型)已经足够了,但如果开发时间允许,可以使用Benchmarker对特定用例进行功率调整,以获得更好的结果和可扩展性。每个优化算法的文档还声明了用于功率调整的高级配置。
参数的默认值会在次要版本之间更改,以便为大多数用户改进它们。高级配置可以用于防止不希望的更改,但不建议这样做。
7. 求解器阶段
求解器可以按照顺序使用多个优化算法。每个优化算法由一个求解器阶段表示。永远不会同时有多个阶段进行求解。
**注意:**一些阶段的实现可以结合多种优化算法的技术,但仍然只是一个阶段。例如:本地搜索阶段可以使用模拟退火和实体禁忌搜索。
以下是一个按顺序运行三个阶段的配置示例:
<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">
...
<constructionHeuristic>
... <!-- 第一个阶段:首次适应降序 -->
</constructionHeuristic>
<localSearch>
... <!-- 第二个阶段:后期接受 -->
</localSearch>
<localSearch>
... <!-- 第三个阶段:禁忌搜索 -->
</localSearch>
</solver>
求解器阶段按照求解器配置定义的顺序运行。
-
当第一个阶段结束时,第二个阶段开始运行,依此类推。
-
当最后一个阶段结束时,求解器终止。
通常,求解器会先运行一个构造启发式算法,然后运行一个或多个元启发式算法:
如果没有配置阶段,OptaPlanner将默认运行一个构造启发式算法阶段,然后是一个本地搜索阶段。
某些阶段(尤其是构造启发式算法)会自动终止。其他阶段(尤其是元启发式算法)只有在配置了终止条件时才会终止:
<solver xmlns="https://www.optaplanner