一 A* 算法
1.1 算法介绍
1.1.1 相关背景
广度优先搜索(Breadth-First-Search,BFS)、深度优先搜索(Depth-First-Search,DFS)以及由此延伸出来的双向广度优先搜索(DBFS)、双向深度优先搜索(DDFS)都属于盲目搜索,这在状态空间不大的情况下是很合适的算法,可是当状态空间十分庞大时,它们的效率实在太低,往往都是在搜索了大量无关的状态结点后才碰到解答,甚至更本不能碰到解答。搜索是一种试探性的查寻过程,为了减少搜索的盲目性引,增加试探的准确性,就要采用启发式搜索了。所谓启发式搜索就是在搜索中要对每一个搜索的位置进行评估,从中选择最好、可能容易到达目标的位置,再从这个位置向前进行搜索,这样就可以在搜索中省略大量无关的结点,提高了速度。
1.1.2 A*算法流程
A*算法是一种启发式搜索算法,可以看做是针对盲目搜索(BFS/DFS)的优化。相对于DFS,A*仅仅多了一个评估值H,使状态空间不那么盲目。在A*算法中,一个结点位置的好坏用估价函数(H)来对它进行评估。A*算法的估价函数可表示为:
其中,$F’(n)$ 是估价函数,$g’(n)$ 是从初始起点到当前节点$n$的最短路径值(也称为最小耗费或最小代价,在盲目搜索中对应搜索深度), $h’(n)$ 是当前节点$n$ 到目标节点的最短路径的启发值。由于这个$f’(n)$ 其实是无法预先知道的,所以实际上使用的是下面的估价函数:
其中$g(n)$ 是从初始结点到当前节点n的实际代价,$h(n)$ 是从当前结点n到目标结点的最佳路径的估计代价。在这里主要是$h(n)$ 体现了搜索的启发信息,因为$g(n)$ 是已知的。用$f(n)$ 作为$f’(n)$ 的近似,也就是用$g(n)$ 代替$g’(n)$ 、$h(n)$ 代替$h’(n)$ , 这样必须满足以下条件:
- $g(n)\ge g’(n)$ . 大多数情况下都是满足的,可以不用考虑。 $g(n)$ 必须保持单调递增。
- $h(n)$ 必须小于等于实际的从当前节点n到达目标节点的最小耗费,即$h(n)\le h’(n)$ , 可以证明应用这样的估价函数是可以找到最短路径的。
A*算法基本上与BFS相同。区别在于扩展出一个结点后,要计算它的估价函数(f),并根据估价函数对待扩展的结点排序,从而保证每次扩展的结点都是估价函数(f)最小的结点。
具体步骤:
- 新建一个
Open和Closed表,置空,将初始状态放入Open表中。 - 将
Open表中最小F值状态取出放入Closed表中,若找到目标状态,则中断过程,此时状态的GG值就是最短路径(在八数码是最少步骤),否则生成其下一步所有能转移的状态集S - 计算状态集S每个状态的F,G,H值,得出F=G+H值,这里的G可取前一个状态(父状态)的GG值加一,为了寻得最短路径,可将其指向父状态。
- 枚举状态集,按5, 6处理
- 若状态集S的状态在
Closed表中,则不理会,否则6 - 若状态集S的状态在
Open表中且其F值较小,则更新(因为有最优的路径啊当然要更新),否则不理会,若不在Open表中,则插入。 - 进入2
伪代码:(源自wiki) 有错误 见我的博客
|
|
1.1.3 其他
- 如果$g(n)$ 为0,即只计算任意顶点$n$ 到目标的评估函数$h(n)$ ,而不计算起点到顶点$n$ 的距离,则算法转化为使用贪心策略的Best-First Search,速度最快,但可能得不出最优解;
- 如果$h(n)$ 不高于实际到目标顶点的距离,则一定可以求出最优解,而且$h(n)$ 越小,需要计算的节点越多,算法效率越低,常见的评估函数有——欧几里得距离、曼哈顿距离、切比雪夫距离;
- 如果$h(n)$ 为0,即只需求出起点到任意顶点$n$的最短路径 ,而不计算任何评估函数$h(n)$ ,则转化为单源最短路径问题,即Dijkstra算法,此时需要计算最多的定点;
1.2 C++实现
1.2.1 数据结构
A*算法主要数据结构为Open表和Close表。其中,Open 表有取最小值、查找、插入、删除操作,Close 表有查找、插入操作。比较好的实现方案是Open 表用优先队列(最小堆) 来实现,Close 表用排序二叉树 来实现。
表中元素的类型取决于具体问题。但该类型的成员应该能(唯一的)表示状态、父节点、g值、h值。且应当定义该类型的拓展子节点(extend)方法、相等判断(operator==)方法、大小比较等。
1.2.2 实现
由于具体问题不可预知,故使用模板类型T作为接口。由于时间仓促,使用std::vector 作为Open、Close表载体。
所有源码见AStar.h 文件。以下只给出主要类、函数声明及含义。
Class Configure :用于设置T类型的相关操作,取决于具体问题。
|
|
Class Status: 状态节点。更新操作(Update)依赖于Configure 类。
|
|
AStar : A*算法
|
|
二 八数码问题
2.1问题介绍
八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。
要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤(下文称为路径)。所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
2.2 分析
初步分析得出如下结论:
考虑到除了8数码问题,还有15数码等问题,可以拓展成$N^2-1$ 数码问题。
拓展子节点只与空格位置有关;故下文用
u d l r分别代表空格的上下左右移动。把$N \times N$ 的二维棋盘“拉伸”成一维数组(下文记为$a_1,a_2, a_3 \cdots a_{n*n}$ ),便于处理。
有些资料中以”1234678X”为目标状态,而另外一些则采用螺旋形”1238X4765”为目标状态。考虑到算法通用性,目标状态不应局限于以上两种。
最终求解的是最短路径,且任意节点可由路径推断出父节点、祖父节点……故使用一个成员变量记录路径之后便不需要再用指针指向父节点
为保证算法正常工作,还必须证明算法的收敛性和可解性。补充如下数学定理。
2.2.1 判断是否有解
逆序对:对于一个排列$a_1, a_2, a_3 \cdots a_n$ , 如果有$ja_i$ ,那么$a_i$和$a_j$ 构成一个逆序对。
定义排列$a_1, a_2 \cdots a_n$ 的逆序数为该所有元素的逆序数总和。
结论:两种状态可以相互到达的条件为一维序列的逆序数奇偶性相同(N为奇数时);奇偶性相反(N为偶数时)。其中,空格不参与逆序数计算。
先证明3个引理:
引理1:如果左右交换一次棋子,则排列的逆序数奇偶性改变。
证明:不失一般性,设$ a_{i+1}> a_i$ .序列{$a_i , a_{i+1}$} 的逆序数为0. 现在将两者交换,显然对于$a_j$ ($ji+1$ )的逆序数不会改变。而交换之后的序列{ $ a_{i+1},a_i ,$ } 逆序数为1,故总序列的逆序数奇偶性发生改变。
引理2:如果空格与左右交换一次,则排列的逆序数奇偶性不变。
证明:因为不表示空格,所以原排列不变,即证引理成立。
引理3:如果空格与上下交换了一次,则排列的逆序数奇偶性不变。
证明:
若N为奇数,不失一般性设N=3, 则考虑序列$12345678X$ 与下面的空格交换了一次,变为$123450786$ 。该过程可以分解为若干次空格连续左移和若干次被换对象(数字6)连续和右边的数交换:$123456780$ →$123456708$ →$123456078$→$123450678$ (空格左移完成)→$123450768$ →$123450786$ (数字6右移完成)。由引理1知数字6移动两次,不改变奇偶性;由定理2知空格左右移动不影响奇偶性;
若N为偶数,则在数字交换时需要交换$N-1$ 次,则根据引理1,总序列奇偶性发生改变。
由以上可知:N为偶数时,两个序列逆序数奇偶性相反才可互相到达;N为奇数时,两个序列逆序数奇偶性相同才可互相到达。
|
|
2.2.2 判断是否有多个最优解
首先可以发现唯一解、对称二解、多解等情况都是存在的。
我尝试在数学上进行分析,但暂时失败了。
暴力枚举证明见我的博客(不定时更新)
2.3 实现
根据1.2 C++实现 , 我们只需要定义八数码问题的状态、H、G、拓展方式等即可代入使用。以下是部分代码。完整代码见AStar.cpp
状态类:
|
|
G(n):
|
|
生成子节点:
|
|
H(n):待定 见下一章。
三 不同估价函数H的对比
3.1 介绍
本章比较了四种不同估价函数H,分别是:
- 简单曼哈顿距离。仅统计棋盘各位置上数字不对应的个数
- 一维曼哈顿距离。统计各数字到正确位置的一维距离之和
- 二维曼哈顿距离。统计各数字到正确位置的二维距离之和
- 恒为0;算法蜕化为BFS。
3.1.1 H=Manhattan distance (Simple)
|
|
3.1.2 H=Manhattan distance (One-dimensional)
|
|
3.1.3 H=Manhattan distance (Two-dimensional)
|
|
3.1.4 H≡0 (BFS)
|
|
3.2 性能分析
为公平全面地评价以上4个H的性能,需要在不同情况、不同深度的图进行测试。
设计如下样本生成器:
|
|
测试函数:(由于数据结构优化尚未完成,该测试程序需要运行较长时间(20分钟左右))
|
|
以下为测试结果:
H=Manhattan distance (Simple)
| 生成序列 | 起始状态 | Open | Extemd(Close) | Sum | 花费时间(ms) | 解序列 | 解序列长度 |
|---|---|---|---|---|---|---|---|
| urddlluurrddlurdllur | 345607182 | 2506 | 3825 | 6331 | 1099 | ldrruulldrdruuldldru | 20 |
| urddlulurddluurrddl | 845326107 | 1550 | 2205 | 3755 | 247 | ruullddrrululdrruld | 19 |
| rulldrdlururddllur | 823105647 | 1473 | 2101 | 3674 | 227 | ldrruuldldrulurrdl | 18 |
| ruldldrurdlluruld | 312045768 | 756 | 1084 | 1840 | 42 | urdldrrulurdldlur | 17 |
| rdlurdlurdlurull | 012843765 | 6 | 5 | 11 | 0 | rrdl | 4 |
| urdldluurrddlul | 342015867 | 286 | 386 | 672 | 6 | rdruullddruruld | 15 |
| ruldrdllurdrul | 142307865 | 237 | 326 | 563 | 5 | rdluldrrulurdl | 14 |
| lurrddluurdld | 245183706 | 144 | 191 | 335 | 2 | urulddruulldr | 13 |
| ldrurdlluurr | 230145768 | 97 | 116 | 213 | 1 | llddrruldlur | 12 |
| urdlldrruul | 103784652 | 71 | 83 | 154 | 0 | rddllurruld | 11 |
| uldruldrur | 230184765 | 6 | 5 | 11 | 0 | lldr | 4 |
| urdldluur | 304162875 | 21 | 21 | 42 | 0 | ldruruld | 9 |
| dlururdl | 134602875 | 19 | 20 | 39 | 0 | ldruruld | 8 |
| ruldlur | 402183765 | 17 | 16 | 33 | 0 | ldrurdl | 7 |
| ruldlu | 042183765 | 12 | 11 | 23 | 0 | drurdl | 6 |
| lurdl | 283014765 | 10 | 9 | 19 | 0 | drurdl | 5 |
H=Manhattan distance (One-dimensional)
| 生成序列 | 起始状态 | Open | Extemd(Close) | Sum | 花费时间(ms) | 解序列 | 解序列长度 |
|---|---|---|---|---|---|---|---|
| urddlluurrddlurdllur | 345607182 | 96 | 111 | 207 | 2 | ldruuldrrdluruldldrrul | 22 |
| urddlulurddluurrddl | 845326107 | 124 | 146 | 270 | 3 | ruuldlurdldrruulldr | 19 |
| rulldrdlururddllur | 823105647 | 527 | 640 | 1167 | 41 | ldrruuldldrulurrdl | 18 |
| ruldldrurdlluruld | 312045768 | 562 | 733 | 1295 | 58 | urdldrrulurdldlur | 17 |
| rdlurdlurdlurull | 012843765 | 4 | 4 | 8 | 0 | rrdl | 4 |
| urdldluurrddlul | 342015867 | 611 | 801 | 1412 | 66 | rdruullddruruld | 15 |
| ruldrdllurdrul | 142307865 | 119 | 154 | 273 | 1 | rdluldrrulurdl | 14 |
| lurrddluurdld | 245183706 | 22 | 23 | 45 | 0 | urulddruulldr | 13 |
| ldrurdlluurr | 230145768 | 51 | 59 | 110 | 0 | llddrruldlur | 12 |
| urdlldrruul | 103784652 | 34 | 37 | 71 | 0 | rddllurruld | 11 |
| uldruldrur | 230184765 | 6 | 5 | 11 | 0 | lldr | 4 |
| urdldluur | 304162875 | 9 | 9 | 18 | 0 | lddruruld | 9 |
| dlururdl | 134602875 | 11 | 10 | 21 | 0 | ldruruld | 8 |
| ruldlur | 402183765 | 16 | 14 | 30 | 0 | ldrurdl | 7 |
| ruldlu | 042183765 | 8 | 7 | 15 | 0 | drurdl | 6 |
| lurdl | 283014765 | 8 | 6 | 14 | 1 | ruldr | 5 |
H=Manhattan distance (Two-dimensional)
| 生成序列 | 起始状态 | Open | Extemd(Close) | Sum | 花费时间(ms) | 解序列 | 解序列长度 |
|---|---|---|---|---|---|---|---|
| urddlluurrddlurdllur | 345607182 | 160 | 202 | 362 | 3 | ldrruulldrdruuldldru | 20 |
| urddlulurddluurrddl | 845326107 | 145 | 184 | 329 | 3 | ruullddrrululdrruld | 19 |
| rulldrdlururddllur | 823105647 | 376 | 515 | 891 | 12 | ldrruuldldrulurrdl | 18 |
| ruldldrurdlluruld | 312045768 | 149 | 198 | 347 | 2 | urdldrrulurdldlur | 17 |
| rdlurdlurdlurull | 012843765 | 4 | 4 | 8 | 0 | rrdl | 4 |
| urdldluurrddlul | 342015867 | 77 | 100 | 177 | 1 | rdruullddruruld | 15 |
| ruldrdllurdrul | 142307865 | 62 | 73 | 135 | 0 | rdluldrrulurdl | 14 |
| lurrddluurdld | 245183706 | 56 | 62 | 118 | 0 | urulddruulldr | 13 |
| ldrurdlluurr | 230145768 | 40 | 41 | 81 | 0 | llddrruldlur | 12 |
| urdlldrruul | 103784652 | 31 | 30 | 61 | 1 | rddllurruld | 11 |
| uldruldrur | 230184765 | 4 | 4 | 8 | 0 | lldr | 4 |
| urdldluur | 304162875 | 13 | 11 | 24 | 0 | lddruruld | 9 |
| dlururdl | 134602875 | 15 | 13 | 28 | 0 | ldruruld | 8 |
| ruldlur | 402183765 | 11 | 10 | 21 | 1 | ldrurdl | 7 |
| ruldlu | 042183765 | 7 | 6 | 13 | 0 | drurdl | 6 |
| lurdl | 283014765 | 7 | 5 | 12 | 0 | ruldr | 5 |
H=0 BFS
| 生成序列 | 起始状态 | Open | Extemd(Close) | Sum | 花费时间(ms) | 解序列 | 解序列长度 |
|---|---|---|---|---|---|---|---|
| urddlluurrddlurdllur | 345607182 | 18765 | 48740 | 67505 | 299365 | ldrruldruullddrruuld | 20 |
| urddlulurddluurrddl | 845326107 | 14725 | 35674 | 50399 | 155379 | ruulldrulddrruulldr | 19 |
| rulldrdlururddllur | 823105647 | 11465 | 23696 | 31561 | 72430 | ldrruuldldrulurrdl | 18 |
| ruldldrurdlluruld | 312045768 | 8304 | 17363 | 25667 | 36628 | urdldrrulurdldlur | 17 |
| rdlurdlurdlurull | 012843765 | 16 | 15 | 31 | 0 | rrdl | 4 |
| urdldluurrddlul | 342015867 | 3758 | 6836 | 10594 | 4907 | rdruullddruruld | 15 |
| ruldrdllurdrul | 142307865 | 2734 | 4771 | 7505 | 1474 | rdluldrrulurdl | 14 |
| lurrddluurdld | 245183706 | 1708 | 3049 | 4754 | 255 | urulddruulldr | 13 |
| ldrurdlluurr | 230145768 | 794 | 1198 | 1992 | 44 | llddrruldlur | 12 |
| urdlldrruul | 103784652 | 615 | 999 | 1614 | 30 | rddllurruld | 11 |
| uldruldrur | 230184765 | 16 | 15 | 31 | 0 | lldr | 4 |
| urdldluur | 304162875 | 216 | 339 | 555 | 4 | lddruruld | 9 |
| dlururdl | 134602875 | 149 | 230 | 379 | 2 | ldruruld | 8 |
| ruldlur | 402183765 | 84 | 108 | 192 | 0 | ldrurdl | 7 |
| ruldlu | 042183765 | 55 | 76 | 131 | 0 | drurdl | 6 |
| lurdl | 283014765 | 32 | 37 | 69 | 0 | ruldr | 5 |
在Matlab中画图比较如下:




可以看到四种H评估函数都基本能得到最优解。
但是在时间上,二维曼哈顿和一维曼哈顿表现比较好。
空间上,同样是二维曼哈顿和一维曼哈顿表现较好。
BFS时间和空间都随着深度而近似指数增加,效率太低。
三 其他
1
本报告相关内容将发表在我的博客上(不定期更新):
A-Algorithm-and-8-Puzzle-Problem
代码(不定时更新):https://github.com/Vast-Stars/Algorithm/tree/master/8digit
2
八数码相关的游戏:
拼图:

https://github.com/netcan/SlidePuzzle
华容道:

3
有个小问题:维基百科上的A*算法流程中,若子节点出现在Close表中,则不采取任何操作,这和书上更新F值相矛盾,为什么?
Reference
http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2624115.html
http://lucienwang.leanote.com/post/八数码——回顾BFS、双向广搜与A
https://www.cs.princeton.edu/courses/archive/fall12/cos226/assignments/8puzzle.html
http://www.cs.cmu.edu/afs/cs/Web/People/adamchik/15-121/labs/HW-7%20Slide%20Puzzle/lab.html