从拼图游戏开始(四)_IDA*算法求解Java实现(一)

2014-11-24 07:42:58 · 作者: · 浏览: 0

终于,在学习了完深搜和广搜、Dijkstra、二叉堆和优先队列以及A*算法之后,可以看一下IDA*算法了。因为求解4x4的拼图游戏所要搜素的节点数很大,所以应该采用IDA*算法,而3x3的拼图游戏则可以采用A*算法。

IDA*算法是A*的一个变形,和A*算法不同的是他不保存之前的搜索状态(意味着同一个节点可能会被搜索多次),它的搜索效率会稍微低于A*算法。笔者对于IDA*的翻译是迭代深度的A*算法,它是一种有信息提示的搜索(informed search),但其实现思想是基于无信息提示的IDDFS(iterative deepening depth-first search)。

下面先来讨论IDDFS,再来实现基于IDDFS的IDA*。

打开维基百科,发现没有中文版的介绍。于是笔者只能用咱的蹩脚英语翻译一下了:

<译>Iterative deepening depth-first search (IDDFS)是一个反复调用depth-limited search的状态空间搜索策略,每次迭代都会增加深度限制直到d(d为深度最浅的可行解对应的深度)。IDDFS等同于广度优先搜索,但是它需要更少的内存开销。每次迭代它会以深度优先搜索的顺序来访问节点,但是总体的累积效果还是广度优先搜索的。

好了,有点乱哈,一下BFS一下DFS的。咱还是不翻译了,这么说吧,我们可以把一次深度优先搜索看做一个深度为n的搜索单元,这个搜索单元是基于上级的广度优先搜索的。然后该深度n会在每次迭代是不断加深,直到找到可行解为止。因为深度优先是不存储"备选节点“的,于是整个IDDFS的内存开销不大,又因为搜索的上层控制是广度优先搜索,于是整个IDDFS等同于广度优先搜索。IDDFS集合了DFS和BFS两者的优点,其空间复杂度是O(bd),时间复杂度是O(b^d)。感兴趣的朋友可以查看笔者的另一篇文章:IDDFS(Iterative deepening depth-first search)的Java实现

现在来说一下IDA*,设想有这样的一个场景,有老板A和员工B,老板A有一个拼图游戏需要求解,老板观察了一下问题,根据问题的manhattan距离得到了一个可能的最小步骤解n。于是把员工B叫过来,说你给我在n步之内解出题目。于是员工B开始解题,他的方法很简单采用深搜的方法搜索所有符合该manhattan距离限制的解,此时,可能当员工B遍历了所以的结果后还是没有找到可行解,于是他只能把这个结果和老板A说了,老板A看了以后,允许解题步骤限制+1,然后员工B在以这个n+1的步骤限制,从头开始做一个遍历,可能他还没有找到可行解,他就会再去找老板,老板说再加1吧,如此循环,直到员工B求得可行解。

实现代码:

package com.wly.algorithmbase.search;


/**
 * IDA*求解15puzzle问题
 * IDA*整合了IDDFS和A*算法。其中IDDFS控制了求解过程中的内存开销,A*算法意味着"启发式"搜索。
 * IDA*可以理解成迭代深度的A*算法,其中这里的"深度"指的是问题的解的"假定损耗"。
 * 使"假定损耗"不断迭代增加,检验是否能在"假定损耗"的前提找到可行解,如果不行的话,就继续迭代。
 * 这里和A*算法不同的是没有开放列表,由于采用了IDDFS的策略,IDA*是深度优先搜索的,故此没有开放列表。
 * 可是求解15Puzzle时,使用的基于"深搜"的IDA*竟然不回溯的,这真是很奇怪啊!
 * 因为在求解15puzzle问题之前,必须使用一定的判断依据来论证问题的可解性,故此在问题可解的前提下使用
 * IDA*求解问题总是可以得到一个可行解的(只不过这个可行解可能不是最优解而已)。
 * @author wly
 * @date 2013-12-20
 *
 */
public class IDAStarAlgorithm {

	//分别代表左、上、右、下四个移动方向的操作数
	private int[] up = {-1,0};
	private int[] down = {1,0};
	private int[] left = {0,-1};
	private int[] right = {0,1};
	
	/**注意,这里UP和DOWN,LEFT和RIGHT必须是两两相对的,因为后面代码中使用
	 * ((dPrev != dCurr) && (dPrev%2 == dCurr%2))来判断前后两个移动方向是否相反
	 */
	private final int UP = 0;
	private final int DOWN = 2;
	private final int LEFT = 1;
	private final int RIGHT = 3;
	
	private int SIZE;
	
	//各个目标点坐标
	private int[][] targetPoints; 
	
	//用于记录移动步骤,存储0,1,2,3,对应上,下,左,右
	private static int[] moves = new int[100000];
	
	private static long ans = 0;; //当前迭代的"设想代价"
	
	//目标状态
	private static int[][] tState = {
		{1 ,2 ,3 ,4 } ,
		{5 ,6 ,7 ,8 } ,
		{9 ,10,11,12} ,
		{13,14,15,0 }
	};
	
	
	private static int[][] sState = {
		{2 ,10 ,3 ,4 } ,
		{1 ,0,6 ,8 } ,
		{5 ,14,7,11} ,
		{9,13,15,12 }
	};
	
	//初始状态
//	private static int[][] sState = {
//		{12,1 ,10,2 } ,
//		{7 ,11,4 ,14} ,
//		{5 ,0 ,9 ,15} ,
//		{8 ,13,6 ,3} 
//	};
	
	private static int blank_row,blank_column;
	
	
	public IDAStarAlgorithm(int[][] state) {
		SIZE = state.length;
		targetPoints = new int[SIZE * SIZE][2];
		
		this.sState = state;
		//得到空格坐标
		for(int i=0;i
  
    targetPoints[state[blank_row1][blank_column1]][0]) {
				h1 = h - 1;
			} else if(direction == UP && blank_row1 
					< targetPoints[state[blank_row1][blank_column1]][0]){
				h1 = h - 1;
			} else if(direction == RIGHT && blank_column1 
					> targetPoints[state[blank_row1][blank_column1]][1]) {
				h1 = h - 1;
			} else if(direction == LEFT && blank_column1 
					< targetPoints[state[blank_row1][blank_column1]][1]) {
				h1 = h - 1;
			} else { //这种情况发生在当前移动位置已经处于最后位置,此时向任意方向的移动都会使得估价函数值变大
				h1 = h + 1;
			}
			
			if(h1+dep+1>ans) { //剪枝
				continue;
			}
			
			moves[dep] = direction;
			
			//迭代深度求解
			if(solve(state2, blank_row1, blank_column1, dep+1, direction, h1)) {
				return true;
			}
		}