奇怪的搜索技巧:优化搜索
这次的内容预告:迭代加深、A*、IDA*、极大极小搜索法、跳舞链。
零、前置:dfs的最优性剪枝
比最优解要劣就退出。
一、迭代加深dfs
当搜索深度达到约束值却还没找到可行解时结束搜索。如果我们在一个深度约束下没有搜索到答案,那么答案一定在更深的位置,那么就把约束深度调整到更深,然后再次搜索,直到搜索到答案为止。
估价函数:在实际运用中,如果没有一个合适的方法来剪枝,迭代加深搜索也会很容易超时。好在迭代加深搜索有一个比较特殊的剪枝方法,就是对当前的情况通过一个乐观估计函数进行预估,如果发现即使在最好的情况下搜索到当前的最深深度限制也没办法得到答案,那么就及时退出来实现剪枝。这个其实叫做可行性剪枝。也是下文的IDA*。
二、A*
优化优先队列bfs。这里真正要用到上面提到的估价函数。这里有个要求:设当前状态 u u u到目标估计值 f ( u ) f(u) f(u),实际得出 g ( u ) g(u) g(u),必须满足 f ( u ) < g ( u ) f(u)<g(u) f(u)<g(u)。即估计值是更优的。
这个算法的关键就在于设计优秀的估价函数。
三、IDA*
考虑优化迭代加深的dfs,利用和A* 一样的思路。A* 的思路有一个缺陷,空间消耗大而且每次对堆操作花费 O ( log n ) O(\log n) O(logn)时间。所以对迭代加深dfs拓展成类似A* 的形式就是IDA*。
关键点来了:综合一下迭代加深和A*,得出限制:若当前深度+未来估计步数>深度限制,就回溯。
练习:
四、极大极小搜索法 与 Alpha-Beta剪枝
适用范围:博弈题。
极大极小搜索法:正方形代表自己的选择,圆形代表对方的选择。每个节点表示对我的利益值。正方形的值即为儿子中最大值,圆形为儿子中最小值。
B总是选择候选方案中的最小值,而A总是选择候选方案中的最大值,极小极大的名字也就源于此。该算法使用深度优先搜索(Depth First Search)遍历决策树来填充树中间节点的利益值,叶子节点的利益值通常是通过一个利益评估函数计算。
Alpha-Beta剪枝:假设α为下界,β为上界,对于α ≤ N ≤ β:
若 α ≤ β 则N有解。 若 α > β 则N无解。初始设置α为负无穷大,β为正无穷大。 每次回溯的时候,父亲节点的α或β值进行修改即可。这样就可以剪掉很多无用状态。
练习:POJ 1085 三角点格棋
五、跳舞链(Dancing Links)
适用问题:求解精确覆盖问题。
主要是通过新建双向链表来完成回溯。
可以把Dancing Links的求解过程表述如下
- Dancing函数的入口
- 判断Head.Right=Head?,若是,输出答案,返回True,退出函数。
- 获得Head.Right的元素C
- 标示元素C
- 获得元素C所在列的一个元素
- 标示该元素同行的其余元素所在的列首元素
- 获得一个简化的问题,递归调用Daning函数,若返回的True,则返回True,退出函数。
- 若返回的是False,则回标该元素同行的其余元素所在的列首元素,回标的顺序和之前标示的顺序相反
- 获得元素C所在列的下一个元素,若有,跳转到步骤6
- 若没有,回标元素C,返回False,退出函数。
练习