1. 程式人生 > >A* search算法

A* search算法

mar class 選擇 指向 最終 應用場景 操作 span font

今天,還是國慶和中秋雙節的時間節點,一個天氣不錯的日子,孩子已經早早的睡覺了,玩了一整天,也不睡覺,累的實在扛不住了,勉強洗澡結束,倒床即睡著的節奏。。。

不多說題外話,進入正題。

什麽是A*搜索算法呢?就用百科的解說吧:

A*算法,A*(A-Star)算法是一種靜態路網中求解最短路徑最有效的直接搜索方法,也是解決許多搜索問題的有效算法。算法中的距離估算值與實際值越接近,最終搜索速度越快。

A*搜索的實際應用場景很多,但是大家最為熟悉的恐怕莫過於遊戲了。典型的遊戲就是穿越障礙尋寶,要求在最少的代價內找到寶貝,通常遊戲中的代價,就是用最少的步驟實現寶貝的找尋。還有一種遊戲場景是,給定入口地點,用最少的步驟走到指定的出口地點,中間有障礙物,每次只能在上下左右四個方向上走一步,且不能穿越障礙物。

就拿第二種遊戲場景來解釋A* search具體指的是什麽內容吧。

技術分享

如上圖所示,假設我們有一個7*5的迷宮方格,綠色的點表示起點,紅色的點表示終點。中間三個藍色的格子表示一堵墻,是障礙物。遊戲的規則,是綠色的起點,每次只能向上下左右4個方向中移動一步,且不能穿越中間的墻,以最少的步驟到達紅色的終點

在解決這個問題之前,先要引入A*搜索算法的核心集合和公式:

核心集合:OpenList,CloseList

核心公式:F=G+H

其中,OpenList和CloseList用來存儲格子點信息,OpenList表示可到達格子節點集合,CloseList表示已到達格子節點集合。

F=G+H表示對格子價值的評估,G表示從起點到當前點的代價;H表示從當前點到達終點的代價,指不考慮障礙物遮擋的情況下,這裏,代價是指走的步數。至於F,就是對G和H的綜合評估了,當然F越小,則從起點到達終點付出的代價就越小了。

就實際操作一下吧。還是上面的圖,每個節點,用n(x,y)表示,x表示橫坐標,y表示縱坐標,比如綠色的起點是n(1,2):

技術分享

第一步:把起點放入OpenList裏面。

OpenList: n(1,2)

CloseList

第二步:找出OpenList中F值最小的方格,即唯一的方格n(1,2)作為當前方格,並把當前格移出OpenList,放入CloseList。表示這個格子已到達且驗證過了。

OpenList

CloseList:n(1,2)

第三步:找出當前格上下左右所有可到達的格子,看它們是否在OpenList當中。如果不在,加入OpenList,計算出相應的G、H、F值,並把當前格子作為它們的“父節點”。

技術分享

OpenList:n(0,2), n(1,1), n(2,2), n(1,3)

CloseList:n(1,2)

其中,n(0,2), n(1,1), n(2,2), n(1,3)的父節點是n(1,2).所謂的父節點,表示當前的這幾個節點n(0,2), n(1,1), n(2,2), n(1,3)都是從這個所謂的父節點出發得到的分支節點,父節點用作後續找出最短路徑用的

上述3步,是一次局部尋路的過程,我們需要不斷的重復第二步第三步,最終找到到達終點的最短路徑。

第二輪 ~ 第一步:找出OpenList中F值最小的方格,即方格n(2,2)作為當前方格,並把當前格移出OpenList,放入CloseList。代表這個格子已到達並檢查過了。

技術分享

此時的兩個核心集合的節點信息:

OpenList:n(0,2), n(1,1), n(1,3)

CloseList:n(1,2), n(2,2)

其中,n(0,2), n(1,1), n(1,3)的父節點是n(1,2),n(2,2)的上一級節點(也可以稱為父節點)是n(1,2).

第二輪 ~ 第二步:找出當前格上下左右所有可到達的格子,看它們是否在OpenList當中。如果不在,加入OpenList,計算出相應的G、H、F值,並把當前格子作為它們的“父節點”。

技術分享

此時的兩個核心集合的節點信息:

OpenList:n(0,2), n(1,1), n(1,3);n(2,1), n(2,3)

CloseList:n(1,2) <-----n(2,2)

其中,n(0,2), n(1,1), n(1,3)的父節點是n(1,2),而n(2,1), n(2,3)的父節點是n(2,2). CloseList中節點的指向關系,反映了尋路的路徑過程。

為什麽這一次OpenList只增加了兩個新格子呢?因為n(3,2)是墻壁,自然不用考慮,而n(1,2)在CloseList當中,說明已經檢查過了,也不用考慮

第三輪 ~ 第一步:找出OpenList中F值最小的方格。由於這時候多個方格的F值相等,任意選擇一個即可,比如n(2,3)作為當前方格,並把當前格移出OpenList,放入CloseList。代表這個格子已到達並檢查過了

技術分享

此時的兩個核心集合的節點信息:

OpenList:n(0,2), n(1,1), n(1,3);n(2,1)

CloseList:n(1,2) <-----n(2,2)<-----n(2,3)

其中,n(0,2), n(1,1), n(1,3)的父節點是n(1,2),而n(2,1)的父節點是n(2,2)。CloseList中節點的指向關系,反映了尋路的路徑過程。

第三輪 ~ 第二步:找出當前格上下左右所有可到達的格子,看它們是否在OpenList當中。如果不在,加入OpenList,計算出相應的G、H、F值,並把當前格子作為它們的“父節點”。

技術分享

此時的兩個核心集合的節點信息:

OpenList:n(0,2), n(1,1), n(1,3);n(2,1) ;n(2,4)

CloseList:n(1,2) <-----n(2,2)<-----n(2,3)

其中,n(0,2), n(1,1), n(1,3)的父節點是n(1,2),而n(2,1)的父節點是n(2,2)。n(2,4)的父節點是n(2,3). CloseList中節點的指向關系,反映了尋路的路徑過程。

剩下的就是以前面的方式繼續叠代,直到OpenList中出現終點方格為止。

實際的推理,就到這裏,下面,將結合上述的推理理論,用java程序,加以實現。今天,先將偽代碼附上,改天將具體的java實現代碼貼上來。

public Node AStarSearch(Node start, Node end) {
    // 把起點加入openList  
    openList.add(start);
    //主循環,每一輪檢查一個當前方格節點
    while (openList.size() > 0) {
        // 在OpenList中查找F值最小的節點作為當前方格節點
        Node current = findMinNode();
        // 當前方格節點從open list中移除
        openList.remove(current);
        // 當前方格節點進入closeList
        closeList.add(current);
        // 找到所有鄰近節點
        List<Node> neighbors = findNeighbors(current);
        for (Node node : neighbors) {
            if (!openList.contains(node)) {
                //鄰近節點不在openList中,標記父親、G、H、F,並放入openList
                markAndInvolve(current, end, node);
            }
        }
        //如果終點在OpenList中,直接返回終點格子
        if (find(openList, end) != null) {
            return find(openList, end);
        }
    }
    //OpenList用盡,仍然找不到終點,說明終點不可到達,返回空
    return null;
}

JAVA代碼實現待續。。。

A* search算法