八數碼問題,A*算法,啟發函數


八數碼難題:設問題的初始狀態為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]
View Code

2021-06-04


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM