八數碼難題:設問題的初始狀態為S0和目標狀態Sg,如圖所示。請用A*算法求解。(定義兩種以上的評估函數,分別給出搜索樹和計算過程,並進行不同評估函數的對比分析)
初始狀態 目標狀態
2 |
8 |
3 |
|
1 |
2 |
3 |
1 |
|
4 |
|
8 |
|
4 |
7 |
6 |
5 |
|
7 |
6 |
5 |
啟發函數(3種啟發函數,可以比較優劣):
1 def Heuristic(self, list_end): # 啟發函數, list_end是目標狀態的八數碼 2 self.key = self.h = 0 3 '''啟發函數中h(n)的求解''' 4 # ''' 1. n狀態時,不在目標狀態的數碼個數 5 for i in range(9): 6 self.h += 0 if self.digits[i] == list_end[i] else 1 7 # ''' 8 9 # '''2. n狀態時,不在目標狀態的數碼移動到目標狀態時的距離--曼哈頓距離 10 for i in range(1, 9): 11 idx_end = list_end.index(i) 12 idx_self = self.digits.index(i) 13 self.h += abs(idx_end - idx_self) // 3 + abs(idx_end - idx_self - (idx_end - idx_self) // 3 * 3) 14 # ''' 15 16 # '''3. 數碼排成一行,n狀態時,不在目標狀態的數碼移動到目標狀態時的距離 17 for i in range(1, 9): 18 idx_end = list_end.index(i) 19 idx_self = self.digits.index(i) 20 self.h += abs(idx_end - idx_self) 21 # ''' 22 self.key = self.h + self.d # 啟發函數
利用優先隊列求解八數碼問題:
結構體:包含啟發函數,數碼狀態,以及解的路徑。選用其中一種啟發函數。
1 # 設計八數碼問題的數據結構 2 class Eight_Node: 3 def __init__(self): 4 self.key = 0 # 啟發函數 key = f(n) = d(n) + h(n) 5 self.h = 0 # h(n),根據Heuristic(self, list_end)函數求解h(n) 與 f(n) 6 self.d = 0 # 樹高 7 self.digits = list()*9 # 矩陣,一行表示 8 self.solutionTree = list() # 解的過程 9 10 def __gt__(self, other): 11 return self.key > other.key 12 13 def Heuristic(self, list_end): # 啟發函數, list_end是目標狀態的八數碼 14 self.key = self.h = 0 15 '''啟發函數中h(n)的求解''' 16 ''' 1. n狀態時,不在目標狀態的數碼個數 17 for i in range(9): 18 self.h += 0 if self.digits[i] == list_end[i] else 1 19 ''' 20 21 '''2. n狀態時,不在目標狀態的數碼移動到目標狀態時的距離--曼哈頓距離 22 for i in range(1, 9): 23 idx_end = list_end.index(i) 24 idx_self = self.digits.index(i) 25 self.h += abs(idx_end - idx_self) // 3 + abs(idx_end - idx_self - (idx_end - idx_self) // 3 * 3) 26 ''' 27 28 # '''3. 數碼排成一行,n狀態時,不在目標狀態的數碼移動到目標狀態時的距離 29 for i in range(1, 9): 30 idx_end = list_end.index(i) 31 idx_self = self.digits.index(i) 32 self.h += abs(idx_end - idx_self) 33 # ''' 34 self.key = self.h + self.d # 啟發函數
解函數:
1 def Solve_Eight_Node(start_list1, end_list2): # 函數求解 2 start_node = Eight_Node() # 初始狀態 3 end_node = Eight_Node() # 目標狀態 4 # 初始狀態start_node, 目標狀態end_node,構建八數碼 5 start_node.digits = start_list1 6 start_node.solutionTree.append(start_node.digits) 7 end_node.digits = end_list2 8 start_node.Heuristic(end_node.digits) 9 # close表 10 dic = [[-1, 0], [1, 0], [0, -1], [0, 1]] # 上、下、左、右--數碼移動 11 close_base = list() # close_base記錄所有使用過的狀態,也可用於去重 12 close_base.append(''.join(str(i) for i in start_node.digits)) 13 14 # 優先隊列,以啟發函數升序 15 priQu = PriorityQueue() # open表,優先隊列實現 16 priQu.put(start_node) 17 # 開始搜索 18 conclusion = Eight_Node() # 記錄搜索到的結果 19 while not priQu.empty(): 20 node = priQu.get() 21 if node.h == 0: # 查找到目標狀態,結束,啟發函數為0,和目標狀態一樣 22 conclusion = node 23 break 24 # 數碼進行移動,處理四個方向 25 idx = node.digits.index(0) 26 x = idx // 3 # 0 於八數碼位置的坐標(0,0) <= (x,y) <= (2,2) 27 y = idx - 3 * x 28 for i in range(4): # 0,1,2,3,分別和上下左右的方塊交換 29 new_x = x + dic[i][0] 30 new_y = y + dic[i][1] 31 if 0 <= new_x <= 2 and 0 <= new_y <= 2: # 未超過邊界 32 new_node = Eight_Node() 33 new_node.digits = copy.deepcopy(node.digits) # 深度復制八數碼矩陣 34 new_node.solutionTree = copy.deepcopy(node.solutionTree) # 深度復制八數碼解過程 35 new_node.digits[idx] = new_node.digits[new_x * 3 + new_y] # 數碼移動 36 new_node.digits[new_x * 3 + new_y] = 0 37 new_node.d = node.d + 1 # 樹高加一 38 new_node.Heuristic(end_node.digits) # 計算key,h,啟發函數 39 new_node.solutionTree.append(new_node.digits) # 解過程增加一個狀態 40 node_str = ''.join(str(j) for j in new_node.digits) 41 if node_str not in close_base: # 判重 42 close_base.append(node_str) 43 priQu.put(new_node) 44 return conclusion
測試:
解空間樹:
全部代碼;

1 from queue import PriorityQueue 2 import copy 3 4 # 設計八數碼問題的數據結構 5 class Eight_Node: 6 def __init__(self): 7 self.key = 0 # 啟發函數 key = f(n) = d(n) + h(n) 8 self.h = 0 # h(n),根據Heuristic(self, list_end)函數求解h(n) 與 f(n) 9 self.d = 0 # 樹高 10 self.digits = list()*9 # 矩陣,一行表示 11 self.solutionTree = list() # 解的過程 12 13 def __gt__(self, other): 14 return self.key > other.key 15 16 def Heuristic(self, list_end): # 啟發函數, list_end是目標狀態的八數碼 17 self.key = self.h = 0 18 '''啟發函數中h(n)的求解''' 19 ''' 1. n狀態時,不在目標狀態的數碼個數 20 for i in range(9): 21 self.h += 0 if self.digits[i] == list_end[i] else 1 22 ''' 23 24 '''2. n狀態時,不在目標狀態的數碼移動到目標狀態時的距離--曼哈頓距離 25 for i in range(1, 9): 26 idx_end = list_end.index(i) 27 idx_self = self.digits.index(i) 28 self.h += abs(idx_end - idx_self) // 3 + abs(idx_end - idx_self - (idx_end - idx_self) // 3 * 3) 29 ''' 30 31 # '''3. 數碼排成一行,n狀態時,不在目標狀態的數碼移動到目標狀態時的距離 32 for i in range(1, 9): 33 idx_end = list_end.index(i) 34 idx_self = self.digits.index(i) 35 self.h += abs(idx_end - idx_self) 36 # ''' 37 self.key = self.h + self.d # 啟發函數 38 39 40 def Solve_Eight_Node(start_list1, end_list2): # 函數求解 41 start_node = Eight_Node() # 初始狀態 42 end_node = Eight_Node() # 目標狀態 43 # 初始狀態start_node, 目標狀態end_node,構建八數碼 44 start_node.digits = start_list1 45 start_node.solutionTree.append(start_node.digits) 46 end_node.digits = end_list2 47 start_node.Heuristic(end_node.digits) 48 # close表 49 dic = [[-1, 0], [1, 0], [0, -1], [0, 1]] # 上、下、左、右--數碼移動 50 close_base = list() # close_base記錄所有使用過的狀態,也可用於去重 51 close_base.append(''.join(str(i) for i in start_node.digits)) 52 53 # 優先隊列,以啟發函數升序 54 priQu = PriorityQueue() # open表,優先隊列實現 55 priQu.put(start_node) 56 # 開始搜索 57 conclusion = Eight_Node() # 記錄搜索到的結果 58 while not priQu.empty(): 59 node = priQu.get() 60 if node.h == 0: # 查找到目標狀態,結束,啟發函數為0,和目標狀態一樣 61 conclusion = node 62 break 63 # 數碼進行移動,處理四個方向 64 idx = node.digits.index(0) 65 x = idx // 3 # 0 於八數碼位置的坐標(0,0) <= (x,y) <= (2,2) 66 y = idx - 3 * x 67 for i in range(4): # 0,1,2,3,分別和上下左右的方塊交換 68 new_x = x + dic[i][0] 69 new_y = y + dic[i][1] 70 if 0 <= new_x <= 2 and 0 <= new_y <= 2: # 未超過邊界 71 new_node = Eight_Node() 72 new_node.digits = copy.deepcopy(node.digits) # 深度復制八數碼矩陣 73 new_node.solutionTree = copy.deepcopy(node.solutionTree) # 深度復制八數碼解過程 74 new_node.digits[idx] = new_node.digits[new_x * 3 + new_y] # 數碼移動 75 new_node.digits[new_x * 3 + new_y] = 0 76 new_node.d = node.d + 1 # 樹高加一 77 new_node.Heuristic(end_node.digits) # 計算key,h,啟發函數 78 new_node.solutionTree.append(new_node.digits) # 解過程增加一個狀態 79 node_str = ''.join(str(j) for j in new_node.digits) 80 if node_str not in close_base: # 判重 81 close_base.append(node_str) 82 priQu.put(new_node) 83 return conclusion 84 85 86 if __name__ == "__main__": 87 start_list = list(map(int, input("請輸入處於初始狀態的八數碼(一行輸入即可):").split(","))) 88 end_list = list(map(int, input("請輸入處於目標狀態的八數碼(一行輸入即可):").split(","))) 89 result = Solve_Eight_Node(start_list, end_list) 90 print("八數碼問題的求解:") 91 print("初始狀態 目標狀態") 92 for i in range(3): 93 print(start_list[3 * i:3 * i + 3], ' ', end_list[3 * i:3 * i + 3]) 94 print("求解樹:") 95 index_i = 0 96 for li in result.solutionTree: 97 print("step # %d :" % index_i) 98 for i in range(3): 99 print(li[3 * i:3 * i + 3]) 100 print("-----------") 101 index_i += 1 102 # 2,8,3,1,0,4,7,6,5 [0,1,2,3,4,5,6,7,8] [1,2,3,8,0,4,7,6,5] [0,1,2,3,4,5,6,7,8] 103 # 1,2,3,8,0,4,7,6,5 [8,7,6,5,4,3,2,1,0] [2,1,6,4,0,8,7,5,3] [8,7,6,5,4,3,2,1,0]
2021-06-04