人工智能实验5——A*算法解决八数码难题
问题描述跟上一个实验一样,只是解法换为了A*算法
A*算法流程
1) G:=s; //算法开始时搜索图只包括初始状态节点
2) OPEN:=(s), CLOSE:=( ); //此时仅有s作为待扩展节点,而CLOSE表为空
3) 若OPEN是空表,则算法以失败结束;//因为此时并未搜索到解答(目标状态),但又无法继续搜索下去;
4) n:=MOVE-FIRST(OPEN)
5) 若n是目标状态节点,则搜索成功结束,并给出解答路径;
6) 扩展节点n,将非节点n祖先的子节点置于子节点集合SNS中,并插入搜索图G中;
7) 标记和修改指针:
把SNS中的子节点分为三类:
(1)全新节点——未曾在G中出现过, (2)已出现于OPEN表的节点,//未扩展,无子节点 (3)已出现于CLOSE表的节点;//已扩展,有子节点
将第1类子节点加入OPEN表,并建立从子节点到父节点n的指针;
比较第2类子节点经由新、老父节点到达初始状态节点s的路径代价,若经由新父节点的代价较小, 则修改子节点指向老父节点的指针,使之指向新父节点;
对于第3类子节点作与第2类同样的处理,并把这些子节点从CLOSE表中移出,重新加入OPEN表——推迟修改其后继节点指向父节点的指针,当重新对其进行扩展时再确定是否需要修改其后继节点指向父节点的指针;
8) 按某种原则重新排序OPEN表中的节点; 哪个节点到目标节点的评估函数小就先考察哪个节点
9) 返回3)
package com.Mytest; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.LinkedList; import java.util.PriorityQueue; import java.util.Queue; import java.util.Scanner; import java.util.Stack; public class test {//A星算法 class EightNumNode{ int[][] numMap; //eight num map int x; //the space coordinate in lateral int y; //the space coordinate in vertical int parentID; //the ID of parent int selfID; //the ID of itself int g; //the depth in the tree int h; //the num in the wrong places(or Manhattan Distance) int f; //f = g+h } public static EightNumNode beginStat; public static EightNumNode endStat; public static ArrayList<EightNumNode> openList; public static ArrayList<EightNumNode> closeList; public static Stack<EightNumNode> pathStack; public static int idCounter = 0; public int findSpaceX(int[][] M){ for(int i=0; i<M.length; i++) for(int j=0; j<M[0].length; j++) if(M[i][j] == 0) return i; return -1; } public int findSpaceY(int[][] M){ for(int i=0; i<M.length; i++) for(int j=0; j<M[0].length; j++) if(M[i][j] == 0) return j; return -1; } public boolean checkSame(int[][] A, int[][] B){ for(int i=0; i<A.length; i++) for(int j=0; j<A[0].length; j++) if(A[i][j] != B[i][j]) return false; return true; } public void printMap(int[][] M){ for(int i=0; i<M.length; i++){ for(int j=0; j<M[0].length; j++){ System.out.print(M[i][j]+" "); } System.out.println(); } } public int countH (int[][] A, int[][] B){//the num in the wrong places int hSum = 0; for(int i=0; i<A.length; i++){ for(int j=0; j<A[0].length; j++){ if(A[i][j] != B[i][j] && A[i][j] != 0){ hSum++; } } } return hSum; } /*public int countH (int[][] A, int[][] B){// Manhattan Distance int hSum = 0; for(int i=0; i<A.length; i++){ for(int j=0; j<A[0].length; j++){ if(A[i][j] != B[i][j] && A[i][j] != 0){ for(int m=0; m<B.length; m++){ for(int n=0; n<B[0].length; n++){ if(A[i][j] == B[m][n]){ hSum += Math.abs(i-m)+Math.abs(j-n); } } } } } } return hSum; }*/ public int checkInOpen(int[][] checkMap){ for(int i=0; i<openList.size(); i++){ if(checkSame(openList.get(i).numMap, checkMap)) return i; } return -1; } public int checkInClose(int[][] checkMap){ for(int i=0; i<closeList.size(); i++){ if(checkSame(closeList.get(i).numMap, checkMap)) return i; } return -1; } public int checkByID(ArrayList<EightNumNode> checkList, int checkID){ for(int i=0; i<checkList.size(); i++){ if(checkList.get(i).selfID == checkID) return i; } return -1; } public void sortedAdd(ArrayList<EightNumNode> eList, EightNumNode e){ int eIndex=0; for(; eIndex<eList.size() && eList.get(eIndex).f<=e.f ; eIndex++) ; eList.add(eIndex, e); } public void expendPointN(EightNumNode par){ if(par.y-1 >= 0){//move left EightNumNode son = new EightNumNode(); son.numMap = new int[3][3]; for(int i=0; i<par.numMap.length; i++) for(int j=0; j<par.numMap[0].length; j++) son.numMap[i][j] = par.numMap[i][j]; son.numMap[par.x][par.y] = son.numMap[par.x][par.y-1]; son.numMap[par.x][par.y-1] = 0; son.x = par.x; son.y = par.y-1; son.parentID = par.selfID; son.g = par.g + 1; son.h = countH(son.numMap, endStat.numMap); son.f = son.g + son.h; if(par.parentID == -1){//the root node son.selfID = idCounter++; sortedAdd(openList, son); } else { boolean flag = true; for(int i=0; i<closeList.size(); i++) if(checkSame(son.numMap, closeList.get(checkByID(closeList, par.parentID)).numMap)) flag = false; if(flag){//not N's parent int oIndex = checkInOpen(son.numMap); int cIndex = checkInClose(son.numMap); if( oIndex > -1){//in openList if(son.g < openList.get(oIndex).g){ son.selfID = openList.get(oIndex).selfID; openList.remove(oIndex); sortedAdd(openList, son); } } else if(cIndex > -1){//in closeList if(son.g < closeList.get(cIndex).g){ son.selfID = closeList.get(cIndex).selfID; closeList.remove(cIndex); sortedAdd(openList, son); } } else{//Neither in openList nor in closeList son.selfID = idCounter++; sortedAdd(openList, son); } } } } if(par.x-1 >= 0){//move up EightNumNode son = new EightNumNode(); son.numMap = new int[3][3]; for(int i=0; i<par.numMap.length; i++) for(int j=0; j<par.numMap[0].length; j++) son.numMap[i][j] = par.numMap[i][j]; son.numMap[par.x][par.y] = son.numMap[par.x-1][par.y]; son.numMap[par.x-1][par.y] = 0; son.x = par.x-1; son.y = par.y; son.parentID = par.selfID; son.g = par.g + 1; son.h = countH(son.numMap, endStat.numMap); son.f = son.g + son.h; if(par.parentID == -1){//the root node son.selfID = idCounter++; sortedAdd(openList, son); } else { boolean flag = true; for(int i=0; i<closeList.size(); i++) if(checkSame(son.numMap, closeList.get(checkByID(closeList, par.parentID)).numMap)) flag = false; if(flag){//not N's parent int oIndex = checkInOpen(son.numMap); int cIndex = checkInClose(son.numMap); if( oIndex > -1){//in openList if(son.g < openList.get(oIndex).g){ son.selfID = openList.get(oIndex).selfID; openList.remove(oIndex); sortedAdd(openList, son); } } else if(cIndex > -1){//in closeList if(son.g < closeList.get(cIndex).g){ son.selfID = closeList.get(cIndex).selfID; closeList.remove(cIndex); sortedAdd(openList, son); } } else{//Neither in openList nor in closeList son.selfID = idCounter++; sortedAdd(openList, son); } } } } if(par.y+1 < 3){//move right EightNumNode son = new EightNumNode(); son.numMap = new int[3][3]; for(int i=0; i<par.numMap.length; i++) for(int j=0; j<par.numMap[0].length; j++) son.numMap[i][j] = par.numMap[i][j]; son.numMap[par.x][par.y] = son.numMap[par.x][par.y+1]; son.numMap[par.x][par.y+1] = 0; son.x = par.x; son.y = par.y+1; son.parentID = par.selfID; son.g = par.g + 1; son.h = countH(son.numMap, endStat.numMap); son.f = son.g + son.h; if(par.parentID == -1){//the root node son.selfID = idCounter++; sortedAdd(openList, son); } else { boolean flag = true; for(int i=0; i<closeList.size(); i++) if(checkSame(son.numMap, closeList.get(checkByID(closeList, par.parentID)).numMap)) flag = false; if(flag){//not N's parent int oIndex = checkInOpen(son.numMap); int cIndex = checkInClose(son.numMap); if( oIndex > -1){//in openList if(son.g < openList.get(oIndex).g){ son.selfID = openList.get(oIndex).selfID; openList.remove(oIndex); sortedAdd(openList, son); } } else if(cIndex > -1){//in closeList if(son.g < closeList.get(cIndex).g){ son.selfID = closeList.get(cIndex).selfID; closeList.remove(cIndex); sortedAdd(openList, son); } } else{//Neither in openList nor in closeList son.selfID = idCounter++; sortedAdd(openList, son); } } } } if(par.x+1 < 3){//move down EightNumNode son = new EightNumNode(); son.numMap = new int[3][3]; for(int i=0; i<par.numMap.length; i++) for(int j=0; j<par.numMap[0].length; j++) son.numMap[i][j] = par.numMap[i][j]; son.numMap[par.x][par.y] = son.numMap[par.x+1][par.y]; son.numMap[par.x+1][par.y] = 0; son.x = par.x+1; son.y = par.y; son.parentID = par.selfID; son.g = par.g + 1; son.h = countH(son.numMap, endStat.numMap); son.f = son.g + son.h; if(par.parentID == -1){//the root node son.selfID = idCounter++; sortedAdd(openList, son); } else { boolean flag = true; for(int i=0; i<closeList.size(); i++) if(checkSame(son.numMap, closeList.get(checkByID(closeList, par.parentID)).numMap)) flag = false; if(flag){//not N's parent int oIndex = checkInOpen(son.numMap); int cIndex = checkInClose(son.numMap); if( oIndex > -1){//in openList if(son.g < openList.get(oIndex).g){ son.selfID = openList.get(oIndex).selfID; openList.remove(oIndex); sortedAdd(openList, son); } } else if(cIndex > -1){//in closeList if(son.g < closeList.get(cIndex).g){ son.selfID = closeList.get(cIndex).selfID; closeList.remove(cIndex); sortedAdd(openList, son); } } else{//Neither in openList nor in closeList son.selfID = idCounter++; sortedAdd(openList, son); } } } } } public void driver(int[][] beginM, int[][] endM){ openList = new ArrayList<test.EightNumNode>(); closeList = new ArrayList<test.EightNumNode>(); pathStack = new Stack<test.EightNumNode>(); //set the begin state beginStat = new EightNumNode(); beginStat.numMap = beginM; beginStat.x = findSpaceX(beginM); beginStat.y = findSpaceY(beginM); beginStat.parentID = -1; beginStat.selfID = idCounter++; beginStat.g = 0; beginStat.h = countH(beginM, endM); beginStat.f = beginStat.g + beginStat.h; //set the end state endStat = new EightNumNode(); endStat.numMap = endM; endStat.x = findSpaceX(endM); endStat.y = findSpaceY(endM); endStat.parentID = -1; sortedAdd(openList, beginStat); EightNumNode bestNode; while (!openList.isEmpty()) { bestNode = openList.remove(0); closeList.add(bestNode); if(checkSame(bestNode.numMap, endStat.numMap)){ System.out.println("Success!!!"); pathStack.push(bestNode); break; } else expendPointN(bestNode); } if (openList.isEmpty()) { System.out.println("Fail!!!"); } else { int tempParID = pathStack.peek().parentID; int tempParIndex; EightNumNode tempNode; while(tempParID != -1){ tempParIndex = checkByID(closeList, tempParID); tempNode = closeList.get(tempParIndex); pathStack.push(tempNode); tempParID = tempNode.parentID; } System.out.println("The path from beginning to end:"); int i=0; while(!pathStack.empty()){ System.out.println("step "+(i++)); printMap(pathStack.pop().numMap); System.out.println(); } } } public static void main(String[] args) { /*int[][] beginMap = { { 2, 8, 3 }, { 1, 6, 4 }, { 7, 0, 5 } }; int[][] endMap = { { 1, 2, 3 }, { 8, 0, 4 }, { 7, 6, 5 } };*/ Scanner scan = new Scanner(System.in); System.out.println("Please input the beginMAP:"); int[][] beginMap = new int[3][3]; for(int i=0; i<beginMap.length; i++) for(int j=0; j<beginMap[0].length; j++) beginMap[i][j] = scan.nextInt(); System.out.println("Please input the endMAP:"); int[][] endMap = new int[3][3]; for(int i=0; i<endMap.length; i++) for(int j=0; j<endMap[0].length; j++) endMap[i][j] = scan.nextInt(); test t = new test(); //t.driver(beginMap, endMap); if(t.checkSame(beginMap, endMap)){ System.out.println("The beginMap and endMap are the same."); System.out.println("No need to move!"); } else{ t.driver(beginMap, endMap); } } }
这里的open表本来我是想用PriorityQueue<E>,通过重写Comparable接口的compareTo()来达到自动排序,后来因为算法还要求查找某个节点在不在open表里,这样就连PriorityQueue里的add()和contains()也要一并重写,比较烦,所以直接用了ArrayList,在每次add前找到适当的插入位置来达到排序目的。
这里的启发函数是用f = g + h,其中g为节点深度,h为不在正确位置上的数字的个数(也有用曼哈顿距离的,我也写了,加注释那里,还有些f直接等于h)
回溯的过程是直接在close表里搜索相应的父节点,直到根节点为止,因为我把每个节点标了不同的ID号,所以这里是按照ID号来查找父节点的。