圖形算法可視化

最近看了一些和圖形、算法可視化相關(guān)的文章和代碼,挺有意思,于是自己也學(xué)著做了些東西。

迷宮生成算法

迷宮小時(shí)候玩過(guò),但從來(lái)沒(méi)琢磨過(guò)迷宮是怎么設(shè)計(jì)的,以為就是有人慢慢畫(huà)出來(lái)的。看過(guò)網(wǎng)上這篇文章后,才知道,原來(lái)還可以隨機(jī)生成:

Maze Generation - Visualizing Algorithms

自己找了些資料參考,試著實(shí)現(xiàn)了幾種之后,才慢慢領(lǐng)會(huì)到其中的一些原理。

算法中討論的迷宮滿足一個(gè)條件:迷宮中任意兩點(diǎn)間有且只有一條路徑

要隨機(jī)生成滿足這樣條件的迷宮,看起來(lái)很復(fù)雜啊。但是換個(gè)思路之后,就發(fā)現(xiàn)問(wèn)題沒(méi)那么復(fù)雜了。

“樹(shù)”其實(shí)就滿足這個(gè)條件:

  • 所有的枝葉都可以通過(guò)樹(shù)枝、樹(shù)干連通
  • 由于枝干不交叉,沒(méi)有環(huán),所以枝葉間連通的路徑是唯一的

所以,生成隨機(jī)的迷宮的問(wèn)題,就轉(zhuǎn)化為生成隨機(jī)的樹(shù)的過(guò)程。進(jìn)一步,可以拆分為以下過(guò)程:

  • 在迷宮網(wǎng)格內(nèi)隨機(jī)選擇一個(gè)點(diǎn)作為“樹(shù)根”
  • 從樹(shù)根開(kāi)始,向隨機(jī)選擇的某一方向開(kāi)始生長(zhǎng)
  • 直到樹(shù)的枝干通過(guò)不斷生長(zhǎng)、分叉充滿迷宮的所有網(wǎng)格

迷宮生成的不同算法,區(qū)別主要在兩點(diǎn):

  • 從一個(gè)位置開(kāi)始生成后一直向隨機(jī)方向延伸的最大長(zhǎng)度:有的是延伸一個(gè)網(wǎng)格后立即更換生長(zhǎng)點(diǎn),有的則是直到無(wú)法繼續(xù)延伸后才更換生長(zhǎng)點(diǎn)
  • 更換生長(zhǎng)點(diǎn)時(shí)選擇位置的方式:有的是記錄當(dāng)前枝干經(jīng)過(guò)的網(wǎng)格,依次后退,有的干脆是完全隨機(jī)選擇一個(gè)有可能向外生長(zhǎng)的點(diǎn)

深度優(yōu)先算法

深度優(yōu)先算法,也叫遞歸回溯算法。它會(huì)一直向隨機(jī)方向生長(zhǎng),直到無(wú)法生成的位置,向后回退一格,繼續(xù)生長(zhǎng),直到所有網(wǎng)格被填充。

深度優(yōu)先算法生成的迷宮

深度優(yōu)先算法生成的迷宮,會(huì)有比較明顯的長(zhǎng)路徑,這是因?yàn)闃?shù)在一開(kāi)始生成的時(shí)候,空間比較充裕,會(huì)有一些長(zhǎng)的枝條產(chǎn)生。

Prim 算法

Prim 算法不會(huì)一直沿著一條路徑進(jìn)行探索,而是不斷嘗試隨機(jī)的生長(zhǎng)點(diǎn)。所以 Prim 算法生成的迷宮,分叉會(huì)比較多:

Prim 算法生成的迷宮

算法實(shí)現(xiàn)

綜合以上兩種算法,我既不希望有過(guò)長(zhǎng)的路徑,也不希望有太多的分叉,所以我采用的思路的嘗試沿著一條路徑延伸最多一定的長(zhǎng)度,然后再隨機(jī)選擇生長(zhǎng)點(diǎn)執(zhí)行相同的過(guò)程。

下圖是在 40*40 的迷宮網(wǎng)格,每條枝干最多生長(zhǎng)15個(gè)網(wǎng)格的效果:

迷宮生成算法 - 開(kāi)始

這是執(zhí)行了一段時(shí)間之后,迷宮大部分區(qū)域已經(jīng)走過(guò):

迷宮生成算法 - 執(zhí)行中

這是最后的效果:

迷宮生成算法 - 完成

項(xiàng)目地址:luobotang/maze
在線DEMO:迷宮生成算法 - luobotang

算法可視化

上面例子中的迷宮生成算法過(guò)程,迷宮網(wǎng)格是通過(guò) HTML 的 <table> 實(shí)現(xiàn),相鄰網(wǎng)格的連通效果,則是借助 CSS 的邊框樣式。

整個(gè)迷宮的所有網(wǎng)格由二維數(shù)組表示,每個(gè)網(wǎng)格的狀態(tài)包含是否被訪問(wèn)、與相鄰網(wǎng)格的連通情況等。

算法的執(zhí)行過(guò)程由定時(shí)器驅(qū)動(dòng),每次執(zhí)行一步,從而有動(dòng)畫(huà)的效果。

最短路徑算法

與圖相關(guān)的最短路徑算法,在生活中應(yīng)該是有著廣泛的應(yīng)用了吧,從一個(gè)位置到另一個(gè)位置,借助已有的路網(wǎng),計(jì)算最短的路徑。當(dāng)然,還會(huì)因?yàn)槁窙r、臨時(shí)障礙,以及用戶(hù)的個(gè)人偏好而產(chǎn)生不同結(jié)果。

對(duì)于“圖”上,基本要素就是:

  • 節(jié)點(diǎn)
  • 邊:根據(jù)場(chǎng)景的不同,還會(huì)有方向、權(quán)重屬性

Dijkstra 算法

Dijkstra 算法是用于計(jì)算最短路徑的比較著名的一種算法,早在1956年就發(fā)表了。

Dijkstra 算法如果看算法的詳細(xì)執(zhí)行過(guò)程,有點(diǎn)復(fù)雜,但是其基本思路在做過(guò)之后會(huì)發(fā)現(xiàn),貌似很簡(jiǎn)單。

已確定 A 到 B 的最短路徑,B 與 C 相連,且 A 到 B 的距離加上 B 到 C 的距離,小于當(dāng)前 A 到 C 的距離,那么 A 到 B 再到 C 就是 A 到 C 的最短路徑。

圖示例

如上圖所示,最初從 A 來(lái)看,到 C 的最短路徑是 A -> C,距離是 4。但繼續(xù)探索到 B 后,發(fā)現(xiàn) A -> B 加上 B -> C 距離只有 3,比 A -> C 的距離要小,所以 A 到 C 的最短路徑更新為:A -> B -> C。

算法實(shí)現(xiàn)

基本思路上面都介紹了,細(xì)節(jié)就是每次探索節(jié)點(diǎn)時(shí),都選擇當(dāng)前未探索過(guò)的到源點(diǎn)距離最短的節(jié)點(diǎn),這樣可以源點(diǎn)到當(dāng)前點(diǎn)的路徑已經(jīng)是最短路徑。

算法可視化

圖的可視化比較復(fù)雜了,只是繪制出來(lái)其實(shí)不難,但要將節(jié)點(diǎn)、邊進(jìn)行合理布局就比較麻煩,是另一個(gè)話題了。

我選擇用 vis.js 提供的 Network 來(lái)繪制圖形,然后通過(guò)逐步執(zhí)行算法來(lái)更新圖形。

這是初始狀態(tài):

Dijkstra 算法 - 開(kāi)始

執(zhí)行過(guò)程中,會(huì)記錄節(jié)點(diǎn)是否被訪問(wèn),以及當(dāng)前的最短路徑和對(duì)應(yīng)的距離:

Dijkstra 算法 - 執(zhí)行中

全部執(zhí)行完成后,就得到了源點(diǎn)開(kāi)始到圖中所有節(jié)點(diǎn)的最短路徑:

Dijkstra 算法 - 完成

項(xiàng)目地址:luobotang/graph
在線DEMO:Dijkstra 算法 - luobotang

結(jié)語(yǔ)

其實(shí)上面這些實(shí)現(xiàn)起來(lái)并沒(méi)有特別困難,有很多現(xiàn)成的資料和代碼可以利用。但是不管什么飯,都得自己吃過(guò)、消化過(guò)才是自己的。所以,我把自己吸收的“營(yíng)養(yǎng)”記錄下來(lái),如果你也有興趣,不妨自己上手一試。

最后,感謝閱讀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容