AStar解决八数码问题(java实现)

八数码游戏(八数码问题)描述为:在3×3组成的九宫格棋盘上,摆有八个将牌,每一个将牌都刻有1-8八个数码中的某一个数码。棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。这种游戏求解的问题是:给定一种初始的将牌布局或结构(称初始状态)和一个目标的布局(称目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。

对于八数码问题的解决,首先要考虑是否有答案。每一个状态可认为是一个1×9的矩阵,问题即通过矩阵的变换,是否可以变换为目标状态对应的矩阵?由数学知识可知,可计算这两个有序数列的逆序值,如果两者都是偶数或奇数,则可通过变换到达,否则,这两个状态不可达。这样,就可以在具体解决问题之前判断出问题是否可解,从而可以避免不必要的搜索。

如果初始状态可以到达目标状态,那么采取什么样的方法呢?
常用的状态空间搜索有深度优先和广度优先。广度优先是从初始状态一层一层向下找,直到找到目标为止。深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。 广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。这在状态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。他的效率实在太低,甚至不可完成。由于八数码问题状态空间共有9!个状态,对于八数码问题如果选定了初始状态和目标状态,有9!/2个状态要搜索,考虑到时间和空间的限制,在这里采用A*算法作为搜索策略。在这里就要用到启发式搜索  启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无畏的搜索路径,提到了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。

启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)  其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。 在此八数码问题中,显然g(n)就是从初始状态变换到当前状态所移动的步数,估计代价h(n)我们就可采用当前状态各个数字牌不在目标状态未知的个数,即错位数。

本算法的设计步骤是:

初始化两个链表open和closed,将初始状态放入open表中

《AStar解决八数码问题(java实现)》

public class Struct {
	int num[] = new int[9];//状态
	Struct parent;//父节点
	Struct next;//open表或closed表中的后一个节点
	int fvalue;//总路径
	int gvalue;//实际路径
	int hvalue;//节点到达目标状态的艰难程度
	/**
	 * 重写hashcode和equals方法
	 */
	@Override
	public int hashCode() {
		return this.hvalue;
	}
	@Override
	public boolean equals(Object obj) {
		Boolean flag = true;
		if(obj instanceof Struct)
		{
			Struct p = (Struct)obj;
			for(int i =0;i<9;i++)
			{
				if(p.num[i]!=this.num[i])
					flag = false;
			}
		}
		else
		{
			flag=false;
		}
			return flag;
	}
	
}
package com.lxl;
/**
 * 新的比较器
 */
import java.util.Comparator;

public class NewComparator implements Comparator<Struct> {

	@Override
	public int compare(Struct o1, Struct o2) {
		if(o1.fvalue>o2.fvalue)
			return 1;
		else if(o1.fvalue<o2.fvalue)
			return -1;
		else
			return 0;
	}

}
package com.lxl;
import java.util.*;
public class AStar8Num {
	List<Struct> open = new ArrayList<Struct>();//open表
	List<Struct> closed = new ArrayList<Struct>();//closed表
	List<Struct> spring = new ArrayList<Struct>();//spring表
	
	int start[] = new int[9];
	int target[] = new int[9];
	
	Struct structOfStart = new Struct();//初始状态
	Struct structOfTarget = new Struct();//目标状态
	public void init()
	{
		int i = 0;
		System.out.println("输入初始状态:");
		Scanner io = new Scanner(System.in);
		String s = io.nextLine();
		//切分读取的一行字符串,以空格为标杆
		String str[] = s.split(" ");
		for(String st : str)
		{
			if(!st.equals(""))
			{
				start[i++]=Integer.parseInt(st);
			}
		}
		System.out.println("输入目标状态:");
		Scanner io1 = new Scanner(System.in);
		String s1 = io1.nextLine();
		//切分读取的一行字符串,以空格为标杆
		String str1[] = s1.split(" ");
		i=0;//还原i值
		for(String st : str1)
		{
			if(!st.equals(""))
			{
				target[i++]=Integer.parseInt(st);
			}
		}
/*		for(i=0;i<9;i++)
		{
			System.out.print(start[i]+" ");
		}*/
		//初始状态
		
		for(i = 0;i<9;i++)
		{
			structOfStart.num[i]=start[i];
		}
		structOfStart.gvalue=0;
		structOfStart.hvalue=getHvalue(structOfStart);
		structOfStart.fvalue = structOfStart.gvalue+structOfStart.hvalue;
		structOfStart.parent=null;
		structOfStart.next=null;
		open.add(structOfStart);//初始状态加入open表中
		
		//目标状态
		
		for(i=0;i<9;i++)
		{
			structOfTarget.num[i]=target[i];
		}
		structOfTarget.hvalue=getHvalue(structOfTarget);
	}
	
	//计算某个状态的h值
	public int getHvalue(Struct status)
	{
		int i,num=0;
		for(i=0;i<9;i++)
		{
			if(status.num[i]!=target[i])
				num++;
		}
		status.hvalue = num;
		return status.hvalue;
	}
	//将某个状态加入到open表中,需要按非递减排序的
	public void add(Struct status , List<Struct> list)
	{
		list.add(status);
		//需要构造新的比较器NewComparator
		Collections.sort(list, new NewComparator());
	}
	//两个结点是否有相同的状态
	public Boolean hasSameStatus(Struct s1,Struct s2)
	{
		boolean flag = true;
		for(int i =0 ;i<9;i++)
		{
			if(s1.num[i]!=s2.num[i])
				flag = false;
		}
		return flag;
	}
	//结点与其祖先结点是否有相同的状态
	public Boolean hasAnceSameStatus(Struct origin,Struct ancester)
	{
		boolean flag = false;
		while(ancester!=null)
		{
			if(hasSameStatus(origin,ancester))
			{
				flag=true;
				return flag;
			}
			ancester = ancester.parent;//寻找祖先结点
		}
		return flag;
	}
	//把数组b的值复制给数组a
	public void copySnumToTnum(int a[],int b[])
	{
		int len = b.length;
		for(int i = 0;i<len;i++)
		{
			a[i]=b[i];
		}
	}
	//移动后产生后继结点
	public void getShift(Struct status,int index,int pos)
	{
		int medium = 0;//中介值
		Struct temp = new Struct();
		//temp.num = status.num;传的是地址
		
		//复制数组的值
		copySnumToTnum(temp.num,status.num);
		//outputStatus(status);
		
		//右移
		if(index==1)
		{
			//交换位置
			medium = temp.num[pos];
			temp.num[pos] = temp.num[pos-1];
			temp.num[pos-1] = medium;
			
			//如果与父辈结点没有相同的状态
			if(!hasAnceSameStatus(temp,status.parent))
			{
				temp.gvalue = status.gvalue+1;
				temp.hvalue = getHvalue(temp);
				temp.fvalue = temp.gvalue+temp.hvalue;
				temp.parent = status;
				temp.next=null;
				//加入spring表中
				spring.add(0,temp);
			}
			
		}
		//下移
		else if(index==2)
		{
			//交换位置
			medium = temp.num[pos];
			temp.num[pos] = temp.num[pos-3];
			temp.num[pos-3] = medium;
			
			if(!hasAnceSameStatus(temp,status.parent))
			{
				temp.gvalue = status.gvalue+1;
				temp.hvalue = getHvalue(temp);
				temp.fvalue = temp.gvalue+temp.hvalue;
				temp.parent = status;
				temp.next=null;
				//加入spring表中
				spring.add(0,temp);
			}
		}
		//左移
		else if(index==3)
		{
			//交换位置
			medium = temp.num[pos];
			temp.num[pos] = temp.num[pos+1];
			temp.num[pos+1] = medium;
			if(!hasAnceSameStatus(temp,status.parent))
			{
				temp.gvalue = status.gvalue+1;
				temp.hvalue = getHvalue(temp);
				temp.fvalue = temp.gvalue+temp.hvalue;
				temp.parent = status;
				temp.next=null;
				//加入spring表中
				spring.add(0,temp);
			}
		}
		//上移
		else
		{
			//交换位置
			medium = temp.num[pos];
			temp.num[pos] = temp.num[pos+3];
			temp.num[pos+3] = medium;
			if(!hasAnceSameStatus(temp,status.parent))
			{
				temp.gvalue = status.gvalue+1;
				temp.hvalue = getHvalue(temp);
				temp.fvalue = temp.gvalue+temp.hvalue;
				temp.parent = status;
				temp.next=null;
				//加入spring表中
				spring.add(0,temp);
			}
		}
	}
	//产生后继结点
	public void getNexts(Struct status)
	{
		int pos = 0;
		int i;
		//找到空格位置
		for(i=0;i<9;i++)
		{
			if(status.num[i]==0)
			{
				pos=i;
				break;
			}
		}
		//右移
		if(pos%3!=0)
		{
			getShift(status,1,pos);
		}
		//下移
		if(pos>2)
		{
			getShift(status, 2, pos);
		}
		//左移
		if(pos%3!=2)
		{
			getShift(status, 3, pos);
		}
		//上移
		if(pos<6)
		{
			getShift(status, 4, pos);
		}
	}
	//得到路径
	public void getPath(Struct status)
	{
		int deepnum = status.gvalue;
		if(status.parent!=null)
		{
			getPath(status.parent);
		}
		System.out.println("第"+deepnum+"层状态为:");
		deepnum--;
		outputStatus(status);
	}
	
	//输出状态
	public void outputStatus(Struct status)
	{
		for(int i = 0;i<status.num.length;i++)
		{
			if(i%3==0)
				System.out.println();
			System.out.print(status.num[i]+" ");
		}
		System.out.println();
	}
	//判断是否能解决
	public Boolean icansolve()
	{
		boolean flag = false;
		int i ,j;
		int resultOfStart=0;
		int resultOfTarget = 0;
		for(i=0;i<9;i++)
		{
			for(j=0;j<i;j++)
			{
				if(start[j]<start[i]&&start[j]!=0)
					resultOfStart++;
				if(target[j]<target[i]&&target[j]!=0)
					resultOfTarget++;
			}
		}
		//System.out.println(resultOfStart);
		//System.out.println(resultOfTarget);
		if((resultOfStart+resultOfTarget)%2==0)
			flag=true;
		return flag;
	}
	public void reslove()
	{
		int numcount = 1;
		Struct getOfOpen = null;
		boolean flag = false;
		init();
		//能不能解决
		if(!icansolve())
		{
			System.out.println("不能解决!!");
			System.exit(0);
		}
			
		System.out.println("从表中拿出的结点的状态及相应的值:");
		while(!open.isEmpty())
		{
			getOfOpen = open.get(0);
			closed.add(getOfOpen);
			open.remove(0);//移去加入到closed表中的结点
			
			System.out.println("第"+numcount+++"个状态是:");
			outputStatus(getOfOpen);
			System.out.println("其f值为:"+getOfOpen.fvalue);
			System.out.println("其g值为:"+getOfOpen.gvalue);
			System.out.println("其h值为:"+getOfOpen.hvalue);
			if(hasSameStatus(getOfOpen,structOfTarget))
			{
				flag = true;
				break;
			}
			getNexts(getOfOpen);//产生后继结点	
			
			while(!spring.isEmpty())
			{
				//得到spring表中的结点
				Struct struct = spring.get(0);
				if(open.contains(struct))
				{
					//得到open表中相同的结点,注意这里重写了equals和hashcode方法
					Struct structInOpen = open.get(open.indexOf(struct));
					//改变open表中节点的parent指针及相关参数
					if(struct.gvalue<structInOpen.gvalue)
					{
						structInOpen.parent = struct.parent;
						structInOpen.fvalue = struct.fvalue;
						structInOpen.gvalue = struct.gvalue;
						//在这里是不是应该重新排序open表??????
						
						Collections.sort(open, new NewComparator());
						
						
					}
					//删除spring表中的该节点
					spring.remove(struct);
				}
				else if(closed.contains(struct))
				{
					//得到closed表中相同的结点,注意这里重写了equals和hashcode方法
					Struct structInClosed = closed.get(closed.indexOf(struct));
					//改变closed表中节点的parent指针及相关参数
					if(struct.gvalue<structInClosed.gvalue)
					{
						structInClosed.parent = struct.parent;
						structInClosed.fvalue = struct.fvalue;
						structInClosed.gvalue = struct.gvalue;
						//加入至open表中
						add(structInClosed,open);
					}
					//删除spring表中的该节点
					spring.remove(struct);
				}
				else
				{
					add(struct,open);
					spring.remove(struct);
				}
			}
		}
		if(flag)
		{
			System.out.println("*************************************");
			System.out.println("路径长度为:"+getOfOpen.gvalue);
			getPath(getOfOpen);
		}
			
		
	}
	public static void main(String[] args) {
		new AStar8Num().reslove();
	}
}

    原文作者:九宫格问题
    原文地址: https://blog.csdn.net/cow__sky/article/details/8276508
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞