這是我參與更文挑戰的第8天,活動詳情查看: 更文挑戰html
本文正在參加「Java主題月 - Java 開發實戰」,詳情查看 活動連接git
[TOC]算法
A*算法,A*(A-Star)算法是一種靜態路網中求解最短路徑最有效的直接搜索方法,也是解決許多搜索問題的有效算法。算法中的距離估算值與實際值越接近,最終搜索速度越快。markdown
基本概念
- 首先在大學咱們遇到最多的算法Dijkstra、Floyd、廣度搜索、深度搜索。關於這些算法咱們之後再慢慢的研究,今天的重點在A*算法上。A*算法是一種啓發式算法。與上述幾種算法不一樣的是A*算法在考慮起始節點的同時還會考慮到目標節點的代價。
- 在A*算法中咱們給每一個節點都定義一些屬性。最基本的就是下文提到的三基數-這裏的三基數是我本身定義的一個名詞。什麼叫啓發式就是在探索路徑的時候既要選擇裏起始點最近也要考慮到裏目標節點的消費問題。
三基值
- 上面的一些概念可能會使你很模糊,這裏咱們直接看定義。
F=G+H : 表示一個節點的總消費值;換句話說就是離起始節點和目標節點距離的總和 G : 表示該從其實節點到該節點的消費值; H : 表示從該節點到目標節點的消費值;(這裏注意一下,這裏的消費值實際上是一個預估值,由於咱們沒法判斷到目標節點的具體路徑,這個H值得獲取本文會提供三種方法,其中使用最普遍的是曼哈頓距離)函數
圖1oop
三基值計算
常規約定
- 在方格地圖中咱們約定橫向或者縱向單位消費爲10
- 在方格地圖中斜向單位消費爲14
- 在牆(牆、河流等不可通過的節點統稱)角咱們是不能夠斜着穿越的,這是常識。實際中咱們每一個移動的物體都是有本身的空間的,以下圖這樣S-->E的過程S'已經佔用了Q(牆)的領域了。
圖2post
圖3.net
G值計算
- 三基數在上面定義中咱們已經列出了其計算公式,可能有的人看的不是很明白。咱們這裏詳細解釋一下
- 首先咱們先看三基值中的G值,G
表示該從其實節點到該節點的消費值
,這句話的意思從開始節點移動到當前節點須要的實際的消費,這裏是須要考慮到不可通過的節點的。以下圖S表示起始點,E表示目標節點,N表示當前節點,黑色的表示牆(不可通過的節點集合)。咱們規定在牆角是沒法斜着穿過去,在其餘地方是能夠斜着通過的。有了這個約定,咱們能夠算出圖3中
- S--->N1 消費10,
- S--->N2消費14,由於從S到N2不是處於牆角。
- 途中N3--->N4就是明顯的牆角。因此N3--->N4消費是20。
H值計算方式
- H值和G值做用相反,H值是預估到目標節點的消費值。
- 首先G是針對其實點的,而H值是針對目標節點的
- 其次G值是真實值,而H值是預估值
- 最後G值得計算是容許斜線行走的,可是H值計算只能橫向和縱向的結合
複製代碼
圖4
集合列表
- 在咱們A*尋路的過程當中,咱們須要用到兩個集合,一個咱們成爲開放集合(OpenList),另一個咱們稱之爲閉合集合(ClosedList)。
- 舉個例子咱們去超市購物,咱們都會推個手推車,把喜歡的東西放進購物車裏。最後結算的時候或者是中途咱們會選擇價格更實惠的東西替代咱們已經選擇的同等產品
- 在A*中咱們也是這樣,openList就至關於購物車,咱們會將見到的喜歡的物品加入購物車,可是加入購物車並不必定最後會買,在A*中,咱們會將節點周圍可用的節點加入openList,可是並不必定最後須要。在openList添加的過程咱們會慢慢用'更實惠'的節點代替已經選擇的相同的節點。在超市最後放到咱們本身的包裏纔是最後咱們要帶回家的東西。在A*算法中加到closedList中才是咱們最後的東西。
尋路解析
本小節圖片來源如下文章。其中思想參考源如下文章
- 通過上面的介紹咱們瞭解了A*中咱們一些約定的定義,理解上面這些定義的基礎上咱們下面的流程會很易懂。
初始地圖
- 如圖5,地圖上藍色方格表示牆,關於牆的定義上面已經解釋過了。左邊青色的表示S(起始節點),右邊的紅色節點表示咱們的目標節點。小圓點表示從S到E的最有路徑之一。從圖5咱們能夠看出咱們在牆角沒有斜走,而在其餘路段上咱們選擇斜着走了。下面介紹節點就以圖5中的座標爲準。好比起始節點咱們就稱之爲(2,3)。
-
藍色方格表示不可通過方格
-
青色的邊框的方格表示已經加入openList
-
高亮顯示的邊框表示添加在closedList
-
青色表示其實節點
-
紅色表示目標節點
-
遞歸尋走
- 首先咱們常規的尋路是不可能出現首尾相同的狀況。可是項目中得處理這種狀況,若是其實節點和目標節點是地圖上的統一節點,那麼咱們的路徑就是當前節點。
- 選擇當前節點周圍可用的節點,若是不在openList集合中則分別計算三基數的值而且加入到openList集合中,計算三基數的同事將待加入openList的節點的父節點設置爲當前節點。
- 若是已經存在openList集合中且結合中的G值大於當前節點(周圍節點之一)的G值,則將當前節點更新到openList集合中。不然不加入也不更新。
- 周圍節點選擇完以後咱們就把該節點(起始節點)加入closedList中,而後從openList中選擇F值最小的節點,在繼續重複上面三步驟。
圖示流程
圖6
圖7
- 從圖7中咱們能夠看出在起始節點(2,3)周圍(3,3)這個節點的F值此時在openList中最小,因此此時咱們將(2,3)移除openList並將(2,3)加入到closedList中。此時(2,3)由高亮方格顯示錶示加入closedList集合,
- (2,3)節點就算是結束了他的使命了,加入了closedList集合中的節點咱們將不會再去考慮,咱們能夠認爲加入到closedList集合中的點已經成牆了。那麼下面咱們從openList中選取F值最小(3,3)爲新的起始節點,開始重複上面的流程,可是咱們發現(3,3)的右上、正右、右下都是牆,還有正左(2,3)在closedList集合中。這四點咱們是不用考慮的。那麼只剩下
(2,2),(3,2),(2,4),(3,4)這四個點。可是這四個點恰巧有所有在openList中。依照上面的流程咱們得以此比較這四個點和openList集合中對應的點的G值誰大誰小。就一個原則誰小留誰。由圖8咱們能夠知道,新得到的這四個點的G值均大於openlist裏對應的點的G值,因此咱們這裏放棄這四個點。他們沒有被咱們喜歡,咱們拋棄這些點。這一輪結束咱們將(3,3)加入到closedList集合中。若是新節點G值小於對應的openList中的點的G值得話,咱們就要更新openList集合中的對應的那個點。所謂的更新就是將新節點替換原來那個節點。注意此時新節點和openList對應的那個點出了三基數不一樣,還有父節點也不一樣了。
- 這裏須要解釋下爲何新節點的G值都會加10呢,那是由於咱們最起始的節點是(2,3),而此時的起始節點是(3,3),好比咱們算(2,2)G值得時候其實是(2,3)--->(3,3)--->(2,2)整個過程的G值。因此是10+14。
圖8
- 由圖9咱們可以看到此時咱們openList中最小F值應該是(3,4),下面的步驟就是一直重複。
圖9
不足之處
- 關鍵在於估價函數h(n)的選取:估價值h(n)<= n到目標節點的距離實際值,這種狀況下,搜索的點數多,搜索範圍大,效率低。但能獲得最優解。而且若是h(n)=d(n),即距離估計h(n)等於最短距離,那麼搜索將嚴格沿着最短路徑進行, 此時的搜索效率是最高的。若是 估價值>實際值,搜索的點數少,搜索範圍小,效率高,但不能保證獲得最優解
源碼
源碼下載點我
[//]: A譯文: blog.csdn.net/coutamg/art… *[//]:其餘最小路徑算法:www.cnblogs.com/biyeymyhjob… *[//]:啓發式算法:baike.baidu.com/item/%E5%90…