人工智能实验4——用盲目搜索求解八数码问题
八数码问题:在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。
广度优先搜索算法流程:
1. 把起始节点放到OPEN表中(如果该起始节点为一目标节点,则求得一个解答)。
2. 如果OPEN是个空表,则没有解,失败退出;否则继续。
3. 把第一个节点(节点n)从OPEN表移出,并把它放入CLOSED的扩展节点表中。 扩展节点n。如果没有后继节点,则转向上述第(2)步。
4. 把n的所有后继节点放到OPEN表末端,并提供从这些后继节点回到n的指针。
5. 如果n的任一个后继节点是个目标节点,则找到一个解答,成功退出;否则转向第(2)步。
package com.Mytest; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stack; public class test {//广度优先搜索 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(the ID is the index in the closeList) int selfID; //the ID of itself } public static EightNumNode beginStat; public static EightNumNode endStat; public static Queue<EightNumNode> openList; public static ArrayList<EightNumNode> closeList; public static Stack<EightNumNode> pathStack; 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 boolean 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; if(par.parentID == -1){ openList.offer(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.offer(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } 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; if(par.parentID == -1){ openList.offer(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.offer(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } 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; if(par.parentID == -1){ openList.offer(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.offer(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } 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; if(par.parentID == -1){ openList.offer(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.offer(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } return false; } public void driver(int[][] beginM, int[][] endM){ openList = new LinkedList<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; //set the end state endStat = new EightNumNode(); endStat.numMap = endM; endStat.x = findSpaceX(endM); endStat.y = findSpaceY(endM); endStat.parentID = -1; openList.offer(beginStat); EightNumNode n; int idcount = 0; while (!openList.isEmpty()) { n = openList.remove(); closeList.add(n); n.selfID = idcount++; if (expendPointN(n)) { System.out.println("Success!!!"); break; } } if (openList.isEmpty()) { System.out.println("Fail!!!"); } else { int tempParID = pathStack.peek().parentID; EightNumNode tempNode; while(tempParID != -1){ tempNode = closeList.get(tempParID); 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) { 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(); /* int[][] beginMap = { { 2, 8, 3 }, { 1, 0, 4 }, { 7, 6, 5 } }; int[][] endMap = { { 1, 2, 3 }, { 8, 0, 4 }, { 7, 6, 5 } }; */ test t = new test(); 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); } } }
深度优先搜索算法流程:
1. 把起始节点放到OPEN表中(如果该起始节点为一目标节点,则求得一个解答)。
2. 如果OPEN是个空表,则没有解,失败退出;否则继续。
3. 把第一个节点(节点n)从OPEN表移出,并把它放入CLOSED的扩展节点表中。
4. 考察节点n是否为目标节点,若是,则找到问题的解,用回溯法求解路径,退出
5. 如果没有后继节点,则转向上述第(2)步。
6. 扩展节点n,把n的所有后继节点放到OPEN表前端,并提供从这些后继节点回到n的指针。转向第(2)步。
package com.Mytest; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stack; public class test2 {//深度优先搜索 class EightNumNode{ int[][] numMap; int x; int y; int parentID; int selfID; } public static EightNumNode beginStat; public static EightNumNode endStat; public static Stack<EightNumNode> openList; public static ArrayList<EightNumNode> closeList; public static Stack<EightNumNode> pathStack; 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 boolean expendPointN(EightNumNode par){ 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; if(par.parentID == -1){ openList.push(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.push(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } 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; if(par.parentID == -1){ openList.push(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.push(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } 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; if(par.parentID == -1){ openList.push(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.push(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } 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; if(par.parentID == -1){ openList.push(son); } else { if(!checkSame(son.numMap, closeList.get(par.parentID).numMap)) openList.push(son); } if(checkSame(son.numMap, endStat.numMap)){ pathStack.push(son); return true; } } return false; } public void driver(int[][] beginM, int[][] endM){ openList = new Stack<EightNumNode>(); closeList = new ArrayList<EightNumNode>(); pathStack = new Stack<EightNumNode>(); beginStat = new EightNumNode(); beginStat.numMap = beginM; beginStat.x = findSpaceX(beginM); beginStat.y = findSpaceY(beginM); beginStat.parentID = -1; endStat = new EightNumNode(); endStat.numMap = endM; endStat.x = findSpaceX(endM); endStat.y = findSpaceY(endM); endStat.parentID = -1; openList.push(beginStat); EightNumNode n; int idcount = 0; while (!openList.isEmpty()) { n = openList.pop(); closeList.add(n); n.selfID = idcount++; if (expendPointN(n)) { System.out.println("Success!!!"); break; } } if (openList.isEmpty()) { System.out.println("Fail!!!"); } else { int tempParID = pathStack.peek().parentID; EightNumNode tempNode; while(tempParID != -1){ tempNode = closeList.get(tempParID); 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) { 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(); /* int[][] beginMap = { { 2, 8, 3 }, { 1, 0, 4 }, { 7, 6, 5 } }; int[][] endMap = { { 1, 2, 3 }, { 8, 0, 4 }, { 7, 6, 5 } }; */ test t = new test(); 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表用队列,扩展节点N的顺序是左上右下 深度:open表用栈,扩展节点N的顺序是下右上左
有个很好的PPT可以帮助理解 http://wenku.baidu.com/view/6fb5e40ebb68a98271fefa4b.html