A*算法解決迷宮尋址問題

最近在想作一個迷宮小遊戲看成本身的期末大做業,其中有一部分電腦敵人須要自動尋找玩家所在位置,思來想去想了幾種方法,可是因爲都不夠好,因此一直被卡住了。後來上了人工智能導論這門課,瞭解到A*算法對於實現電腦機器人自動尋址很是的巧妙,所以查閱大量資料,寫下這篇文章。

1、什麼是A*算法?node

在解決迷宮尋址問題以前,我先帶你們瞭解一下A*算法:

A*算法是一種啓發式搜索算法,它不需遍歷全部節點,只是利用包含問題啓發式信息的評價函數對節點進行排序,使搜索方向朝着最有可能找到目標併產生最優解的方向。它的獨特之處是檢查最短路徑中每一個可能的節點時引入了全局信息,對當前節點距終點的距離作出估計,並做爲評價節點處於最短路徑上的可能性度量。python

A*算法中引入了評估函數,評估函數爲:f(n)=g(n)+h(n),其中:n是搜索中所處的任意狀態。g(n)是從起點到任意一個n節點的移動耗費。h(n)是對n到目標狀態代價的啓發式估計。即評估函數f(n)是從初始節點到達節點n處已經付出的代價與節點n到達目標節點的接近程度估價值的總和。咱們一般會定義n點到目標點的最小實際距離爲h(n)*,A*算法要知足的條件爲:h(n)<=h(n)*。算法

2、爲何使用A*算法?數組

舉個簡單的例子來講明一下爲何要使用A*算法。假設有一個這樣的地圖:app

0dom

0函數

0學習

0阿里雲

0人工智能

0

0

0

0

0

0

1

0

0

0

0

0

A

0

1

0

B

0

0

0

0

0

1

0

0

0

0

0

0

0

0

0

0

0

0

0表明能夠經過,1表明障礙物,你如今須要從A點到達B點。若是採用迪傑斯特拉算法的話,首先會把A周圍能夠走的四個方格所有遍歷一遍,而後又重複第一步的步驟把前一步驟遍歷過的格子周圍的格子遍歷一遍,一直這樣重複,最後能夠遍歷到B點,找到一條到達B點最短的路徑,可是因爲遍歷的次數太多,消耗的時間和資源也會增長,雖然地圖簡單的話不會又很明顯的影響,可是若是地圖變得很複雜,那麼這種感受尤其明顯。看一下下面下圖就會有這種感受了(圖片來自阿里雲棲社區paulquei)


再看一下最佳優先搜索算法(BFS),他是一種啓發式算法,他會始終朝着距離終點最近得點走下去,而不像迪傑斯特拉算法把全部可能的點都遍歷一遍,就拿本例來講,由於B點相對於A點在右邊,因此右方向的優先度更高,而後它就直接往右走,當碰到障礙物它就往下或者上走,繞過障礙物後繼續沿着B的方向走,最後它能夠找到一條到達B的路徑,就本例來講,它能夠找到一條最短路徑且時間比迪傑斯特拉算法短了不少,可是若是把地圖變得複雜一點,它就可能沒法找到最短路徑。看一下下面下圖就會有這種感受了(圖片來自阿里雲棲社區paulquei)



最後再說一下A*算法的思路,相比於迪傑斯特拉算法和最佳優先搜索(BFS)算法,A*算法更像是它們的結合體。A*算法首先會遍歷它周圍能夠走的方格,而後再從已經遍歷的方格中找出一個距離目標點最近的方格,而後重複前一步驟。就本例題而言,咱們先假設有f(n)=g(n)+h(n),該公式在上文中已經提到過。A首將本身標記爲已走過的方格並先判斷它周圍的四個格子都可以走,而後把這四個格子標記爲已遍歷,同時計算出他們的f(n),g(n),h(n)的值,以第一步爲例,四個方向的g(n)=1,往上的h(n)=5,f(n)=6,往下的h(n)=5,f(n)=6,往右的h(n)=5,f(n)=6,往左的h(n)=3,f(n)=4。而後選擇f(n)最小也就是距離終點最短的點標記爲走過的路徑,而後重複這一步驟,直到找到一條最短路徑。A*算法相對於前面兩種算法,不只能夠準確的找到最短路徑,並且還能使搜索時間大大縮短。看一下下面下圖就會有這種感受了(圖片來自阿里雲棲社區paulquei)


3、A*算法解決迷宮尋址問題的思路?

本實驗以人工智能導論(王萬良 第四版)後面實驗五爲對象,講解A*算法的實現迷宮尋路思路:

假設地圖爲map[[0,0,0,0,],[1,0,1,0,1],[0,0,1,1,1],[0,1,0,0,0],[0,0,0,1,0]]。首先,咱們須要定義未判斷過的數組openList,判斷過的數組closeList,存儲當前節點可達鄰居節點的數組neighbors,變量F,G,H。F爲地點到終點曼哈頓距離,G爲起點到任意一點n的距離,H爲n到終點的估價距離。而後開始咱們的算法步驟,首先將起始節點加入到openList中,而後循環判斷openList中距離終點的曼哈頓距離最小的節點(即F值最小的結點,F=G+H),而後將其移出openList並放進closeList,表明此時這個格子已經檢查過了。而後再找出當前格子四周能夠到達的格子,用neighbors數組存放當前節點的鄰居節點並判斷鄰居節點是否已存在於openList或closeList中,若是不存在則將它們放入openList,而後計算出相應的F,G,H值,並將當前節點記錄爲它們的父節點。而後再比較當前節點子節點的F值,把F值最小的那個移出openList,同時放入closeList。以實驗五爲例,當走到這步時候,openList包含了(0,1),closeList包含了(0,0),(0,1)。這時咱們再繼續重複前面的步驟,直到最後openList中包含了終點位置,這時咱們會找到一條最短的可達路徑:

(0,0)->(0,1)->(1,1)->(2,1)->(2,0)->(3,0)->(4,0)->(4,1)->(4,2)->(3,2)->(3,3)->(3,4)->(4,4)

固然,這是理想狀態下的迷宮,它的路線包含在colseList中,還有一種無解的迷宮,無解的判斷條件是當openList中全部節點用盡時,若是在colseList中沒法找到終點節點,則返回迷宮無解。

4、A*算法實現迷宮尋址具體實現代碼?

#定義隨機生成地圖 實驗五未用到此方法 故註釋
# def creatMap(x):
# print('hello')
# map = []
# print("Map is here:")
# for i in range(x):
# map.append([])
# for j in range(x):
# map[i].append(random.randint(0,1))
# if j==x-1:
# print(map[i][j])
# else:
# print(map[i][j],end="")
# map[0][0] = 0
# map [x-1][x-1] = 0
# return map
#生成節點class Node(object): 
#初始化節點
    def __init__(self, now, begin, end):
        super(Node, self).__init__()
        self.m_own = now
        self.m_g = 0
        self.m_h = abs( end[1] - now[1] ) + abs( end[0] - now[0] )#計算H值
        self.m_f = self.m_g + self.m_h
        self.m_begin = begin
        self.m_end = end
        self.m_flag = str( now )
        self.m_neighbors = []#用來存儲當點節點的鄰居節點
        self.m_parent = None
        #返回當前節點的字符串形式
    def get_flag(self):
        return self.m_flag
    #獲得F值
    def get_fvalue(self):
        return self.m_f
    # 獲得當前節點
    def get_position(self):
        return self.m_own
    #把鄰居節點設爲當前節點的父節點
    def set_parent(self, parentN):
        self.m_parent = parentN
    #獲得父節點
    def get_parent(self):
        return self.m_parent
    #調用方法獲得父節點
    parentN = property( get_parent, set_parent )
     # 設置G值
    def set_gvalue(self, g):
        self.m_g = g
    #獲得G值
    def get_gvalue(self):
        return self.m_g
    #調用上面方法設置並得G值
    gvalue = property( get_gvalue, set_gvalue )
    #獲得當前格子的相鄰的格子
    def get_neighbors(self):
        #neb爲上下左右四個方向,數組值爲當前節點移動距離,例如[0,1]說明當前節點向右移動一格
        neb = [[0,1],[0,-1],[1,0],[-1,0]]
        #循環遍歷當前節點四周節點
        for item in neb:
            x = item[0] + self.m_own[0]
            y = item[1] + self.m_own[1]
            if (x >= 0 and x < map_size) and (y >= 0 and y < map_size):
                if map[x][y] == 0:
                    self.m_neighbors.append( Node( (x, y), self.m_begin, self.m_end ) )
        return self.m_neighbors
#尋路函數
def aStarSearch( begin, end ):
    # 定義 未判斷數組openList 已判斷數組colseList
    openlist = []
    closelist = []
    #把起點加入open list
    begin_node = Node( begin, begin, end )
    begin_node.gvalue = 0
    openlist.append( begin_node )
    #主循環,每一輪檢查一個當前方格節點
    while len(openlist) > 0:
        #在openlist中查找F值最小的節點做爲當前方格節點
        _min = findMinNode( openlist )
        #當前方格節點從openlist中移除,進入closelist,表明這個格子已到達並檢查過了。
        openlist.remove( _min )
        closelist.append( _min )
        #找到當前格上下左右全部可到達的格子,看它們是否在Openlist和Closelist中,若是不在,加入Openlist,計算出相應的G、H、F值,並把當前格子做爲它們的「父節點」
        neighbors = _min.get_neighbors()
        for item in neighbors:
            if not isContains( openlist, item ) and not isContains( closelist, item ):
                item.parentN = _min
                item.gvalue = _min.gvalue + 1
                openlist.append( item )
        #若是終點在openlist中,直接返回終點格子
        for x in openlist:
            if x.get_flag() == str(end):
                return x
    #openlist用盡,仍然找不到終點,說明終點不可到達,返回空
    return None
#找到並返回openList中起點距離終點最近的點並返回
def findMinNode( _list ):
    _min = _list[0]
    for x in _list:
        if x.get_fvalue() < _min.get_fvalue():
            _min = x
    return _min
#判斷節點是否在openList或closeList中
def isContains( _list, _node ):
    for item in _list:
        if item.get_flag() == _node.get_flag():
            return True
    return False
# 定義地圖
map = [
[0,0,0,0,0],
[1,0,1,0,1],
[0,1,1,1,1],
[0,1,0,0,0],
[0,0,0,1,0]
]
map_size = 5
dst= aStarSearch((0,0),(4,4))
# 異常處理 若是迷宮無解則dst最後一個節點爲None,它將不會有get_flag方法,發生異常,此時對異常進行處理
try:
    path = [dst.get_flag()]
    while dst.parentN:
        dst = dst.parentN
        path.insert( 0, dst.get_flag() )
    print( path )
except AttributeError:
    print('該迷宮無解!')
    pass複製代碼


代碼運行結果示例:

地圖:map = [

[0,0,0,0,0],
[1,0,1,0,1],
[0,1,1,1,1],
[0,1,0,0,0],
[0,0,0,1,0]]


地圖:map = [

[0,0,0,0,0],
[1,0,1,0,1],
[0,0,1,1,1],
[0,1,0,0,0],
[0,0,0,1,0]]


第一次寫文章,但願你們一塊兒相互學習,共同進步,有不對的地方請各位大佬悉心指導!

相關文章
相關標籤/搜索