日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

A*寻路算法与它的速度

發(fā)布時間:2024/4/11 编程问答 93 豆豆
生活随笔 收集整理的這篇文章主要介紹了 A*寻路算法与它的速度 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

如果你是一個游戲開發(fā)者,或者開發(fā)過一些關于人工智能的游戲,你一定知道A*算法,如果沒有接觸過此類的東東,那么看了這一篇文章,你會對A*算法從不知道變得了解,從了解變得理解。
我不是一個純粹的游戲開發(fā)者,我只是因為喜歡而研究,因為興趣而開發(fā),從一些很小的游戲開始,直到接觸到了尋路等人工智能,才開始查找一些關于尋路方面的文章,從而知道了A*算法,因為對于初期了解的我這個算法比較復雜,開始只是copy而已,現(xiàn)在我們一起來精密的研究一下A*算法,以及提高它的速度的方法。

一,A*算法原理

我看過Panic翻譯的國外高手Patrick Lester的一篇關于A*算法初探的文章,現(xiàn)在我就根據(jù)回憶,來慢慢認識A*算法的原理。
我們先來看一張圖

圖中從起點到終點,需要繞過一些遮擋,也許我們看的比較簡單,但是實際上,交給電腦來實現(xiàn)卻要經(jīng)過一番周折,電腦如何知道哪里有遮擋物,又是如何找到從起點到終點的最短路徑的呢?
了解這些,我們首先要知道一個公式:
F = G + H?
其中,F 是從起點經(jīng)過該點到終點的總路程,G 為起點到該點的“已走路程”,H 為該點到終點的“預計路程”。
A*算法,要從起點開始,按照它的算法,逐步查找,直到找到終點。
初期,地圖上的節(jié)點都是未開啟也未關閉的初始狀態(tài),我們每檢測一個節(jié)點,就要開啟一些節(jié)點,檢測完之后,要把檢測完的節(jié)點,就要把它關閉。
我們需要一個開啟列表和關閉列表,用來儲存已經(jīng)被開啟的節(jié)點和被關閉的節(jié)點。
這些就讓我們在實際過程中來深入了解吧。
看下面的圖

首先,我們來從起點出發(fā),開啟它周圍的所有點,因為遮擋是無法通過的,我們不去管它,這樣,被我們開啟的節(jié)點,就是圖中的三個節(jié)點,它們的父節(jié)點就是起點,所以圖中的箭頭指向起點,計算相應的FGH值,如圖所視,檢測完畢,將起點放入關閉列表。
這個時候,我們從被開啟的所有節(jié)點中查找F值最小的節(jié)點,做為下一次檢測的節(jié)點,然后開啟它周圍的點。
這時候起點左方和下方的F值都是70,我們根據(jù)自己的喜好選擇任意一個,這里先選擇下方的節(jié)點進行檢測。
如下圖

首先把未被開啟的剩下的節(jié)點的父節(jié)點指向檢測點。
已經(jīng)開啟的點,我們不去開啟第二遍,但是我們計算一下從檢測點到達它們的新的G值是否更小,如果更小則代表目前的路徑是最優(yōu)的路徑,那么把這個節(jié)點的父節(jié)點改為目前的檢測點,并重新計算這個點的FGH的值,全部檢測完畢之后,關閉檢測點,然后開始尋找下一個節(jié)點,如此循環(huán),直到找到終點。
然后從終點開始,按照每個節(jié)點的父節(jié)點,倒著畫出路徑,如下圖

?

?

這個就是A*算法的原理,說難倒是不難,但是對于初步接觸的人來說有點費勁而已。

?

二,A*算法的速度

前面,我們了解了A*算法的原理,發(fā)現(xiàn),在每次查找最小節(jié)點的時候,我們需要在開啟列表中查找F值最小的節(jié)點,研究A*的速度,這里就是關鍵,如何更快的找出這個最小節(jié)點呢?

1,普通查找算法

我們先來看看,最簡單的做法,就是每次都把開啟列表中所有節(jié)點檢測一遍,從而找到最小節(jié)點

[c-sharp]?view plaincopy
  • private?function?getMin():uint?{??
  • ????var?len:uint?=?_open.length;??
  • ????var?min:Object?=?new?Object();??
  • ????min.value_f?=?100000;??
  • ????min.i?=?0;??
  • ????for?(var?i:uint?=?0;?i<len;?i++)?{??
  • ????????if?(min.value_f>_open[i].value_f)?{??
  • ????????????min.value_f?=?_open[i].value_f;??
  • ????????????min.i?=?i;??
  • ????????}??
  • ????}??
  • ????return?min.i;??
  • }??
  • 這里我用了一張很簡單的地圖來驗證此方法
    運行結果如圖

    我們看到,耗時38毫秒,其實這個數(shù)字是不準確的,我們權且當作參考

    2,排序查找算法

    顧名思義,這個算法就是,始終維持開啟列表的排序,從小到大,或者從大到小,這樣當我們查找最小值時,只需要把第一個節(jié)點取出來就行了
    維持列表的排序,方法是在太多了,我的方法也許很笨,勉強參考一下吧,我們每次排序的同時,順便計算列表中的平均值,這樣插入新節(jié)點的時候,根據(jù)這個平均值來判斷從前面開始判斷還是從后面開始判斷

    [c-sharp]?view plaincopy
  • //加入開放列表??
  • private?function?setOpen(newNode:Object):void?{??
  • ????newNode.open?=?true;??
  • ????var?__len:int?=?_open.length;??
  • ????if?(__len==0)?{??
  • ????????_open.push(newNode);??
  • ????????_aveOpen?=?newNode.value_f;??
  • ????}else?{??
  • ????????//和F平均值做比較,決定從前面或者后面開始判斷??
  • ????????if?(newNode.value_f<=_aveOpen)?{??
  • ????????????for?(var?i:int=0;?i<__len;?i++)?{??
  • ????????????????//找到比F值小的值,就插在此值之前??
  • ????????????????if?(newNode.value_f<=_open[i].value_f)?{??
  • ????????????????????_open.splice(i,?0,?newNode);??
  • ????????????????????break;??
  • ????????????????}??
  • ????????????}??
  • ????????}?else?{??
  • ????????????for?(var?j:int=__len;?j>0;?j--)?{??
  • ????????????????//找到比F值大的值,就插在此值之前??
  • ????????????????if?(newNode.value_f>=_open[(j-1)].value_f)?{??
  • ????????????????????_open.splice(j,?0,?newNode);??
  • ????????????????????break;??
  • ????????????????}??
  • ????????????}??
  • ????????}??
  • ????????//計算開放列表中F平均值??
  • ????????_aveOpen?+=?(newNode.value_f-_aveOpen)/_open.length;??
  • ????}??
  • }??
  • //取開放列表里的最小值??
  • private?function?getOpen():Object?{??
  • ????var?__next:Object?=??_open.splice(0,1)[0];??
  • ????//計算開放列表中F平均值??
  • ????????_aveOpen?+=?(_aveOpen-__next.value_f)/_open.length;??
  • ????????return?__next;??
  • }??
  • 運行結果如圖

    我們看到,耗時25毫秒,這個數(shù)字雖然不準確的,但是與普通查找算法相比較,速度確實是提高了

    3,二叉樹查找算法
    (參考了火夜風舞的C++新霖花園中的文章)
    這個算法可以說是A*算法的黃金搭檔,也是被稱為苛求速度的binary heap”的方法
    就是根據(jù)二叉樹原理,來維持開啟列表的“排序”,這里說的排序只是遵循二叉樹的原理的排序而已,即父節(jié)點永遠比子節(jié)點小,就像下面這樣
    ?? 1
    |??? |
    5??? 9
    |?? |? |
    7? 12 10
    二叉樹每個節(jié)點的父節(jié)點下標 = n / 2;(小數(shù)去掉)
    二叉樹每個節(jié)點的左子節(jié)點下標 = n * 2;右子節(jié)點下標 = n * 2 +1
    注意,這里的下標和它的值是兩個概念

    [c-sharp]?view plaincopy
  • //加入開放列表??
  • private?function?setOpen(newNode:Object,newFlg:Boolean?=?false):void?{??
  • ????var?new_index:int;??
  • ????if(newFlg){??
  • ????????newNode.open?=?true;??
  • ????????var?new_f:int?=?newNode.value_f;??
  • ????????_open.push(newNode);??
  • ????????new_index?=?_open.length?-?1;??
  • ????}else{??
  • ????????new_index?=?newNode.index;??
  • ????}??
  • ????while(true){??
  • ????????//找到父節(jié)點??
  • ????????var?f_note_index:int?=?new_index/2;??
  • ????????if(f_note_index?>?0){??
  • ????????????//如果父節(jié)點的F值較大,則與父節(jié)點交換??
  • ????????????if(_open[new_index].value_f?<?_open[f_note_index].value_f){??
  • ????????????????var?obj_note:Object?=?_open[f_note_index];??
  • ????????????????_open[f_note_index]?=?_open[new_index];??
  • ????????????????_open[new_index]?=?obj_note;??
  • ??????????????????????????????????
  • ????????????????_open[f_note_index].index?=?f_note_index;??
  • ????????????????_open[new_index].index?=?new_index;??
  • ????????????????new_index?=?f_note_index;??
  • ????????????}else{??
  • ????????????????break;??
  • ????????????}??
  • ????????}else{??
  • ????????????break;??
  • ????????}??
  • ????}??
  • }??
  • //取開放列表里的最小值??
  • private?function?getOpen():Object?{??
  • ????if(_open.length?<=?1){??
  • ????????return?null;??
  • ????}??
  • ????var?change_note:Object;??
  • ????//將第一個節(jié)點,即F值最小的節(jié)點取出,最后返回??
  • ????var?obj_note:Object?=?_open[1];??
  • ????_open[1]?=?_open[_open.length?-?1];??
  • ????_open.pop();??
  • ????_open[1].index?=?1;??
  • ????var?this_index:int?=?1;??
  • ????while(true){??
  • ????????var?left_index:int?=?this_index?*?2;??
  • ????????var?right_index:int?=?this_index?*?2?+?1;??
  • ????????if(left_index?>=?_open.length){??
  • ????????????break;??
  • ????????}else?if(left_index?==?_open.length?-?1){??
  • ????????????//當二叉樹只存在左節(jié)點時,比較左節(jié)點和父節(jié)點的F值,若父節(jié)點較大,則交換??
  • ????????????if(_open[this_index].value_f?>?_open[left_index].value_f){??
  • ????????????????change_note?=?_open[left_index];??
  • ????????????????_open[left_index]?=?_open[this_index];??
  • ????????????????_open[this_index]?=?change_note;??
  • ??????????????????????????????????
  • ????????????????_open[left_index].index?=?left_index;??
  • ????????????????_open[this_index].index?=?this_index;??
  • ??????????????????????????????????
  • ????????????????this_index?=?left_index;??
  • ????????????}else{??
  • ????????????????break;??
  • ????????????}??
  • ????????}else?if(right_index?<?_open.length){??
  • ????????????//找到左節(jié)點和右節(jié)點中的較小者??
  • ????????????if(_open[left_index].value_f?<=?_open[right_index].value_f){??
  • ????????????????//比較左節(jié)點和父節(jié)點的F值,若父節(jié)點較大,則交換??
  • ????????????????if(_open[this_index].value_f?>?_open[left_index].value_f){??
  • ????????????????????change_note?=?_open[left_index];??
  • ????????????????????_open[left_index]?=?_open[this_index];??
  • ????????????????????_open[this_index]?=?change_note;??
  • ??????????????????????????????????????
  • ????????????????????_open[left_index].index?=?left_index;??
  • ????????????????????_open[this_index].index?=?this_index;??
  • ??????????????????????????????????????
  • ????????????????????this_index?=?left_index;??
  • ????????????????}else{??
  • ????????????????????break;??
  • ????????????????}??
  • ????????????}else{??
  • ????????????????//比較右節(jié)點和父節(jié)點的F值,若父節(jié)點較大,則交換??
  • ????????????????if(_open[this_index].value_f?>?_open[right_index].value_f){??
  • ????????????????????change_note?=?_open[right_index];??
  • ????????????????????_open[right_index]?=?_open[this_index];??
  • ????????????????????_open[this_index]?=?change_note;??
  • ??????????????????????????????????????
  • ????????????????????_open[right_index].index?=?right_index;??
  • ????????????????????_open[this_index].index?=?this_index;??
  • ??????????????????????????????????????
  • ????????????????????this_index?=?right_index;??
  • ????????????????}else{??
  • ????????????????????break;??
  • ????????????????}??
  • ????????????}??
  • ????????}??
  • ????}??
  • ????return?obj_note;??
  • }??
  • 運行結果如圖

    我們看到,耗時15毫秒,速度是這三個方法里最快的,但是因為這個數(shù)字是不夠準確的,實際上,用二叉樹查找法,會讓A*算法的速度提高幾倍到10幾倍,在一些足夠復雜的地圖里,這個速度是成指數(shù)成長的。

    4,總結
    得出結論,用了A*算法,就要配套的用它的黃金搭檔,二叉樹,它可以讓你的游戲由完美走向更完美。

    總結

    以上是生活随笔為你收集整理的A*寻路算法与它的速度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。