這幾天都在寫人工智能和信息安全做業,沒怎麼學算法書,不過如今上的課也多多少少在學算法相關的,此次實驗,咱們人工智能老師就是要求咱們解決八數碼問題。java
首先咱們要知道什麼是八數碼問題:算法
八數碼問題:在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* 算法)編程求解八數碼問題(初始狀態任選),並對實驗結果進行分析,得出合理的結論。spa
這裏我爲了簡單點用的bfs(寬度優先搜索),首先給出矩陣後爲了方便處理(標記是否出現過)轉爲一維字符串形式,code
/** * 把存儲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; }
而後判斷由初始矩陣轉目標矩陣是否有解,如何判斷是否有解我在網上找了下,是判斷初始矩陣的狀態字符串的逆序數與目標矩陣的狀態字符串的逆序數是否同奇或者同偶,若是同是奇數或同是偶數就有解。否者無解。blog
/** * 判斷是否能夠由初始的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 }