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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

转:跳点搜索算法JPS及其优化(万字长文)

發(fā)布時間:2023/12/4 综合教程 28 生活家
生活随笔 收集整理的這篇文章主要介紹了 转:跳点搜索算法JPS及其优化(万字长文) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

歡迎關(guān)注作者git博客

1.引言
??尋路算法用途眾多,例如在游戲和地圖中。A*算法已經(jīng)眾所周知,對于其優(yōu)化也是層出不窮,然而性能并沒有取得突破性進(jìn)展。本文介紹JPS的效率、多線程、內(nèi)存、路徑優(yōu)化算法。為了測試搜索算法的優(yōu)化性能,實(shí)驗(yàn)中設(shè)置游戲場景使得起點(diǎn)和終點(diǎn)差距200個格子,需要尋路10000次。結(jié)果發(fā)現(xiàn),A*尋路總時間約2.6074x1011納秒(一秒為109納秒);基礎(chǔ)版JPS尋路總時間1.7037x1010納秒;利用位運(yùn)算優(yōu)化的JPS(下文稱JPS-Bit)尋路總時間3.2364x109納秒;利用位運(yùn)算和剪枝優(yōu)化的JPS(下文稱JPS-BitPrune)尋路總時間2.3703x109納秒;利用位運(yùn)算和預(yù)處理的JPS(下文稱JPS-BitPre)尋路總時間2.0043x109納秒;利用位運(yùn)算、剪枝和預(yù)處理三個優(yōu)化的JPS(下文稱JPS-BitPrunePre)尋路總時間9.5434x108納秒。其中的預(yù)處理在一些文章被稱為JPS+。本文的JPS-Bit和JPS-BitPrune都支持動態(tài)阻擋。本文解決了絕大部分開源JPS存在的潛在BUG:穿越阻擋,在圖2.2.1中,從H走到K時,穿越H右邊阻擋。
??上述結(jié)果表明,尋路200個格子的路徑,JPS的五個版本,平均消耗時間分別為1.7毫秒、0.32毫秒、0.23毫秒、0.02毫秒、0.095毫秒,尋路速度分別為A*算法的15倍、81倍、110倍、130倍、273倍,大幅度超越A*算法,標(biāo)志著尋路已經(jīng)不會成為性能的瓶頸。
事實(shí)上,在2012到2014年舉辦的三屆(目前為止只有三屆)基于Grid網(wǎng)格尋路的比賽GPPC(The Grid-Based Path Planning Competition)中,JPS已經(jīng)被證明是基于無權(quán)重格子,在沒有預(yù)處理的情況下尋路最快的算法。本文接下來介紹JPS基礎(chǔ)版本以及四個優(yōu)化算法;然后解讀GPPC2014的比賽,介紹GPPC的比賽地圖數(shù)據(jù),并從尋路時間、路徑長度、消耗內(nèi)存、失敗率等方面比較22個參賽尋路算法的優(yōu)劣。

2.JPS算法
2.1 算法介紹
??JPS又名跳點(diǎn)搜索算法(Jump Point Search),是由澳大利亞兩位教授于2011年提出的基于Grid格子的尋路算法。A*算法整體流程如表2.1.1所示,JPS算法在保留A*算法的框架的同時,進(jìn)一步優(yōu)化了A*算法尋找后繼節(jié)點(diǎn)的操作。為了說明JPS在A*基礎(chǔ)上的具體優(yōu)化策略,我們在圖2.1.1中給出A*和JPS的算法流程圖對比。由圖2.1.1看出,JPS與A*算法主要區(qū)別在后繼節(jié)點(diǎn)拓展策略上,不同于A*算法中直接獲取當(dāng)前節(jié)點(diǎn)所有非關(guān)閉的可達(dá)鄰居節(jié)點(diǎn)來進(jìn)行拓展的策略,JPS根據(jù)當(dāng)前結(jié)點(diǎn)current的方向、并基于搜索跳點(diǎn)的策略來擴(kuò)展后繼節(jié)點(diǎn),遵循“兩個定義、三個規(guī)則”(見表2.1.2,兩個定義確定強(qiáng)迫鄰居、跳點(diǎn),三個規(guī)則確定節(jié)點(diǎn)的拓展策略),具體流程如下:
??一,若current當(dāng)前方向是直線方向:(1)如果current左后方不可走且左方可走(即左方是強(qiáng)迫鄰居),則沿current左前方和左方尋找不在closedset的跳點(diǎn);(2)如果current當(dāng)前方向可走,則沿current當(dāng)前方向?qū)ふ也辉赾losedset的跳點(diǎn);(3)如果current右后方不可走且右方可走(右方是強(qiáng)迫鄰居),則沿current右前方和右方尋找不在closedset的跳點(diǎn);
??二,若current當(dāng)前方向?yàn)閷蔷€方向:(1)如果current當(dāng)前方向的水平分量可走(例如current當(dāng)前為東北方向,則水平分量為東,垂直分量為北),則沿current當(dāng)前方向的水平分量尋找不在closedset的跳點(diǎn);(2)如果current當(dāng)前方向可走,則沿current當(dāng)前方向?qū)ふ也辉赾losedset的跳點(diǎn);(3)如果current當(dāng)前方向的垂直分量可走(例如current當(dāng)前為東北方向,則水平分量為東,垂直分量為北),則沿current當(dāng)前方向的垂直分量尋找不在closedset的跳點(diǎn)。JPS尋找跳點(diǎn)的過程有三種優(yōu)化:一,位運(yùn)算;二;預(yù)處理;三;剪枝中間跳點(diǎn)。


圖2.1.1 A*和JPS的算法流程圖對比
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?表2.1.1 A*算法流程
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
Step 1. 將起始點(diǎn)start加入開啟集合openset
Step 2. 重復(fù)以下工作:
? ? 一、當(dāng)openset為空,則結(jié)束程序,此時沒有路徑。
? ? 二、尋找openset中F值最小的節(jié)點(diǎn),設(shè)為當(dāng)前點(diǎn)current
? ? 三、從openset中移出當(dāng)前點(diǎn)current
? ? 四、關(guān)閉集合closedset中加入當(dāng)前點(diǎn)current
? ? 五、若current為目標(biāo)點(diǎn)goal,則結(jié)束程序,此時有路徑生成,此時由goal節(jié)點(diǎn)開始逐級追溯路徑上每一個節(jié)點(diǎn)x的父節(jié)點(diǎn)parent(x),
? ? ? ? 直至回溯到開始節(jié)點(diǎn)start,此時回溯的各節(jié)點(diǎn)即為路徑。
? ? 六、對current的八個方向的每一個相鄰點(diǎn)neighbor
? ? ? ? 1.如果neighbor不可通過或者已經(jīng)在closedset中,略過。
? ? ? ? 2.如果neighbor不在openset中,加入openset中
? ? ? ? 3.如果neighbor在openset中,G值判定,若此路徑G值比之前路徑小,則neighbor的父節(jié)點(diǎn)為current,同時更新G與F值。
? ? ? ? ? ?反之,則保持neighbor和原父節(jié)點(diǎn)的關(guān)系與G、F值。G值表示從起點(diǎn)到當(dāng)前點(diǎn)路徑耗費(fèi);H值表示不考慮不可通過區(qū)域,當(dāng)前點(diǎn)到終點(diǎn)的理論路徑耗費(fèi);F=G+H。

術(shù)語參考:
? ? current?? ?當(dāng)前節(jié)點(diǎn)
? ? openset?? ?開啟節(jié)點(diǎn)集合,集合內(nèi)節(jié)點(diǎn)有待進(jìn)一步拓展
? ? closedset?? ?關(guān)閉節(jié)點(diǎn)結(jié)合,集合內(nèi)節(jié)點(diǎn)不再拓展
? ? neighbor?? ?鄰居,與當(dāng)前節(jié)點(diǎn)相鄰的節(jié)點(diǎn)
? ? parent(x)?? ?節(jié)點(diǎn)x的父節(jié)點(diǎn),即算法尋得的路徑中節(jié)點(diǎn)parent(x)的下一節(jié)點(diǎn)為x

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 表2.1.2 JPS算法的“兩個定義、三個規(guī)則”
定義一,強(qiáng)迫鄰居(forced neighbour):
? ? 如果節(jié)點(diǎn)n是x的鄰居,并且節(jié)點(diǎn)n的鄰居有阻擋(不可行走的格子),并且從parent(x)、x、n的路徑長度比其他任何從parent(x)到n且不經(jīng)過x的路徑短,
? ? 其中parent(x)為路徑中x的前一個點(diǎn),則n為x的強(qiáng)迫鄰居,x為n的跳點(diǎn),例如圖2.2.1中,尋找從S到E的路徑時,K為I的強(qiáng)迫鄰居(I為K的跳點(diǎn))。
? ? 這里不認(rèn)為從H到K能走,因?yàn)閷蔷€有阻擋(這點(diǎn)和論文不一致,但和代碼一致,因?yàn)槿绻鸋到K能直接到達(dá),會走進(jìn)H右邊的阻擋區(qū),大部分的JPS開源代碼根據(jù)論文都認(rèn)為H到K能直接到達(dá),所以存在穿越阻擋的情況),如果需要H到K可走,則K為H的強(qiáng)迫鄰居(H為K的跳點(diǎn))。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
定義二,跳點(diǎn)(jump point):
? ? (1)如果點(diǎn)y是起點(diǎn)或目標(biāo)點(diǎn),則y是跳點(diǎn),例如圖2.2.1中,S是起點(diǎn)也是跳點(diǎn),E是目標(biāo)點(diǎn)也是跳點(diǎn);
? ? (2)如果y有強(qiáng)迫鄰居則y是跳點(diǎn), 例如I是跳點(diǎn),請注意此類跳點(diǎn)和強(qiáng)迫鄰居是伴生關(guān)系,從定義一強(qiáng)迫鄰居的定義來看n是強(qiáng)迫鄰居,x是跳點(diǎn),
? ? ? ? 二者的關(guān)系是伴生的,例如圖2.2.1中K的鄰居只有I是跳點(diǎn),M雖然也是K的鄰居,但M不是跳點(diǎn),因?yàn)镵不是M的強(qiáng)迫鄰居;
? ? (3)如果parent(y)到y(tǒng)是對角線移動,并且y經(jīng)過水平或垂直方向移動可以到達(dá)跳點(diǎn),則y是跳點(diǎn),例如圖2.2.1中G是跳點(diǎn),因?yàn)閜arent(G)為S,
? ? ? ? S到G為對角線移動,從G到跳點(diǎn)I為垂直方向移動,I是跳點(diǎn),所以G也是跳點(diǎn)。?

規(guī)則一:
? ? JPS搜索跳點(diǎn)的過程中,如果直線方向(為了和對角線區(qū)分,直線方向代表水平方向和垂直方向,且不包括對角線等斜線方向,下文所說的直線均為水平方向和垂直方向)、
? ? 對角線方向都可以移動,則首先在直線方向搜索跳點(diǎn),再在對角線方向搜索跳點(diǎn)。
規(guī)則二:
? ? (1)如果從parent(x)到x是直線移動,n是x的鄰居,若有從parent(x)到n的路徑不經(jīng)過x且路徑長度小于或等于從parent(x)經(jīng)過x到n的路徑,
? ? ? ? 則走到x后下一個點(diǎn)不會走到n;
? ? (2)如果從parent(x)到x是對角線移動,n是x的鄰居,若有從parent(x)到n的路徑不經(jīng)過x且路徑長度小于從
? ? ? ? parent(x)經(jīng)過x到n的路徑,則走到x后下一個點(diǎn)不會走到n(相關(guān)證明見論文)。
規(guī)則三:
? ? 只有跳點(diǎn)才會加入openset,因?yàn)樘c(diǎn)會改變行走方向,而非跳點(diǎn)不會改變行走方向,最后尋找出來的路徑點(diǎn)也都是跳點(diǎn)。

2.2 JPS算法舉例
??下面舉例說明JPS具體的尋路流程。示例如圖2.2.1所示,5*5的網(wǎng)格,黑色代表阻擋區(qū),S為起點(diǎn),E為終點(diǎn)。JPS要尋找從S到E的最短路徑,首先初始化將起點(diǎn)S加入openset。從openset取出F值最小的點(diǎn)S,并從openset刪除,加入closedset,S的當(dāng)前方向?yàn)榭?#xff0c;則沿八個方向?qū)ふ姨c(diǎn),在該圖中從S出發(fā)只有下、右、右下(相對屏幕的絕對參考系:上、下、左、右,相對玩家的參考系:前、后、左、右,本文為了描述方便,絕對參考系和相對參考系會切換使用,請見諒)三個方向可走,但向下搜索到D遇到邊界,向右搜索到F遇到阻擋,因此都沒有找到跳點(diǎn),然后沿右下方向?qū)ふ姨c(diǎn),在G點(diǎn),根據(jù)上文定義二的第(3)條,parent(G)為S,praent(G)到S為對角線移動,并且G經(jīng)過垂直方向移動(向下移動)可以到達(dá)跳點(diǎn)I,因此G為跳點(diǎn) ,將G加入openset。從openset取出F值最小的點(diǎn)G,并從openset刪除,加入closedset,因?yàn)镚當(dāng)前方向?yàn)閷蔷€方向(從S到G的方向),因此在右(當(dāng)前方向水平分量)、下(當(dāng)前方向垂直分量)、右下(當(dāng)前方向)三個方向?qū)ふ姨c(diǎn),在該圖中從G出發(fā)只有向下可走,因此向下尋找跳點(diǎn),根據(jù)上文定義二的第(2)條找到跳點(diǎn)I,將I加入openset。從openset取出F值最小的點(diǎn)I,并從openset刪除,加入closedset,因?yàn)镮的當(dāng)前方向?yàn)橹本€方向(從G到I的方向),在I點(diǎn)時I的左后方不可走且左方、前方可走,因此沿左、左前、前尋找跳點(diǎn),但左前、前都遇到邊界,只有向左尋找到跳點(diǎn)Q(根據(jù)上文定義二的第(2)條)),因此將Q加入openset。從openset取出F值最小的點(diǎn)Q,并從openset刪除,加入closedset,因?yàn)镼的當(dāng)前方向?yàn)橹本€方向,Q的左后方不可走且左方、前方可走,因此沿左、左前、前尋找跳點(diǎn),但左前、前都遇到邊界,只有向左尋找到跳點(diǎn)E(根據(jù)上文定義二的第(1)條)),因此將E加入openset。從openset取出F值最小的點(diǎn)E,因?yàn)镋是目標(biāo)點(diǎn),因此尋路結(jié)束,路徑是S、G、I、Q、E。注意,本文不考慮從H能走到K的情況,因?yàn)閷蔷€有阻擋,如果需要H到K能直接到達(dá),則路徑是S、G、H、K、M、P、E,修改跳點(diǎn)的計(jì)算方法即可,但在游戲中如果H到K能直接到達(dá),則會穿越H右邊的阻擋。


圖2.2.1

表2-2-1.png 表2.2.1 A*和JPS的尋路消耗對比
??上述的JPS尋路效率是明顯快于A*的,原因在于:在從S到A沿垂直方向?qū)ぢ窌r,在A點(diǎn),如果是A*算法,會將F、G、B、H都加入openset,但是在JPS中這四個點(diǎn)都不會加入openset。對F、G、H三點(diǎn)而言,因?yàn)閺腟、A、F的路徑長度比S、F長,所以從S到F的最短路徑不是S、A、F路徑,同理S、A、G也不是最短路徑,根據(jù)上文規(guī)則二的第(1)條,走到A后不會走到F、G,所以F、G不會加入openset,雖然S、A、H是S到H的最短路徑,但因?yàn)榇嬖赟、G、H的最短路徑且不經(jīng)過A,據(jù)上文規(guī)則二的第(1)條,從S走到A后,下一個走的點(diǎn)不會是H,因此H也不會加入openset;對B點(diǎn)而言,根據(jù)上文規(guī)則三,B不是跳點(diǎn),也不會加入openset,直接走到C即可。表2.2.1所示為A*和JPS在尋路消耗中的對比,D. Age: Origins、D. Age 2、StarCraft為三個游戲龍騰世紀(jì):起源、、龍騰世紀(jì)2、星際爭霸的場景圖集合,M.Time表示操作openset和closedset的時間,G.Time表示搜索后繼節(jié)點(diǎn)的時間??梢夾*大約有58%的時間在操作openset和closedset,42%時間在搜索后繼節(jié)點(diǎn);而JPS大約14%時間在操作openset和closedset,86%時間在搜索后繼節(jié)點(diǎn)。避免在openset中加入太多點(diǎn),從而避免過多的維護(hù)最小堆是JPS比A*快的原因(最小堆插入新元素時間復(fù)雜度log(n),刪除最小元素后調(diào)整堆,時間復(fù)雜度也為log(n)),實(shí)際上在從S到E的尋路過程中,進(jìn)入openset的只有S、G、I、Q、E。

3.JPS優(yōu)化
3.1 JPS算法的五個效率優(yōu)化算法
3.1.1 JPS效率優(yōu)化之一JPS-Bit:位運(yùn)算優(yōu)化
??利用位運(yùn)算優(yōu)化的JPS-Bit的關(guān)鍵優(yōu)化思路在于利用位運(yùn)算來優(yōu)化JPS中節(jié)點(diǎn)拓展的效率。JPS-Bit和下文介紹的JPS-BitPrune支持動態(tài)阻擋,當(dāng)動態(tài)阻擋出現(xiàn)時,將格子標(biāo)記為阻擋,當(dāng)動態(tài)阻擋消失時,將格子標(biāo)記為非阻擋,如果動態(tài)阻擋占據(jù)k個格子,則時間復(fù)雜度為O(k)。下面以圖3.1.1.1中的場景示例說明如何將位運(yùn)算融合于JPS算法中,其中黑色部分為阻擋,假設(shè)當(dāng)前位置為I(標(biāo)藍(lán)位置),當(dāng)前方向?yàn)橛?#xff0c;位運(yùn)算中使用1代表不可走,0代表可走,則I當(dāng)前行B的八位可以用八個bit:00000100表示,I上一行B-的八位可以用八個bit:00000000表示,I的下一行B+的八位可以用八個bit:00110000表示。在當(dāng)前行尋找阻擋的位置可以用CPU的指令__builtin_clz(B)(返回前導(dǎo)0的個數(shù)),即當(dāng)前阻擋在第5個位置(從0開始)。尋找當(dāng)前行的跳點(diǎn)可以用__builtin_clz(((B->>1) && !B -) ||((B+>>1) && !B+)) 尋找,例如本例中(B+>>1) && !B+為:(00110000 >> 1) && 11001111,即00001000,而(B->>1) &&!B為00000000,所以__builtin_clz(((B->>1) && !B -) ||((B+>>1) && !B+))為__builtin_clz(00001000)為4,所以跳點(diǎn)為第4個位置M(從0開始)。注意論文中使用_builtin_ffs(((B-<<1) && !B -) ||((B+<<1) && !B+)),__builtin_ffs(x)返回x的最后一位1是從后向前第幾位,比如7368(1110011001000)返回4,因?yàn)檎撐膶Ω褡拥腷it編碼采用小端模式,而這里對格子的bit編碼采用大端模式。
??由于JPS-Bit使用運(yùn)算效率更高的位運(yùn)算和CPU指令運(yùn)算來優(yōu)化原始JPS節(jié)點(diǎn)擴(kuò)展過程中的遍歷操作,JPS-Bit的算法效率高于原始的JPS,實(shí)測中JPS-Bit的尋路時間比JPS縮短5倍左右。


圖3.1.1.1
3.1.2 JPS效率優(yōu)化之二JPS-BitPrune:位運(yùn)算與剪枝優(yōu)化
??利用位運(yùn)算和剪枝優(yōu)化的JPS-BitPrune在JPS-Bit的基礎(chǔ)上進(jìn)一步進(jìn)行剪枝優(yōu)化,剪掉不必要的中間跳點(diǎn)(見表2.1.2,定義二第(3)條定義),根據(jù)定義二,中間跳點(diǎn)在節(jié)點(diǎn)拓展過程中只具有簡單的“承接”作用,不具備拓展價值,將中間跳點(diǎn)放入openset會增大擴(kuò)展的次數(shù),因此JPS-BitPrune將中間跳點(diǎn)全部刪除,將中間跳點(diǎn)后繼跳點(diǎn)中的非中間跳點(diǎn)的父跳點(diǎn)改為中間跳點(diǎn)的父跳點(diǎn),可以有效避免冗余的節(jié)點(diǎn)拓展運(yùn)算。
??拐點(diǎn)獲取:值得一提的是,JPS-BitPrune由于刪除了中間跳點(diǎn),因此JPS-BitPrune需要在搜索到完整的路徑之后以一定的策略在最后尋得的路徑中加入中間拐點(diǎn),使得每兩個相鄰的路徑節(jié)點(diǎn)之間都是垂直、水平、對角線方向可達(dá)的。對此,JPS-BitPrune采用的具體方法如下:
??假設(shè)目前搜索到的路徑為start(jp1)、jp2、jp3…jpk…end(jpn),對每兩個相鄰的跳點(diǎn)jpi、jpi+1,一,如果jpi、jpi+1的x坐標(biāo)或者y坐標(biāo)相等,說明這兩個跳點(diǎn)在同一個水平方向或垂直方向,可以直線到達(dá),無需在這兩個跳點(diǎn)之間加入拐點(diǎn);二,如果jpi、jpi+1的x坐標(biāo)和y坐標(biāo)都不相等,(1)如果x坐標(biāo)的差dx(即jpi的x坐標(biāo)減去jpi+1的x坐標(biāo))和y坐標(biāo)的差dy的絕對值相等,說明這兩個跳點(diǎn)在對角線方向,也可以直線到達(dá),無需在這兩個跳點(diǎn)之間加入拐點(diǎn);(2)如果x坐標(biāo)的差dx和y坐標(biāo)的差dy的絕對值不相等,說明這兩個跳點(diǎn)不在對角線方向,并且有可能不能直線到達(dá)(因?yàn)樘c(diǎn)附近有阻擋),此時jpi、jpi+1之間只需要加入一個從jpi出發(fā)離jpi+1最近的對角線上的點(diǎn)即可(jpi、jpi+1不能水平、垂直、對角線到達(dá),說明jpi、jpi+1之間一定存在被剪枝的中間跳點(diǎn),只需要補(bǔ)上離jpi+1最近的一個中間跳點(diǎn)充當(dāng)拐點(diǎn)即可,該拐點(diǎn)即為jpi沿對角線方向走min(dx,dy)步到達(dá)的點(diǎn))。


圖3.1.2.1 JPS-BitPrune的剪枝優(yōu)化示例
??下面以圖3.1.2.1的問題場景示例JPS-BitPrune如何在剪枝的同時進(jìn)行尋路。起點(diǎn)為S(坐標(biāo)為(1,1),即S(1,1)),節(jié)點(diǎn)1、4、6均為中間跳點(diǎn):因?yàn)楣?jié)點(diǎn)2、3是滿足定義二第(2)條的跳點(diǎn),所以節(jié)點(diǎn)1是為了到達(dá)節(jié)點(diǎn)2、3的中間跳點(diǎn),同理節(jié)點(diǎn)4、6也為中間跳點(diǎn)。在剪枝中間跳點(diǎn)之前,要將中間跳點(diǎn)的后繼節(jié)點(diǎn)的父節(jié)點(diǎn)調(diào)整為該中間跳點(diǎn)的父節(jié)點(diǎn)。例如圖3.1.2.1中,節(jié)點(diǎn)1的后繼跳點(diǎn)為節(jié)點(diǎn)2、3、4,其中節(jié)點(diǎn)4也為中間跳點(diǎn),刪掉中間跳點(diǎn)中的節(jié)點(diǎn)1后,節(jié)點(diǎn)2、3的父跳點(diǎn)由節(jié)點(diǎn)1改為節(jié)點(diǎn)S,刪除中間跳點(diǎn)中的節(jié)點(diǎn)4后,節(jié)點(diǎn)4的后繼跳點(diǎn)5的父跳點(diǎn)由節(jié)點(diǎn)4改為節(jié)點(diǎn)S(節(jié)點(diǎn)4的父跳點(diǎn)為節(jié)點(diǎn)1,但節(jié)點(diǎn)1已經(jīng)被刪掉,因此回溯到節(jié)點(diǎn)S),刪除中間跳點(diǎn)中的節(jié)點(diǎn)6后,節(jié)點(diǎn)6的后繼跳點(diǎn)7的父跳點(diǎn)由節(jié)點(diǎn)6改為節(jié)點(diǎn)S(節(jié)點(diǎn)6的父跳點(diǎn)為節(jié)點(diǎn)4,但節(jié)點(diǎn)4被刪,節(jié)點(diǎn)4的父跳點(diǎn)節(jié)點(diǎn)1也被刪,因此回溯到節(jié)點(diǎn)S)。上述過程是簡化的邏輯描述,實(shí)際運(yùn)行中的做法是從節(jié)點(diǎn)S尋找跳點(diǎn),首先找到中間跳點(diǎn)節(jié)點(diǎn)1,然后在水平方向和垂直方向?qū)ふ业教c(diǎn)節(jié)點(diǎn)2、3,將節(jié)點(diǎn)2、3的父跳點(diǎn)設(shè)為節(jié)點(diǎn)S;繼續(xù)沿對角線方向?qū)ふ姨c(diǎn),走到節(jié)點(diǎn)4后,沿水平方向和垂直方向?qū)ふ业教c(diǎn)節(jié)點(diǎn)5,將節(jié)點(diǎn)5的父跳點(diǎn)設(shè)為節(jié)點(diǎn)S;繼續(xù)沿對角線方向?qū)ふ姨c(diǎn),走到節(jié)點(diǎn)6后,沿水平方向和垂直方向?qū)ふ业教c(diǎn)7,將跳點(diǎn)7的父跳點(diǎn)設(shè)為節(jié)點(diǎn)S。因此JPS-BitPrune獲得路徑S(1,1) 、節(jié)點(diǎn)7(4,6)。因?yàn)槁窂街蠸(1,1)無法垂直、水平、對角線方向走到節(jié)點(diǎn)7(4,6),需要加入中間拐點(diǎn),根據(jù)上述的拐點(diǎn)添加策略,有dx為3,dy為5,需要從S沿對角線走3步,即節(jié)點(diǎn)6(4,4)可作為中間拐點(diǎn),因此,在圖3.1.2.1的示例中,JPS-BitPrune最后構(gòu)建的完整路徑為S(1,1) 、節(jié)點(diǎn)6(4,4) 、節(jié)點(diǎn)7(4,6)。

3.1.2.1 剪枝的優(yōu)化效率
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?下面通過對比剪枝前后的JPS節(jié)點(diǎn)拓展的情況來說明剪枝操作的優(yōu)化效率:
場景一(無剪枝) 如果不對中間跳點(diǎn)進(jìn)行剪枝,那么從節(jié)點(diǎn)S尋路到節(jié)點(diǎn)7將經(jīng)歷如下過程:
? ? (1)從節(jié)點(diǎn)S搜索跳點(diǎn),找到跳點(diǎn)節(jié)點(diǎn)1,openset此時只有節(jié)點(diǎn)1;
? ? (2)從openset取出F值最小跳點(diǎn)節(jié)點(diǎn)1,并搜索節(jié)點(diǎn)1的后繼跳點(diǎn),水平方向和垂直方向找到跳點(diǎn)節(jié)點(diǎn)2、3,對角線方向找到跳點(diǎn)節(jié)點(diǎn)4,此時openset有節(jié)點(diǎn)2、3、4;
? ? (3)從openset取出F值最小跳點(diǎn)節(jié)點(diǎn)4,并搜索節(jié)點(diǎn)4的后繼跳點(diǎn),水平和垂直方向找到跳點(diǎn)節(jié)點(diǎn)5,對角線方向找到跳點(diǎn)6,此時openset有節(jié)點(diǎn)2、3、5、6;
? ? (4)從openset取出F值最小跳點(diǎn)節(jié)點(diǎn)6,垂直方向找到跳點(diǎn)7,此時openset有節(jié)點(diǎn)2、3、5、7;
? ? (5)從openset取出F值最小的跳點(diǎn)節(jié)點(diǎn)7,為目的點(diǎn),搜索結(jié)束,因此完整路徑為節(jié)點(diǎn)S(1,1)、節(jié)點(diǎn)1(2,2) 、節(jié)點(diǎn)4(3,3) 、節(jié)點(diǎn)6(4,4) 、節(jié)點(diǎn)7(4,6)。
? ? ? ? JPS在到達(dá)目的節(jié)點(diǎn)7之前,需要接連拓展中間跳點(diǎn)1,4,6。
1
2
3
4
5
6
7
8
場景二(剪枝中間跳點(diǎn)) 在剪枝中間跳點(diǎn)之后,從節(jié)點(diǎn)S尋路到節(jié)點(diǎn)7的流程得到了明顯簡化:
(1)從節(jié)點(diǎn)S尋找跳點(diǎn),首先找到中間跳點(diǎn)節(jié)點(diǎn)1,然后在水平方向和垂直方向?qū)ふ业教c(diǎn)節(jié)點(diǎn)2、3,將節(jié)點(diǎn)2、3的父跳點(diǎn)設(shè)為節(jié)點(diǎn)S;繼續(xù)沿對角線方向?qū)ふ姨c(diǎn),
走到節(jié)點(diǎn)4后,沿水平方向和垂直方向?qū)ふ业教c(diǎn)節(jié)點(diǎn)5,將節(jié)點(diǎn)5的父跳點(diǎn)設(shè)為節(jié)點(diǎn)S;繼續(xù)沿對角線方向?qū)ふ姨c(diǎn),走到節(jié)點(diǎn)6后,沿水平方向和垂直方向
尋找到跳點(diǎn)7,將跳點(diǎn)7的父跳點(diǎn)設(shè)為節(jié)點(diǎn)S;繼續(xù)沿對角線方向?qū)ふ姨c(diǎn),遇到阻擋,搜索終止,此時openset有節(jié)點(diǎn)2、3、5、7;
(2)從openset取出F值最小的跳點(diǎn)節(jié)點(diǎn)7,為目的點(diǎn),搜索結(jié)束,此時獲得的路徑為S(1,1) 、節(jié)點(diǎn)7(4,6)。不同于無剪枝的JPS需要拓展中間跳點(diǎn)1、4、6,
在JPS-BitPrune中,節(jié)點(diǎn)1、4、6作為中間跳點(diǎn)均被剪枝,有效避免了冗余的節(jié)點(diǎn)拓展,尋路效率得到大大提升。

3.1.3 JPS效率優(yōu)化之三JPS-BitPre:位運(yùn)算與預(yù)處理
??本優(yōu)化中的預(yù)處理在一些文章被稱為JPS+。JPS-BitPre和JPS-BitPrunePre都不支持動態(tài)阻擋,因?yàn)閯討B(tài)阻擋的出現(xiàn)會導(dǎo)致八個方向最多能走的步數(shù)發(fā)生變化,從而導(dǎo)致預(yù)處理的結(jié)果不再準(zhǔn)確。利用位運(yùn)算和預(yù)處理的JPS-BitPre依舊采用JPS-Bit中的位運(yùn)算,而其中的預(yù)處理則是對每個點(diǎn)存儲八個方向最多能走的步數(shù)step,這個step值將影響JPS中節(jié)點(diǎn)的拓展順序和拓展“跨度”,加快尋路效率。由于預(yù)處理版本的JPS需要存儲八個方向最多能走多少步,如果地圖大小是N*N,每個方向最多能走的步數(shù)用16位數(shù)表示,則需要存儲空間N*N*8*16bit,如果N為1024,則大概需要存儲空間為16M,存儲空間占用較大,使用該版本JPS時需要權(quán)衡是否以空間換時間。為降低預(yù)處理數(shù)據(jù)占用的內(nèi)存,本項(xiàng)目也提供每個方向最多能走的步數(shù)用8位數(shù)表示的源碼,尋路速度和步數(shù)用16位數(shù)表示的源碼差異1%左右,但內(nèi)存減少一半。另外預(yù)處理的時間對小于1024*1024個格子的圖可以在1秒內(nèi)處理完,但對于2048*2048個格子的圖需要一小時左右處理完,建議提前預(yù)處理完并存到文件中,程序啟動時讀文件即可。
??其中,step值由跳點(diǎn)、阻擋、邊界等決定,如果遇到跳點(diǎn),則step為走到跳點(diǎn)的步數(shù);否則step為走到阻擋或邊界的步數(shù)。例如對圖3.1.3.1中的N點(diǎn),向北最多走到節(jié)點(diǎn)8,即2步;向南最多走到節(jié)點(diǎn)4,即4步;向西最多走到節(jié)點(diǎn)6,即3步;向東最多走到節(jié)點(diǎn)2(節(jié)點(diǎn)2是滿足定義二第(2)條的跳點(diǎn)),即5步;西北最多走到節(jié)點(diǎn)7,即2步;東北最多走到節(jié)點(diǎn)1(節(jié)點(diǎn)為1是上文定義二第(3)條定義的跳點(diǎn)),即1步;西南最多走到節(jié)點(diǎn)5,即3步;東南最多走到節(jié)點(diǎn)3(節(jié)點(diǎn)3是上文定義二第(3)條定義的跳點(diǎn)),即3步。


圖3.1.3.1 JPS-BitPre尋路的場景示例
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 以圖3.1.3.1中的場景為例,要尋找從節(jié)點(diǎn)N到節(jié)點(diǎn)T的路徑,JPS-BitPre的尋路流程如下:
(1)從openset取出節(jié)點(diǎn)N, 從N沿八個方向?qū)ふ姨c(diǎn),根據(jù)預(yù)處理得到的各方向最遠(yuǎn)可走的step值,可以快速確定八個方向最遠(yuǎn)能到達(dá)的節(jié)點(diǎn)
? ? {1,2,3,4,5,6,7,8},如圖3.1.3.1所示,其中,節(jié)點(diǎn)1、2、3均為滿足定義二的跳點(diǎn),直接加入openset,對于節(jié)點(diǎn)4、5、6、7、8,
? ? 首先判斷終點(diǎn)T位于以N為中心的南、西南、西、西北、北的哪部分,因?yàn)門位于西南方向,只有節(jié)點(diǎn)5位于西南方向,因此節(jié)點(diǎn)4、6、7、8直接略過,
? ? 在從N到5的方向上,N可走3步,而N和T的x坐標(biāo)差絕對值dx為1,y坐標(biāo)差絕對值dy為2,因此將從節(jié)點(diǎn)N到節(jié)點(diǎn)5方向上走min(dx,dy)步即節(jié)點(diǎn)11,加入openset;
(2)從openset取出F值最小節(jié)點(diǎn)11,垂直方向找到跳點(diǎn)T,加入openset;三,從openset取出F值最小節(jié)點(diǎn)T,為目的點(diǎn),搜索結(jié)束,
? ? 此時獲得的路徑為N(4,5)、節(jié)點(diǎn)11(3,4) 、節(jié)點(diǎn)T(3,3)。
1
2
3
4
5
6
7
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 為了說明JPS-BitPre尋路的準(zhǔn)確性與高效率,這里給出原始JPS-Bit從N到T的尋路流程作為對比:
(1)從openset取出節(jié)點(diǎn)N后,需要沿八個方向?qū)ふ姨c(diǎn),節(jié)點(diǎn)1、3、11為上文定義二第(3)條定義的跳點(diǎn),加入openset,
? ? 節(jié)點(diǎn)2為上文定義二的第(2)條定義的跳點(diǎn),加入openset;
(2)從openset取出F值最小節(jié)點(diǎn)11,垂直方向找到跳點(diǎn)T,加入openset;
(3)從openset取出F值最小跳點(diǎn)T,為目的點(diǎn),搜索結(jié)束,此時獲得的路徑也為N(4,5)、節(jié)點(diǎn)11(3,4) 、節(jié)點(diǎn)T(3,3)。
1
2
3
4
5
??對比發(fā)現(xiàn),經(jīng)過預(yù)處理的JPS-BitPre和只使用位運(yùn)算的JPS-Bit最后尋得的路徑是一樣的,然而,由于JPS-BitPre無需在每一步節(jié)點(diǎn)拓展過程中沿各方向?qū)ふ姨c(diǎn),其可以根據(jù)預(yù)處理得到的step值快速確定openset的備選節(jié)點(diǎn),從而大大提升尋路效率。

3.1.4 JPS效率優(yōu)化之五:空間換時間
openset采用最小堆實(shí)現(xiàn),最小堆的底層數(shù)據(jù)結(jié)構(gòu)是一個數(shù)組,從最小堆中插入、刪除時間復(fù)雜度為O(logn)。除了刪除還需要查找操作,每次找到一個跳點(diǎn),都需要判斷在最小堆中是否有,如果有,則判斷是否更新G值、F值、父跳點(diǎn)等,如果沒有,則加入openset。在最小堆的中查找操作時間復(fù)雜度O(n),因此需要優(yōu)化。closedset存的是已經(jīng)從openset中彈出的跳點(diǎn),實(shí)際只需要對每個跳點(diǎn)加個標(biāo)記即可,如果跳點(diǎn)打上標(biāo)記,則表示是closedset中跳點(diǎn),否則不是。綜合上述需求,針對1km*1km的地圖,構(gòu)建2k*2k的二維數(shù)組matrix,數(shù)組每個元素pnode均為一個指針,指針的對象類型包括節(jié)點(diǎn)id、是否擴(kuò)展過expanded(即是否在closedset中)、G值、F值、父跳點(diǎn)指針parent、在最小堆中的索引index等12個byte。如果地圖(x,y)處是搜索到的跳點(diǎn),首先檢查在二維數(shù)組matrix對應(yīng)的(x,y)處指針pnode是否為空,如果為空,表示該跳點(diǎn)之前未搜索過,從內(nèi)存池new出一個跳點(diǎn),將指針加到最小堆openset中,并在執(zhí)行shift up、shift down操作之后,記錄在最小堆中的索引index;如果不為空,則表示該跳點(diǎn)之前搜索過,首先檢查expand標(biāo)記,如果為真,則表示在closedset中,直接跳過該跳點(diǎn);否則表示在openset中,通過matrix(x,y)記錄的在openset中的索引index找到對應(yīng)的指針,檢查matrix(x,y)和openset(index)的指針是否相等進(jìn)行二次確認(rèn),然后檢查判斷是否需要更新G值、F值、父跳點(diǎn)等,采用空間換時間的方法可以將openset和closedset中查找操作降為O(1)。游戲中有很多場景,無需為每個場景構(gòu)建一個matrix,以最大的場景的大小構(gòu)建一個matrix即可。

3.2 多線程支持
游戲服務(wù)器普遍采用單進(jìn)程多線程架構(gòu),多線程下,不能對JPS尋路加鎖,否則尋路串行化,失去了多線程的優(yōu)勢,為了支持多線程JPS尋路,需要將一些變量聲明為線程獨(dú)有thread_local,例如上文提到的為了優(yōu)化openset和closedset的查找速度,構(gòu)建的二維跳點(diǎn)指針數(shù)組matrix。該數(shù)組必須為線程獨(dú)有,否則,不同線程在尋路時,都修改matrix元素指向的跳點(diǎn)數(shù)據(jù),例如A線程在擴(kuò)展完跳點(diǎn)后,將expanded標(biāo)記為真,B線程再試圖擴(kuò)展該跳點(diǎn)時,發(fā)現(xiàn)已經(jīng)擴(kuò)展過,就直接跳過,導(dǎo)致尋路錯誤。

3.3 JPS內(nèi)存優(yōu)化
3.3.1 分層
JPS的地圖格子粒度如果采用0.5m*0.5m,每個格子占1bit,則1km*1km的地圖占用內(nèi)存2k*2k/8個byte,即0.5M;為了向上、向下也能通過取32位數(shù)獲得向上、向下的32個格子阻擋信息,需要存將地圖旋轉(zhuǎn)90度后的阻擋信息;上文JPS優(yōu)化之四:不可達(dá)兩點(diǎn)提前判斷,需要存連通信息,假設(shè)連通區(qū)數(shù)目最多15個,則需內(nèi)存2k*2k/2個byte,即2m,則內(nèi)存為:原地圖阻擋信息0.5m、旋轉(zhuǎn)地圖阻擋信息0.5m、連通區(qū)信息2m,即3m。另外,上文提到用空間換時間的方法,為了優(yōu)化openset和closedset的查找速度,構(gòu)建二維跳點(diǎn)指針數(shù)組matrix。1km*1km的地圖,格子粒度為0.5m*0.5m,構(gòu)建出的matrix指針數(shù)組大小為2k2k4byte即為8m,為了支持多線程,該matrix數(shù)組必須為thread_local,即線程獨(dú)有,16個線程共需內(nèi)存16*8m即為128m,內(nèi)存空間太大,因此需要優(yōu)化這部分內(nèi)存。首先將2k*2k分成100*100的塊,即20*20個塊,20*20個塊為第一層數(shù)組firLayerMatrix,100*100為第二層數(shù)組secLayerMatrix,firLayerMatrix的400個元素為400個指針,每個指針初始化為空,當(dāng)遍歷到的跳點(diǎn)屬于firLayerMatrix中(x,y)的塊時,則從內(nèi)存池new出100*100*4byte的secLayerMatrix,secLayerMatrix每個元素也是一個指針,指向一個從內(nèi)存池new出來的跳點(diǎn)。例如,搜索2k*2k的地圖時,在(231,671)位置找到一個跳點(diǎn),首先檢查firLayerMatrix的(2,6)位置指針是否為空,如果為空,則new出100*100*4byte的secLayerMatrix,繼續(xù)在secLayerMatrix查找(31,71)位置檢查跳點(diǎn)的指針是否為空,如果為空,則從內(nèi)存池new出來跳點(diǎn),加入openset,否則檢查跳點(diǎn)的expanded標(biāo)記,如果標(biāo)記為真,表示在closedset中,直接跳過該點(diǎn),否則表示在openset中,判斷是否更新G值、F值、父節(jié)點(diǎn)等。因?yàn)橛螒蛑蠳PC尋路均為短距離尋路,JPS尋路區(qū)域最大為80*80,一個secLayerMatrix是100*100,因此只需要一個secLayerMatrix,則兩層matrix大小為:20*20*4byte+100*100*4byte即為0.04m。所以16個線程下,總內(nèi)存為:原地圖阻擋信息0.5m、旋轉(zhuǎn)地圖阻擋信息0.5m、連通區(qū)信息2m、兩層matrix0.04m*16,共3.64M,游戲中場景最多不到20個,所有場景JPS總內(nèi)存為72.8M。

3.3.2 內(nèi)存池
在搜索過程中,每次將一個跳點(diǎn)加入openset,都需要new出對應(yīng)的節(jié)點(diǎn)對象,節(jié)點(diǎn)對象中存節(jié)點(diǎn)id、父節(jié)點(diǎn)、尋路消耗等共12個byte,為了減少內(nèi)存碎片,以及頻繁new的時間消耗,需要自行管理內(nèi)存池,每次new節(jié)點(diǎn)對象時,均從內(nèi)存池中申請,為了防止內(nèi)存池增長過大,需要限制搜索步數(shù)。內(nèi)存池是在真正使用內(nèi)存之前,先申請分配一定數(shù)量的、大小相等(一般情況下)的內(nèi)存塊留作備用。當(dāng)有新的內(nèi)存需求時,就從內(nèi)存池中分出一部分內(nèi)存塊,若內(nèi)存塊不夠再繼續(xù)申請新的內(nèi)存。本文的內(nèi)存池共有兩個:一,跳點(diǎn)的內(nèi)存池,初始大小為800個跳點(diǎn),當(dāng)new的跳點(diǎn)數(shù)目超出800個,即停止尋路,因?yàn)榉?wù)器用JPS進(jìn)行NPC的尋路,NPC不會進(jìn)行長距離尋路,假設(shè)NPC尋路上限距離是20m,則尋路區(qū)域面積是40m40m,格子數(shù)8080即6400,經(jīng)統(tǒng)計(jì)跳點(diǎn)數(shù)目占所有格子數(shù)目的比例不到1/10, 即跳點(diǎn)數(shù)目少于640,因此800個跳點(diǎn)足夠使用,800個跳點(diǎn)共占內(nèi)存800byte*12,即為9.6k,忽略不計(jì);二,secLayerMatrix指向的100*100*4byte的內(nèi)存池,因?yàn)槊看螌ぢ范夹枰辽僖粋€secLayerMatrix,如果每次尋路都重新申請,尋路完后再釋放,會造成開銷,因此secLayerMatrix指向的1001004byte的空間也在內(nèi)存池中,申請時,從內(nèi)存池拿出,釋放時,放回內(nèi)存池即可,secLayerMatrix內(nèi)存池占內(nèi)存0.04m。

3.4 路徑優(yōu)化
如圖3.4.1所示,綠色格子為起點(diǎn),紅色格子為終點(diǎn),灰色格子為跳點(diǎn),藍(lán)線為JPS搜出來的路徑,灰色虛線為搜索過程??梢钥闯?#xff0c;從綠色格子到紅色格子可以直線到達(dá),而JPS搜索出來的路徑卻需要轉(zhuǎn)折一次,在游戲表現(xiàn)上,會顯得比較奇怪。因此在JPS搜索出來路徑后,需要對路徑進(jìn)行后處理。比如JPS搜出來的路徑有A、B、C、D、E、F、G、H八個點(diǎn),走到A時,需要采樣檢查A、C是否直線可達(dá),如果A、C直線可達(dá),再檢查A、D是否直線可達(dá),如果A、D直線可達(dá),繼續(xù)檢查A、E,如果A、E直線不可達(dá),則路徑優(yōu)化為A、D、E、F、G、H,走到D時,再檢查D、F是否直線可達(dá),如果D、F直線可達(dá),繼續(xù)檢查D、G,如果D、G直線不可達(dá),則路徑優(yōu)化為A、D、F、G、H。依此類推,直到走到H。因?yàn)椴蓸訖z查的速度很快,大約占JPS尋路時間的1/5,而且只有當(dāng)走到一個路點(diǎn)后,才采樣檢查該路點(diǎn)之后的路點(diǎn)是否可以合并,將采樣的消耗平攤在行走的過程中,因此采樣的消耗可以忽略。


圖3.4.1
4.GPPC競賽解讀
4.1 GPPC競賽與地圖數(shù)據(jù)集
??基于格子的尋路一直是被廣泛研究的熱點(diǎn)問題,也有很多已經(jīng)發(fā)表的算法,但是這些算法沒有相互比較過,因此也難辨優(yōu)劣,使用者如何選擇算法也有很大的困難。為了解決這個問題,美國丹佛大學(xué)的Nathan R. Sturtevant教授創(chuàng)辦了基于Grid格子的尋路比賽:The Grid-Based Path Planning Competition,簡稱GPPC,目前已經(jīng)在2012、2013、2014舉辦過三次,下文主要討論GPPC2014。
??GPPC比賽用的地圖集如表4.1.1所示,地圖數(shù)據(jù)主要分為游戲場景圖和人造地圖。其中來自游戲場景圖的數(shù)據(jù)有三類:Starcraft(星際爭霸)、Dragon Age2(龍騰世紀(jì)2)、Dragon Age:origins(龍騰世紀(jì):起源),三個游戲分別提供地圖11、57、27張,提供尋路問題29970、54360、44414個。來自人造地圖有三類:迷宮、隨機(jī)、房間,三類數(shù)據(jù)分別提供地圖18、18、18張,提供尋路問題145976、32228、27130個。六類數(shù)據(jù)共提供地圖132張,尋路問題347868個。圖4.1.1中給出三個游戲的場景圖示例,圖4.1.2中給出三類人造地圖示例,其中黑色代表阻擋區(qū),白色代表可行走區(qū)。地圖大小從100*100到1550*1550,六個地圖集的大小分布如圖4.1.3所示,橫坐標(biāo)是格子數(shù),縱坐標(biāo)是地圖數(shù)目,最小的地圖來自Dragon Age:origins(龍騰世紀(jì):起源),最大的地圖來自Starcraft(星際爭霸)和人造數(shù)據(jù)。

表4.1.1 GPPC比賽用的地圖集

圖4.1.1 GPPC的三類游戲場景地圖示例

圖4.1.2 GPPC的三類人造場景地圖示例

圖4.1.3 GPPC的六類地圖大小分布
4.2 GPPC的評價體系
??GPPC在相同的配置下運(yùn)行參賽算法,其中CPU的配置是Xeon E5620,四核處理器、2.4Ghz主頻,12G內(nèi)存。為了消除誤差,GPPC要求對每個參賽的尋路方法在34868個尋路問題上運(yùn)行5遍,共尋路34868*5,即174340次,所以下文介紹的總運(yùn)行時間等指標(biāo)都是尋路174340次的結(jié)果匯總。其中運(yùn)行時間包括:加載預(yù)處理數(shù)據(jù)和尋路時間,而預(yù)處理時間并不計(jì)算在運(yùn)行時間內(nèi)。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GPPC定義如下13個指標(biāo)來評價尋路算法(其中,路徑表示從起點(diǎn)到終點(diǎn)的完整路徑):
1. Total (秒):尋路174340次所花費(fèi)的總時間。
2. Avg(毫秒):尋路174340次的平均時間。
3. 20 Step(毫秒):尋找到路徑的前20步所花費(fèi)的平均時間。該指標(biāo)衡量最快多久可以跟隨路徑,在實(shí)時交互例如游戲中,該指標(biāo)很重要。
4. Max Segment(毫秒):每條路徑最長段的尋路平均時間。該指標(biāo)衡量在實(shí)時交互中,尋路方法最差情況下的表現(xiàn)。
5. Avg Len:路徑的平均長度。如果A尋路算法在長路徑上表現(xiàn)好,在短路徑上表現(xiàn)不好;B尋路算法在長路徑上表現(xiàn)不好,在短路徑上表現(xiàn)好,則A的該指標(biāo)優(yōu)于B的指標(biāo),因?yàn)锳vg Len的增加主要來自長路徑。該指標(biāo)偏向于在長路徑上表現(xiàn)好的尋路方法。
6. Avg Sub-Opt:尋到的路徑長度/最優(yōu)路徑長度 的平均值。如果A尋路方法在長路徑上表現(xiàn)好,在短路徑上表現(xiàn)不好;B路徑尋路方法在長路徑上表現(xiàn)不好,在短路徑上表現(xiàn)好,則B的該指標(biāo)優(yōu)于A的指標(biāo),因?yàn)?74340次尋路大多數(shù)的路徑都是短路徑。該指標(biāo)偏向于在短路徑上表現(xiàn)好的尋路方法。
7. Num Solved:在174340次尋路中,成功的數(shù)目。
8. Num Invalid:在174340次尋路中,返回錯誤路徑的數(shù)目。錯誤路徑:路徑的相鄰路點(diǎn)無法直線到達(dá)。
9. Num UnSolved:在174340次尋路中,沒有尋找到路徑的數(shù)目。
10. RAM(before)(兆):尋路算法在加載預(yù)處理數(shù)據(jù)后,尋路之前占用的內(nèi)存。
11. RAM(after)(兆):尋路174340次后占用的內(nèi)存,包括所有尋路結(jié)果占用的內(nèi)存。
12. Storage:預(yù)處理的數(shù)據(jù)占用的硬盤大小。
13. Pre-cmpt(分鐘):預(yù)處理數(shù)據(jù)花費(fèi)的時間,表3中該列數(shù)字之前的“+”表示采用并行計(jì)算進(jìn)行預(yù)處理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
4.3 GPPC參賽算法及其比較
??目前為止參加GPPC競賽的算法共有22個,其中參加GPPC2014的有14個,可大致分為如下4類:一,對A*的改進(jìn),例如Relaxed A*(RA*)和A* Bucket;二,利用格子特點(diǎn)的算法,例如Jump Point Search(JPS)和SubGoal Graphs;三,預(yù)先生成任意兩點(diǎn)第一個路點(diǎn)的壓縮數(shù)據(jù)庫,例如SRC;四,基于節(jié)點(diǎn)優(yōu)先級的算法,例如Contraction Hierarchy(CH)。
??表4.3.1給出參加GPPC2012、2013、2014的共22個算法的結(jié)果對比,其中前14個為參與GPPC2014的算法。其中第一列(Entry列)為算法名,其后13列給出每個算法在13個指標(biāo)下的表現(xiàn)。第一列被黑體加粗的算法表示該算法在某些指標(biāo)(帕累托最優(yōu)的指標(biāo))達(dá)到帕累托最優(yōu),該算法所在的行被加粗的指標(biāo),表示帕累托最優(yōu)的指標(biāo)。帕累托最優(yōu)表示:沒有其他算法在帕累托最優(yōu)的指標(biāo)上均優(yōu)于當(dāng)前算法。例如JPS(2012)帕累托最優(yōu)的指標(biāo):第6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage,達(dá)到帕累托最優(yōu),表示沒有其他算法在6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage上均優(yōu)于JPS(2012)。22種算法沒有嚴(yán)格的優(yōu)劣關(guān)系,只是在不同指標(biāo)下的表現(xiàn)各有側(cè)重,算法的使用者可基于對不同指標(biāo)的具體需求來選擇自己適用的算法。

表4.3.1 GPPC2014的比賽結(jié)果
下面給出所有在GPPC中獲得帕累托最優(yōu)的算法,本文介紹的JPS算法位列其中。
1.RA*(2014):第10個指標(biāo)RAM(before)和第12個指標(biāo)Storage帕累托最優(yōu)。
2.BLJPS(2014):第2個指標(biāo)Avg、第6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage帕累托最優(yōu)。
3.BLJPS2(2014):第2個指標(biāo)Avg、第6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage帕累托最優(yōu)。
4.RA-Subgoal(2014):第2個指標(biāo)Avg和第12個指標(biāo)Storage帕累托最優(yōu)。
5.NSubgoal(2014):第2個指標(biāo)Avg、第6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage帕累托最優(yōu)。
6.CH(2014):第2個指標(biāo)Avg、第6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage帕累托最優(yōu)。
7.SRC-dfs-i(2014):第3個指標(biāo)20 Step和第4個指標(biāo)Max Segment帕累托最優(yōu)。
8.SRC-dfs(2014):第2個指標(biāo)Avg和第6個指標(biāo)Avg Sub-Opt帕累托最優(yōu)。
9.JPS(2012):第6個指標(biāo)Avg Sub-Opt和第12個指標(biāo)Storage帕累托最優(yōu)。本文的主角JPS在未使用預(yù)處理的算法中Avg Sub-Opt表現(xiàn)最優(yōu)。
10.PDH(2012):第3個指標(biāo)20 Step和第12個指標(biāo)Storage帕累托最優(yōu)。
11.Tree(2013):第2個指標(biāo)Avg帕累托最優(yōu)。
---------------------?
作者:runzhiwang.github.io?
來源:CSDN?
原文:https://blog.csdn.net/yjxxtd/article/details/93506231?
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!

總結(jié)

以上是生活随笔為你收集整理的转:跳点搜索算法JPS及其优化(万字长文)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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