1. 程式人生 > >N數碼問題的啟發式搜索算法--A*算法python實現

N數碼問題的啟發式搜索算法--A*算法python實現

ima 最終 turn 特點 函數的一般形式 strong image 開始 控制臺

  • 一、啟發式搜索:A算法

1)評價函數的一般形式 : f(n) = g(n) + h(n)

g(n):從S0到Sn的實際代價(搜索的橫向因子)

h(n):從N到目標節點的估計代價,稱為啟發函數(搜索的縱向因子);

特點: 效率高, 無回溯,

搜索算法

OPEN表 : 存放待擴展的節點.

CLOSED表 : 存放已被擴展過的節點.

2)評價函數 f(x) = g(x) + h(x)

f(x) = g(x) 時,為寬度優先搜索

f(x) = 1/g(x)時,為深度優先搜索

f(x) = h(x) 時,為全局優先搜索

比較f(x)大小,決定節點搜索順序,即在OPEN表中的順序

3)Step1: 把初始節點S0放入OPEN表中;

Step2: 若OPEN表為空,則搜索失敗,退出.

Step3: 移出OPEN中第一個節點N放入CLOSED表, 並標以順序號n;

Step4: 若目標節點Sg=N, 則搜索成功,結束.

Step5: 若N不可擴展, 則轉Step2;

Step6: 擴展N, 生成一組子節點, 對這組子節點作如下處理後, 放入 OPEN表, 按f值重新排序OPEN表, 轉 Step2;

刪除重復節點和修改返回指針處理.

  • 二、啟發式搜索:A*算法

1)評價函數的一般形式:

f(n) = g(n) + h(n) 且 h(n) <= h*(n)

g(n),h(n):定義同A算法;

h*(n):從N到目標節點的最短路徑; 稱此時的A算法為A*算法.

2)程序關鍵點

節點的擴展:close表存放已經擴展後的狀態,open表存放未擴展的狀態。首先獲取節點能擴展的方向,擴展後將父節點放入close表中,如果轉移之後的節點,既不在close表也不再open表,表明該節點還未被擴展,則插入open表,如果在close表中表明之前已經擴展過該狀態,為了避免無限擴展應將該狀態從open表舍棄,如果在open表則比較這兩個矩陣的f值(選取最優解),留小的在open表,之後對open表中的節點根據f值進行排序,pop出f值最小的節點進行擴展,依次進行該過程,直至該節點為目標狀態。
解的路徑的輸出:通過目標狀態節點向上回溯找其父節點,直至開始狀態。

  • 三、python代碼實現
  1 # -*- coding: utf-8 -*-
  2 """
  3 Created on Sun Sep 16 14:31:40 2018
  4 A*算法解決N數碼問題
  5 運行程序後如下是輸入格式:
  6         請輸入矩陣的行數
  7         
  8         3                            輸入對應的N
  9         請輸入初始矩陣A               
 10         
 11         1 0 2                        一行行輸入,每行數字空格隔開,每行最後一個數字輸入完成後直接回車開始輸入第二行
 12         
 13         4 5 6
 14         
 15         3 7 8
 16         請輸入目標矩陣B
 17         
 18         1 2 3
 19         
 20         8 0 4
 21         
 22         7 6 5
 23     
 24 """
 25 import numpy as np
 26 import copy
 27 import time
 28 from operator import itemgetter
 29 
 30 goal = {}
 31 
 32 def get_location(vec, num):    #根據num元素獲取num在矩陣中的位置
 33     row_num = vec.shape[0]     #numpy-shape函數獲得矩陣的維數
 34     line_num = vec.shape[1]
 35     
 36     for i in range(row_num):
 37         for j in range(line_num):
 38             if num == vec[i][j]:
 39                 return i, j
 40 
 41 def get_actions(vec):    #獲取當前位置可以移動的下一個位置,返回移動列表
 42     row_num = vec.shape[0]
 43     line_num = vec.shape[1]
 44     
 45     (x, y) = get_location(vec, 0)    #獲取0元素的位置
 46     action = [(0, 1), (0, -1), (1, 0), (-1, 0)]
 47     
 48     if x == 0:    #如果0在邊緣則依據位置情況,減少0的可移動位置
 49         action.remove((-1, 0))
 50     if y == 0:
 51         action.remove((0, -1))
 52     if x == row_num - 1:
 53         action.remove((1, 0))
 54     if y == line_num - 1:
 55         action.remove((0, 1))
 56         
 57     return list(action)
 58 
 59 def result(vec, action):    #移動元素,進行矩陣轉化
 60      (x, y) = get_location(vec, 0)    #獲取0元素的位置
 61      (a, b) = action    #獲取可移動位置
 62                                  
 63      n = vec[x+a][y+b]    #位置移動,交換元素
 64      s = copy.deepcopy(vec)
 65      s[x+a][y+b] = 0
 66      s[x][y] = n
 67      
 68      return s
 69     
 70 def get_ManhattanDis(vec1, vec2):    #計算兩個矩陣的曼哈頓距離,vec1為目標矩陣,vec2為當前矩陣
 71     row_num = vec1.shape[0]
 72     line_num = vec1.shape[1]
 73     dis  = 0
 74     
 75     for i in range(row_num):
 76         for j in range(line_num):
 77             if vec1[i][j] != vec2[i][j] and vec2[i][j] != 0:
 78                 k, m = get_location(vec1, vec2[i][j])
 79                 d = abs(i - k) + abs(j - m)
 80                 dis += d
 81                 
 82     return dis
 83 
 84 def expand(p, actions, step):                          #actions為當前矩陣的可擴展狀態列表,p為當前矩陣,step為已走的步數
 85     children = []                                      #children用來保存當前狀態的擴展節點
 86     for action in actions:
 87         child = {}
 88         child[parent] = p
 89         child[vec] = (result(p[vec], action))
 90         child[dis] = get_ManhattanDis(goal[vec], child[vec])
 91         child[step] = step + 1                       #每擴展一次當前已走距離加1
 92         child[dis] = child[dis] + child[step]    #更新該節點的f值  f=g+h(step+child[dis])                     
 93         child[action] = get_actions(child[vec])
 94         children.append(child)
 95     
 96     return children
 97 
 98 def node_sort(nodelist):    #按照節點中字典的距離字段對列表進行排序,從大到小
 99     return sorted(nodelist, key = itemgetter(dis), reverse=True)
100 
101 def get_input(num):
102     A = []
103     for i in range(num):
104         temp = []
105         p = []
106         s = input()
107         temp = s.split( )
108         for t in temp:
109             t = int(t)
110             p.append(t)
111         A.append(p)
112    
113     return A  
114 
115 def get_parent(node):
116     q = {}
117     q = node[parent]   
118     return q
119         
120 def test():
121     openlist = []    #open表
122     close = []       #存儲擴展的父節點
123     
124     print(請輸入矩陣的行數)
125     num = int(input())  
126     
127     print("請輸入初始矩陣A")
128     A = get_input(num)
129  
130     print("請輸入目標矩陣B")
131     B = get_input(num)
132     
133     print("請輸入結果文件名")
134     resultfile = input()
135     
136     goal[vec] = np.array(B)   #建立矩陣
137    
138     p = {}
139     p[vec] = np.array(A)
140     p[dis] = get_ManhattanDis(goal[vec], p[vec])
141     p[step] = 0
142     p[action] = get_actions(p[vec])
143     p[parent] = {}
144 
145     if (p[vec] == goal[vec]).all():
146         return
147     
148     openlist.append(p)
149     
150     start_CPU = time.clock()    #開始擴展時CPU開始計算
151     
152     while openlist:
153         
154         children = []
155         
156         node = openlist.pop()    #node為字典類型,pop出open表的最後一個元素
157         close.append(node)  #將該元素放入close表
158       
159         if (node[vec] == goal[vec]).all():    #比較當前矩陣和目標矩陣是否相同
160             end_CPU = time.clock()    #CPU結束計算
161          
162             h = open(resultfile,w,encoding=utf-8,)  #將結果寫入文件  並在控制臺輸出
163             h.write(搜索樹規模: + str(len(openlist)+len(close)) + \n)
164             h.write(close: + str(len(close)) + \n)
165             h.write(openlist: + str(len(openlist)) + \n)
166             h.write(cpu運行時間: + str(end_CPU - start_CPU) + \n)
167             h.write(路徑長: + str(node[dis]) + \n)
168             
169             h.write(解的路徑: + \n)
170             i = 0
171             way = []
172             while close:
173                 way.append(node[vec])  #從最終狀態開始依次向上回溯將其父節點存入way列表中
174                 node = get_parent(node)
175                 if(node[vec] == p[vec]).all():
176                     way.append(node[vec])
177                     break
178             while way:
179                 i += 1
180                 h.write(str(i) + \n)
181                 h.write(str(way.pop()) + \n)
182             h.close()
183             f = open(resultfile,r,encoding=utf-8,)
184             print(f.read())
185             
186             return
187         
188         children = expand(node, node[action], node[step])    #如果不是目標矩陣,對當前節點進行擴展,取矩陣的可能轉移情況
189         
190         for child in children:     #如果轉移之後的節點,既不在close表也不再open表則插入open表,如果在close表中則舍棄,如果在open表則比較這兩個矩陣的f值,留小的在open表
191             f = False
192             flag = False
193             j = 0
194             for i in range(len(openlist)):
195                 if (child[vec] == openlist[i][vec]).all():
196                     j = i
197                     flag = True
198                     break
199             for i in range(len(close)):
200                 if(child[vec] == close[i]).all():
201                     f = True
202                     break
203             if  f == False and flag == False :
204                 openlist.append(child)
205                 
206             elif flag == True:
207                 if child[dis] < openlist[j][dis]:
208                     del openlist[j]
209                     openlist.append(child)
210                     
211         
212         openlist = node_sort(openlist)   #對open表進行從大到小排序
213     
214 test()

  • 四、程序運行結果如下圖所示

技術分享圖片

1

技術分享圖片

2

技術分享圖片

3

  • 五、總結

通過這次編程了解到了搜索具有探索性,要提高搜索效率(盡快地找到目標節點),或要找最佳路徑(最佳解)就必須註意搜索策略。對於狀態圖搜索,已經提出了許多策略,它們大體可分為盲目搜索(bland search)和啟發式搜索(heuristic search)兩大類。其中盲目搜索是無向導搜索。啟發式搜索是有向導搜索,即利用啟發信息(函數)引導去尋找問題解。通過A*算法解決N數碼問題實驗過程中也遇到很多問題,比如節點擴展的方向問題等,通過這次實驗不僅鍛煉了自己python編程能力,也讓自己對N數碼求解最優路徑問題有了更清晰的認識,希望自己能在老師和同學的幫助下,能不斷進步,當然最重要的是自己得付出,只會幻想而不行動的人,永遠也體會不到收獲果實時的喜悅。加油!!

N數碼問題的啟發式搜索算法--A*算法python實現