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

  • 1、啓發式搜索:A算法

1)評價函數的通常形式 : f(n) = g(n) + h(n)node

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

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

特色: 效率高, 無回溯,  編程

搜索算法app

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

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

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

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

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

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;

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

  • 2、啓發式搜索: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值最小的節點進行擴展,依次進行該過程,直至該節點爲目標狀態。
解的路徑的輸出:經過目標狀態節點向上回溯找其父節點,直至開始狀態。

 

  • 3、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()

 

  • 4、程序運行結果以下圖所示

 

                                                             圖 1

 

                                                            圖 2

 

                                                                圖 3

  • 5、總結

經過此次編程瞭解到了搜索具備探索性,要提升搜索效率(儘快地找到目標節點),或要找最佳路徑(最佳解)就必須注意搜索策略。對於狀態圖搜索,已經提出了許多策略,它們大致可分爲盲目搜索(bland search)和啓發式搜索(heuristic search)兩大類。其中盲目搜索是無嚮導搜索。啓發式搜索是有嚮導搜索,即利用啓發信息(函數)引導去尋找問題解。經過A*算法解決N數碼問題實驗過程當中也遇到不少問題,好比節點擴展的方向問題等,經過此次實驗不只鍛鍊了本身python編程能力,也讓本身對N數碼求解最優路徑問題有了更清晰的認識,但願本身能在老師和同窗的幫助下,能不斷進步,固然最重要的是本身得付出,只會幻想而不行動的人,永遠也體會不到收穫果實時的喜悅。加油!!

相關文章
相關標籤/搜索