八数码问题——康托展开+A*算法
康托展开
- 康托展开能够将一系列排列组合映射到有限集中,常用于构建哈希表时的空间压缩。在这里能够完成各种组合的判重。如{0,1,2,3,4}五个数字的所有排列组合,使用康托展开能够将排列组合的情况恰好转换为5!=120个整数,极大的节约空间,同时也能够逆康托展开,求得原排列组合。
- 在解决这里的问题能够将九个数的排列组合转换为int整数,我们直接开一个9!大小的boolean数组,若该排列组合出现过,只需要将该位置状态改为reue,这样很快地判断了是否重复。
/**
* 康托展开,用于判重
* a表示全排列的数组,n位全排列
*/
int cantor(int []a, int n) {
int x = 0;
for (int i = 0; i < n; ++i) {
// 在当前位之后小于其的个数
int smaller = 0;
for (int j = i + 1; j < n; ++j) {
if (a[j] < a[i]) {
smaller++;
}
}
// 康托展开累加
x += factorial[n - i - 1] * smaller;
}
return x+1;
}
节点类
- private int []status=new int[9]; 当前节点的九宫格排列
- private MyNode parentNode; 当前节点的父节点
- private int diffNum; 当前节点与目标位置不同的个数
- private int score; 估值函数的总分,越小优先级越高
- private int zeroPos; 0数字在状态数组中位置
- private int depth; 当前节点遍历的层数
A星算法搜索
判断0点的移动:
使用一个9*4二维数组表示0在九宫格中的某个位置时,要交换位置在一维数组中的下标,减少了需要判断上下左右能否移动的判断,能够直接交换数字的位置,也方便遍历四个方向。
顺序为上左下右逆时针
-1表示该方向不能交换
int [][]changeId={
{
-1,-1,3,1},{
-1,0,4,2},{
-1,1,5,-1},
{
0,-1,6,4},{
1,3,7,5},{
2,4,8,-1},
{
3,-1,-1,7},{
4,6,-1,8},{
5,7,-1,-1}};
open表的排序:
这里采用了Java的PriorityQueue优先级队列,使用自定义比较器(以lambda语法简写)构造,每次从中取出的为估值最小的节点
/**open表*/
PriorityQueue <MyNode>open=new PriorityQueue<MyNode>((o1, o2) -> o1.gethScore()-o2.gethScore());
估值函数:
采用当前节点在遍历的层数(即交换的步数)+当前节点与目标节点相差的个数,这里在节点类的构造函数中求得。
算法流程:
算法其实基于BFS广度优先算法遍历&