算法学习之8数码问题
这几天都在写人工智能和信息安全作业,没怎么学算法书,不过现在上的课也多多少少在学算法相关的,这次实验,我们人工智能老师就是要求我们解决八数码问题。
首先我们要知道什么是八数码问题:
八数码问题:在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。
8 | 5 | 4 |
3 | 7 | |
1 | 2 | 6 |
(a) 初始状态
1 | 2 | 3 |
8 | 4 | |
7 | 6 | 5 |
(b) 目标状态
要求:请任选一种盲目搜索算法(深度优先搜索或宽度优先搜索)或 任选一种启发式搜索方法(A 算法或 A* 算法)编程求解八数码问题(初始状态任选),并对实验结果进行分析,得出合理的结论。
这里我为了简单点用的bfs(宽度优先搜索),首先给出矩阵后为了方便处理(标记是否出现过)转为一维字符串形式,
/** * 把存储8数码的矩阵转换为状态字符串 * @param Matrix 8数码存储矩阵 * 状态字符串 */ private String convertToStrState(int[][] Matrix) { String string = ""; for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { string += Matrix[i][j]; } } return string; }
然后判断由初始矩阵转目标矩阵是否有解,如何判断是否有解我在网上找了下,是判断初始矩阵的状态字符串的逆序数与目标矩阵的状态字符串的逆序数是否同奇或者同偶,如果同是奇数或同是偶数就有解。否者无解。
/** * 判断是否可以由初始的8数码状态到目标状态 * @param startMatrix 初始化8数码的状态矩阵 * 是否可以求解 */ private boolean isCanSolve(int[][] startMatrix) { // System.out.println(countInverseNumber(startMatrix)); // System.out.println(countInverseNumber(targetMatrix)); if(countInverseNumber(startMatrix)%2==1&&countInverseNumber(targetMatrix)%2==1)return true; else return false; }
如果有解,就开始BFS了,首先把初始状态矩阵放入队列中(这里为了处理方便先把矩阵转为状态字符串再放入的队列),然后循环弹出队列,直到队列为空。【每弹出一个矩阵状态字符串,就把这个矩阵的子矩阵状态的状态字符串放入队列中,矩阵的子矩阵状态就是当前矩阵把0与上、下、左、右、四个位置的数(如果有的话)互换后的矩阵。在放子矩阵状态字符串到队列的时候还要先判断下这个矩阵是否重复出现过。】
全部代码吧。
1 package gh; 2 3 import java.io.IOException; 4 import java.util.HashMap; 5 import java.util.HashSet; 6 import java.util.Map; 7 import java.util.Queue; 8 import java.util.Scanner; 9 import java.util.Set; 10 import java.util.Stack; 11 import java.util.concurrent.LinkedBlockingDeque; 12 13 /** 14 * 八数码问题 15 * @author ganhang 16 * 17 */ 18 public class EightPuzzle 19 { 20 private String targetState = "123804765";//目标状态 21 private int[][] targetMatrix = {{1,2,3},{8,0,4},{7,6,5}};//目标矩阵 22 private Set<String> hashState = new HashSet<String>();//判断状态是否出现 23 private int[] dx = {-1,0,1,0}; 24 private int[] dy = {0,1,0,-1}; 25 private Map<String, String> path = new HashMap<String, String>(); 26 private int step = 0; 27 public static void main(String[] args) throws IOException, Exception { 28 EightPuzzle eightPuzzle = new EightPuzzle(); 29 int[][] startMatrix = new int[3][3]; 30 Scanner scanner = new Scanner(System.in); 31 System.out.println("请输入初始状态:"); 32 for (int i = 0; i < 3; i++) { 33 for (int j = 0; j < 3; j++) { 34 startMatrix[i][j] = scanner.nextInt(); 35 } 36 } 37 38 eightPuzzle.searchSolution(startMatrix); 39 40 } 41 /** 42 * 求状态矩阵除去0后的逆序数 43 * @param Matrix 状态矩阵 44 * 45 */ 46 private int countInverseNumber(int[][] Matrix) 47 { 48 int[] tmpElem = new int[9]; 49 int size = 0; 50 for(int i = 0; i < 3; i++) 51 { 52 for(int j = 0; j < 3; j++) 53 { 54 if(Matrix[i][j] != 0) 55 { 56 tmpElem[size++] = Matrix[i][j]; 57 } 58 } 59 } 60 int ans = 0; 61 for(int i = 0; i < size; i++) 62 { 63 for(int j = i+1; j < size; j++) 64 { 65 if(tmpElem[i] > tmpElem[j]) 66 { 67 ans++; 68 } 69 } 70 } 71 return ans; 72 } 73 74 /** 75 * 判断是否可以由初始的8数码状态到目标状态 76 * @param startMatrix 初始化8数码的状态矩阵 77 * 是否可以求解 78 */ 79 private boolean isCanSolve(int[][] startMatrix) 80 { 81 82 // System.out.println(countInverseNumber(startMatrix)); 83 // System.out.println(countInverseNumber(targetMatrix)); 84 if(countInverseNumber(startMatrix)%2==1&&countInverseNumber(targetMatrix)%2==1)return true; 85 else return false; 86 } 87 88 /** 89 * 把存储8数码的矩阵转换为状态字符串 90 * @param Matrix 8数码存储矩阵 91 * 状态字符串 92 */ 93 private String convertToStrState(int[][] Matrix) 94 { 95 String string = ""; 96 for(int i = 0; i < 3; i++) 97 { 98 for(int j = 0; j < 3; j++) 99 { 100 string += Matrix[i][j]; 101 } 102 } 103 return string; 104 } 105 /** 106 * 把状态字符串转换为8数码的矩阵 107 * @param state 状态字符串 108 *8数码矩阵 109 */ 110 private int[][] convertToMatrix(String state) 111 { 112 int[][] matrix = new int[3][3]; 113 for(int i = 0; i < state.length(); i++) 114 { 115 matrix[i/3][i%3] = state.charAt(i) - '0'; 116 } 117 return matrix; 118 } 119 120 /** 121 * 打印路径 122 */ 123 private void printPath() 124 { 125 126 Stack<String> stack = new Stack<String>(); 127 String state = targetState; 128 while(state != null) 129 { 130 stack.push(state); 131 state = path.get(state); 132 step++; 133 } 134 System.out.println("\nOk, I find it!\n"); 135 System.out.println("一共使用了" + (step-1) + "步\n"); 136 while(!stack.isEmpty()) 137 { 138 printMatrix(convertToMatrix(stack.pop())); 139 } 140 } 141 142 /** 143 * 打印8数码矩阵 144 * @param matrix 8数码矩阵 145 */ 146 private void printMatrix(int[][] matrix) 147 { 148 for(int i = 0; i < 3; i++) 149 { 150 for(int j = 0; j < 3; j++) 151 { 152 System.out.print(matrix[i][j] + " "); 153 } 154 System.out.println(""); 155 } 156 System.out.println(""); 157 } 158 159 /** 160 * BFS搜索可行解 161 * @param startMatrix 开始的8数码矩阵 162 */ 163 public void searchSolution(int[][] startMatrix) 164 { 165 if (!isCanSolve(startMatrix)) System.out.println("开始状态到目标状态无解!"); 166 else { 167 Queue<String> queue = new LinkedBlockingDeque<String>(); 168 169 queue.add(convertToStrState(startMatrix));// 初始状态放入队列 170 hashState.add(convertToStrState(startMatrix));// 标记初始状态存在 171 path.put(convertToStrState(startMatrix), null); 172 173 while (!queue.isEmpty())// 队列非空 ,进行BFS 174 { 175 String curState = queue.poll(); 176 int[][] curMatrix = convertToMatrix(curState); 177 178 if (curState.equals(targetState))// 找到目标状态 179 { 180 break; 181 } 182 int curx = 0, cury = 0; 183 184 for (int i = 0; i < 3; i++)// 查找 0 的位置 185 { 186 for (int j = 0; j < 3; j++) { 187 if (curMatrix[i][j] == 0) { 188 curx = i; 189 cury = j; 190 break; 191 } 192 } 193 } 194 195 String newState = "";// 记录新状态 196 int[][] newMatrix = new int[3][3];// 记录新状态矩阵 197 for (int i = 0; i < 4; i++)// BFS 相邻状态 198 { 199 int newx = curx + dx[i]; 200 int newy = cury + dy[i]; 201 if (newx <= 2 && newx >= 0 && newy <= 2 && newy >= 0)// 状态合法 202 { 203 204 for (int j = 0; j < 3; j++) { 205 System.arraycopy(curMatrix[j], 0, newMatrix[j], 0, 206 curMatrix[j].length); 207 } 208 209 int temp = newMatrix[newx][newy]; 210 newMatrix[newx][newy] = newMatrix[curx][cury]; 211 newMatrix[curx][cury] = temp; 212 213 newState = convertToStrState(newMatrix); 214 215 if (!hashState.contains(newState))// 如果改状态还未到达过 216 { 217 path.put(newState, curState); 218 queue.add(newState);// 把新状态压入队列 219 hashState.add(newState);// 将新状态存入Hash 220 } 221 } 222 } 223 } 224 printPath();// 打印路径 225 } 226 } 227 }
随便写写。一点学习心得。。。--如果本文章没有注明转载则为原创文章,可以随意复制发表,但请注明出处与作者