缺一九宫格中的算法-八数码问题

摘要:在一个九宫格内放置了8个数码,从的初始状态到目标状态,给出数码的移动序列,称之为八数码问题。本文简单的实现了八数码到十六数码问题的解决方案。

 

  偶尔看到网页上有缺一九宫格的小游戏,上手玩了几把,通过转圈法,把九宫格还原,虽然还原了,但是比较多的时间,带着疑问,于是就开始研究如何找到把缺一九宫格的最佳路径。

一,问题具体化

 1)移动方式

游戏的移动方式是通过点击白块上下左右的方块来不停交换位置来完成拼图。我们将拼图的分布设置为数组[[1,2,7][4,3,6][5,8,0]]白块的位置设置为0,通过0不断的与周围数字交换使我们的数组变得有序。

 2)问题的有解性

我在玩网上的九宫格游戏时,九宫格会出现最后两块位置始终无法还原的情况。通过度娘复习了一下高数的知识点-逆序数。

  1> 逆序数
  例如:1 2 4 3 ,有1对逆序数, 43
   2 4 3 1 ,有4对逆序数, 21,43,41,31

  2> 奇偶排列
  逆序数为奇数的为奇排列,逆序数为偶数的为偶排列
  1243是奇排列,2431为偶排列

  3> 奇偶排列的定理
  任意交换奇偶数列中两个数的位置,能使其奇偶性互换。

  在移动九宫格时,我们始终是在交换0与数字的位置,我们的目标是将一串数组还原成123456780,他是一个偶排列,0在偶位置。例如123456780(偶排列,0在偶位置),根据游戏的移动方式,0只能和8和6交换位置(一次交换后奇偶性互换,0的位置奇偶性互换)。因此如果出现一个奇排列,0在偶位置的数列,是无论如何也无法移动为123456780(偶排列,0在偶位置)的,因此产生随机数列时要对数列进行判断。

3)搜索方式

  我在玩这个游戏时,是通过将数字依次还原(1回到1位置后在继续还原数字2),通过不断的将错误位置的数减少,最终完成拼图。这是一种有方向的搜索,我知道我要朝着错误位置越来越少的结果一步一步前进。于是引入启发式搜索。

  启发式搜索:就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。

二,算法设计

 

 

 

1)优先队列的排序规则

启发搜索中的估价函数为:f(n) = g(n) + h(n),g(n)为当前产生的代价(当前的步数),h(n)为估价函数,这个问题属于状态空间的搜索,我们使用常用的曼哈顿距离还作为h(n)。

排序以f(n)作为参考,当f(n)相同时,g(n)比较小的优先度高。g(n)慢慢变大,h(n)慢慢减小,当h(n)为0时,n为最优解。

2)防止重复处理同一个节点

会出现往上移动后在往下移动这样的情况,我们使用节点的康托展开来记录这个节点是否被处理过。

康托展开:一个N数列的全排列的数量为Ni,通过康托展开,我们可以知道一个确定的数列在其全排列中的排行。

我们可以通过康托展开来记录已经访问过的节点,例如123在全排列123,132,213,231,312,321中排行第6,我们知道如果百位为2或者3的一定比他大,而百位为2的数是可解的,是2!,百位为3的是2!,在百位为1时,十位为3的一定比他大,等于1!,于是123排行老六。我们只需要记录数字6,就可以知道123被处理过。具体的公式大家自行搜索。

三,算法实现

使用typeScript实现。

https://github.com/sincw/miniGame/blob/master/webContent/src/main/webapp/work/eightPuzzle/arithmetic/eightPuzzleP1.ts

以下页面可浏览移动过程

https://github.com/sincw/miniGame/blob/master/webContent/src/main/webapp/work/eightPuzzle/eightPuzzle.html

四,八数码问题提升至十六数码

这个算法在解决8数码问题时,处理时间在1S以内,康托展开的map长度为两千左右。

解决16数码问题时,经常卡死,出现短步骤解时能够求解。康托展开的map长度大概在十万以上。

这个算法面对8数码还能够使用,但是应用到16数码时就不行了,map和queue会很大,难以维护。

通过学习知道到了IDA*算法,具体思路如下。

1)递归剪枝:f(n) = curLever + h(n),也就是当前深度(移动步数n) + 曼哈顿距离,通过h(n)+(n)<=minStep来保证递归的方向是正确的,当不满足此条件时,说明在当前深度中无解,即迭代加深再次递归。

2)算法实现:https://github.com/sincw/miniGame/blob/master/webContent/src/main/webapp/work/eightPuzzle/arithmetic/eightPuzzleP2.ts

3)用此算法解决深度为60以上的问题时也会花比较多的时间,但是效率比普通的A*快了很多倍。

PS:十六数码解决思路主要参考了  https://blog.csdn.net/u013369277/article/details/50503434   Triple-L 同学的博文
  

五,总结

  启发式算法的优点在于它比盲目型的搜索法要高效,通过仔细设计后的启发函数,能够让我们在很短的时间内得到一个搜索问题的最优解。

 

posted @ 2018-04-27 20:57  油炸土豆  阅读(1801)  评论(0编辑  收藏  举报