1. 程式人生 > >Java中的A A star 尋徑實現

Java中的A A star 尋徑實現

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                據我個人所知,目前流行的尋徑方法大體有兩種,即A* 和Dijkstra(SP演算法)

Dijkstra演算法:

   
  
    由Edsger Wybe Dijkstra先生髮明(已故)
    Dijkstra演算法是典型的最短路演算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。Dijkstra演算法能得出最短路徑的最優解,但由於它遍歷計算的節點很多,所以效率低。Dijkstra演算法是一種逐步搜尋演算法,通過為每個頂點n保留目前為止所找到的從m到n的最短路徑來工作的。

    搜尋過程:
   假如在第m步搜尋到一個最短路徑,而該路徑上有n個距離源節點最近的節點,則將他們認為是一個節點集合N;在第m+1步,搜尋不屬於N的距離源節點最近的節點,並搜尋到的節點加入到N中;繼續搜尋,直至到達目的節點,N中的節點集合便是從源節點到目的節點的最短路徑。

    演算法描述:

    Dijkstra演算法是通過為每個頂點v保留目前為止所找到的從s到v的最短路徑來工作的。初始時,源點s的路徑長度值被賦為0(d[s]=0), 同時把所有其他頂點的路徑長度設為無窮大,即表示我們不知道任何通向這些頂點的路徑(對於V中所有頂點v除s外d[v]= ∞)。當演算法結束時,d[v]中儲存的便是從s到v的最短路徑,或者如果路徑不存在的話是無窮大。 Dijstra演算法的基礎操作是邊的拓展:如果存在一條從u到v的邊,那麼從s到u的最短路徑可以通過將邊(u,v)新增到尾部來拓展一條從s到v的路徑。這條路徑的長度是d[u]+w(u,v)。如果這個值比目前已知的d[v]的值要小,我們可以用新值來替代當前d[v]中的值。拓展邊的操作一直執行到所有的d[v]都代表從s到v最短路徑的花費。這個演算法經過組織因而當d[u]達到它最終的值的時候沒條邊(u,v)都只被拓展一次。演算法維護兩個頂點集S和Q。集合S保留了我們已知的所有d[v]的值已經是最短路徑的值頂點,而集合Q則保留其他所有頂點。集合S 初始狀態為空,而後每一步都有一個頂點從Q移動到S。這個被選擇的頂點是Q中擁有最小的d[u]值的頂點。當一個頂點u從Q中轉移到了S中,演算法對每條外接邊(u,v)進行拓展。

A*(A Star)演算法:

  


    A*(A-Star)演算法是一種靜態路網中求解最短路最有效的方法。
    公式表示為:f(n)=g(n)+h(n), 其中f(n) 是節點n從初始點到目標點的估價函式,g(n) 是在狀態空間中從初始節點到n節點的實際代價,h(n)是從n到目標節點最佳路徑的估計代價。

     搜尋過程:
    
     建立兩個表,OPEN表儲存所有已生成而未考察的節點,CLOSED表中記錄已訪問過的節點。遍歷當前節點的各個節點,將n節點放入CLOSE中,取n節點的子節點X,->算X的估價值.

        While(OPEN!=NULL)
        {
        從OPEN表中取估價值f最小的節點n;
        if(n節點==目標節點) break;
        else
        {
        if(X in OPEN) 比較兩個X的估價值f //注意是同一個節點的兩個不同路徑的估價值
        if( X的估價值小於OPEN表的估價值 )
           更新OPEN表中的估價值; //取最小路徑的估價值
        if(X in CLOSE) 比較兩個X的估價值 //注意是同一個節點的兩個不同路徑的估價值
        if( X的估價值小於CLOSE表的估價值 )
           更新CLOSE表中的估價值; 把X節點放入OPEN //取最小路徑的估價值
        if(X not in both)
        求X的估價值;
           並將X插入OPEN表中; //還沒有排序
        }
        將n節點插入CLOSE表中;
        按照估價值將OPEN表中的節點排序; //實際上是比較OPEN表內節點f的大小,從最小路徑的節點向下進行。
        }


就實際應用而言,A*演算法和Dijistra演算法的最大區別就在於有無估價值,本質上Dijistra演算法相當於A*演算法中估價值為0的情況。所以此次我選取A*演算法進行Java實現。

拋開理論性的數學概念,通常的A*演算法,其實只有兩個步驟,一是路徑評估,以保證移動的下一個位置離目標最近,評估的結果越精確則尋徑的效率也將越高。二是路徑查詢,也即將評估結果進行反應,而後從新位置再次進行評估指導無路可走為止,以此遍歷出可行的路徑。

在A*演算法的程式實現中,本質上我們只需要關心三點,即起點、終點和地圖資訊,有了這三項基本資料,我們就可以構建任何情況下的A*實現。

下面我現在提供的是一個A*的Java靜態尋徑演算法實現,邏輯見程式碼註釋。

 執行效果如下圖(1,1 to 10,13):


(1,1 to 7,9 小房子門口中間)


(1,1 to 6,7 小房子內部)



Node.java
package  org.test.astar;

import  java.awt.Point;
import  java.util.LinkedList;

/** */ /**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:描述路徑節點用類
 * </p>
 * <p>
 * Copyright: Copyright (c) 2008
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * <p>
 * License: 
http://www.apache.org/licenses/LICENSE-2.0
 * </p>
 * 
 * 
@author chenpeng
 * @email:[email protected]
 * 
@version 0.1
 
*/

public   class  Node  implements  Comparable  ... {
    
// 座標
    public Point _pos;

    
// 開始地點數值
    public int _costFromStart;

    
// 目標地點數值
    public int _costToObject;

    
// 父節點
    public Node _parentNode;

    
private Node() ...{
    }


    
/** *//**
     * 以注入座標點方式初始化Node
     * 
     * 
@param _pos
     
*/

    
public Node(Point _pos) ...{
        
this._pos = _pos;
    }


    
/** *//**
     * 返回路徑成本
     * 
     * 
@param node
     * 
@return
     
*/

    
public int getCost(Node node) ...{
        
// 獲得座標點間差值 公式:(x1, y1)-(x2, y2)
        int m = node._pos.x - _pos.x;
        
int n = node._pos.y - _pos.y;
        
// 取兩節點間歐幾理德距離(直線距離)做為估價值,用以獲得成本
        return (int) Math.sqrt(m * m + n * n);
    }


    
/** *//**
     * 檢查node物件是否和驗證物件一致
     
*/

    
public boolean equals(Object node) ...{
        
// 驗證座標為判斷依據
        if (_pos.x == ((Node) node)._pos.x && _pos.y == ((Node) node)._pos.y) ...{
            
return true;
        }

        
return false;
    }


    
/** *//**
     * 比較兩點以獲得最小成本物件
     
*/

    
public int compareTo(Object node) ...{
        
int a1 = _costFromStart + _costToObject;
        
int a2 = ((Node) node)._costFromStart + ((Node) node)._costToObject;
        
if (a1 < a2) ...{
            
return -1;
        }
 else if (a1 == a2) ...{
            
return 0;
        }
 else ...{
            
return 1;
        }

    }


    
/** *//**
     * 獲得上下左右各點間移動限制區域
     * 
     * 
@return
     
*/

    
public LinkedList getLimit() ...{
        LinkedList limit 
= new LinkedList();
        
int x = _pos.x;
        
int y = _pos.y;
        
// 上下左右各點間移動區域(對於斜視地圖,可以開啟註釋的偏移部分,此時將評估8個方位)
        
// 上
        limit.add(new Node(new Point(x, y - 1)));
        
// 右上
        
// limit.add(new Node(new Point(x+1, y-1)));
        
// 右
        limit.add(new Node(new Point(x + 1, y)));
        
// 右下
        
// limit.add(new Node(new Point(x+1, y+1)));
        
// 下
        limit.add(new Node(new Point(x, y + 1)));
        
// 左下
        
// limit.add(new Node(new Point(x-1, y+1)));
        
// 左
        limit.add(new Node(new Point(x - 1, y)));
        
// 左上
        
// limit.add(new Node(new Point(x-1, y-1)));

        
return limit;
    }


}


PathFinder.java
package  org.test.astar;

import  java.awt.Point;
import  java.util.LinkedList;
import  java.util.List;

/** */ /**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:A*尋徑處理用類(此類為演示用,並不意味著演算法是最佳實現)
 * </p>
 * <p>
 * Copyright: Copyright (c) 2008
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * <p>
 * License: 
http://www.apache.org/licenses/LICENSE-2.0
 * </p>
 * 
 * 
@author chenpeng
 * @email:[email protected]
 * 
@version 0.1
 
*/

public   class  PathFinder  ... {
    
// 路徑優先等級list(此示例中為內部方法)
    private LevelList _levelList;

    
// 已完成路徑的list
    private LinkedList _closedList;

    
// 地圖描述
    private int[][] _map;

    
// 行走區域限制
    private int[] _limit;

    
/** *//**
     * 以注入地圖的2維陣列及限制點描述初始化此類
     * 
     * 
@param _map
     
*/

    
public PathFinder(int[][] map, int[] limit) ...{
        _map 
= map;
        _limit 
= limit;
        _levelList 
= new LevelList();
        _closedList 
= new LinkedList();
    }


    
/** *//**
     * A*方式尋徑,注入開始座標及目標座標後運算,返回可行路徑的List
     * 
     * 
@param startPos
     * 
@param objectPos
     * 
@return
     
*/

    
public List searchPath(Point startPos, Point objectPos) ...{
        
// 初始化起始節點與目標節點
        Node startNode = new Node(startPos);
        Node objectNode 
= new Node(objectPos);
        
// 設定起始節點引數
        startNode._costFromStart = 0;
        startNode._costToObject 
= startNode.getCost(objectNode);
        startNode._parentNode 
= null;
        
// 加入運算等級序列
        _levelList.add(startNode);
        
// 當運算等級序列中存在資料時,迴圈處理尋徑,直到levelList為空
        while (!_levelList.isEmpty()) ...{
            
// 取出並刪除最初的元素
            Node firstNode = (Node) _levelList.removeFirst();
            
// 判定是否和目標node座標相等
            if (firstNode.equals(objectNode)) ...{
                
// 是的話即可構建出整個行走路線圖,運算完畢
                return makePath(firstNode);
            }
 else ...{
                
// 否則
                
// 加入已驗證List
                _closedList.add(firstNode);
                
// 獲得firstNode的移動區域
                LinkedList _limit = firstNode.getLimit();
                
// 遍歷
                for (int i = 0; i < _limit.size(); i++...{
                    
//獲得相鄰節點
                    Node neighborNode = (Node) _limit.get(i);
                    
//獲得是否滿足等級條件
                    boolean isOpen = _levelList.contains(neighborNode);
                    
//獲得是否已行走
                    boolean isClosed = _closedList.contains(neighborNode);
                    
//判斷是否無法通行
                    boolean isHit = isHit(neighborNode._pos.x,