A*算法解决八数码问题
介绍
八数码问题
八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
A*算法
A*算法是一种启发式搜索算法,启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。
算法核心公式:
F = G + H
F - 决策的总代价
G - 从开始到决策时刻所花费的真实代价
H - 从决策时刻到目标的预估代价
其中G值是一个真实的代价,也就是从算法流程一开始到进行这个决策的时候,一共花费的代价。H值是一个预估的代价。在极端情况下,如果H是常量0,这个算法退化成广度优先搜索算法,即按照顺序的依次扩充每个节点。如果G是常量,这个算法退化成贪心算法,即每次扩充可以使下一次最优的节点。每次进行决策的时候,A*算法选择遍历所有当前可能的决策,并执行使F最大的决策。
open表:先能够简单认为是一个未搜索节点的表
close表:先能够简单认为是一个已完成搜索的节点的表(即已经将下一个状态放入open表内)
- 规则一:对于新添加的节点S(open表和close表中均没有这个状态),S直接添加到open表中
- 规则二:对于已经添加的节点S(open表或者close表中已经有这个状态),若在open表中,与原来的状态S0的f(n)比较,取最小的一个。若在close表中,那就分两种状况,第一种,close表中的该状态S0的f(n)大于S的,不作修改;第二种S0的f(n)小于S的,那就要须要将close表中S0的f(n)更新,同时将该状态移入到open表中。
- 规则三:下一个搜索节点的选择问题,选取open表中f(n)的值最小的状态做为下一个待搜索节点
- 规则四:每次须要将带搜索的节点下一个全部的状态按照规则一二更新open表,close表,搜索完该节点后,移到close表中。
策略
我们定义从开始状态执行的总步数为真实代价G,而当前状态与最终结果的格数差异我们记为H即从决策时刻到目标的预估代价。例如上图所示中的初始状态与最终状态之间相距了5个位置,即
代码解决
对于八数码问题,我们先考虑到的是如何使用数据结构将各个状态一一对应的表达出来
例如
我们看到八数码问题中有9个元素的位置,我们按照行将其标记为0, 1, 2, 3, 4, 5, 6, 7, 8个元素,每个位置的元素n满足,我们定义空位置为0
而最大8的极端情况下其二进制表达为8D = (1000)B,需要4位的二进制空间,则9个元素总共需要位的二进制数来表示一个状态,故我们需要长整型long来进行表示
对于上图其十进制对应状态表达为1 4 2 8 0 3 7 6 5
二进制表达为 0001 0100 0010 1000 0000 0011 0111 0110 0101,记为一个32位的整型
代码如下:
1 | import java.util.Arrays; |
每一轮while循环,程序都将打印出队的cur数组相关信息,最后打印STEP最终步数
运行结果如下:
1 | /Users/ray/Library/Java/JavaVirtualMachines/openjdk-15.0.2/Contents/Home/bin/java -javaagent:/Users/ray/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/211.7142.45/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=50003:/Users/ray/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/211.7142.45/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/ray/IdeaProjects/oj/out/production/oj com.company.grid |
我们得到最终步数为4