(1)有序和無序的開啟列表:簡單的方法
最簡單的方法就是順序儲存每個節點,然後每次需要提取最低耗費元素的時候都遍歷整個列表。這提供可快速的插入速度,但是移除速度可能是最慢的,因為你需要檢查每個元素才能夠確定哪個才是f值最低的。
通常你可以保持你列表處於有序狀態來提公升效率。這花費了稍微多一點的預處理時間,因為你每次插入新元素都必須把他們放在恰當的位置。不過移除元素倒是很快。你只要移除第乙個元素就可以了,它一定是f值最低的。 有很多方法可以保持你的資料有序(選擇排序,氣泡排序,快速排序,等等)並且你可以用你最熟悉的搜尋引擎找到這方面的文章。不過我們至少可以先提幾種想法。最簡單的方法可能是,當你需要新增新元素的時候,從列表開始的地方,依次比較每個元素的f值和要插入的f值的大小。一旦找到乙個相等或者更高的f值,你就可以把新元素插入到列表中那個元素的前面。取決於你使用的計算機於亞,使用class或者struct實現的鍊錶可能是不錯的方法。 這種方法可以通過保持列表中所有元素的平均值來得到改進,使用這個平均值來決定是從頭(如上所說)還是從尾開始處理。總的說來,比平均f值低的新元素將被從頭開始處理,而比平均f值高的則從末尾開始。這種方法可以節省一半的時間。
複雜一些,但是更快的方法是把這一想法提高到新的層次使用快速排序,它基本上是從比較新元素和列表中間元素的f值開始。如果新元素的f值低,你接著把它和1/4處元素進行比較,如果還是更低你就比較它和1/8處的元素,如此這般,不斷的折半你的列表並且比較,直到找到合適的位置。這個描述很簡單,你可能會想到網上尋找快速排序的更多資料。這比至此描述的任何方法都快。 二叉堆
(2)基於堆儲存結構
二叉堆和剛才說的快速排序很像,經常被那些苛求a*速度的人使用。根據我的經驗,二叉堆平均提高尋路速度2-3倍,對於包含大量節點的地圖(也就是說100×100節點或者更多)效果更明顯。友情提醒,然而二叉堆很難處理,除非你使用含有大量節點的地圖,速度至關重要,否則不值得為它頭痛。 文章其他的部分深入說明了二叉堆和它在a*演算法中的用途。如果你對我的文章存有疑惑,在文章末尾進一步閱讀的小節中提供了更多的觀點。
仍然有興趣?好,我們繼續。。。
在有序列表中,每個元素都按照由低到高或由高到低的順序儲存在恰當的位置。這很有用,但是還不夠。事實上,我們並不關心數字127是否比128在更低的位置上。我們只是想讓f值最低的元素能放在列表頂端以便容易訪問。列表的其他部分即使是混亂的也不必在意。列表的其他部分只有在我們需要另乙個f值最低的元素的時候,才有必要保持有序。
基本上,我們真正需要的是乙個「堆」,確切的說,是個二叉堆。二叉堆是一組元素,其中最大或者最小(取決於需要)的元素在堆頂端。既然我們要尋找f值最小的元素,我們就把它放在堆頂端。這個元素有兩個子節點,每個的f值等於,或者略高於這個元素。每個子節點又有兩個子節點,他們又有和他們相等或略高的子節點。。。依次類推。這裡是乙個堆可能的樣子:
注意,f值最低的元素(10)在最頂端,第二低的元素(20)是它的乙個子節點。可是,其後就沒有任何疑問了。在這個特定的二叉堆裡,第三低的元素是24,它離堆頂有兩步的距離,它比30小,但是30卻在左側離堆頂一步之遙的地方。簡單的堆放,其他的元素在堆的哪個位置並不重要,每個單獨的元素只需要和它的父節點相等或者更高,而和它的兩個子節點相比,更低或者相等,這就可以了。這些條件在這裡都完全符合,所以這是個有效的二叉堆。
很好,你可能會想,這的確有趣,但是如何把它付諸實施呢?嗯,關於二叉堆的乙個有趣的事實是,你可以簡單的把它儲存在乙個一維陣列中。
在這個陣列中,堆頂端的元素應該是陣列的第乙個元素(是下標1而不是0)。兩個子節點會在2和3的位置。這兩個節點的4個子節點應該在4-7的位置。
總的來說,任何元素的兩個子節點可以通過把當前元素的位置乘以2(得到第乙個子節點)和乘2加1(得到第二個子節點)來得到。就這樣,例如堆中第三個元素(數值是20)的兩個子節點,可以在位置2*3 = 6和2*3 +1 = 7這兩個位置找到。那兩個位置上的數字非別是30和24,當你檢視堆的時候就能理解。
你其實不必要知道這些,除了表明堆中沒有斷層之外知道這些沒有任何價值。7個元素,就完整的填滿了乙個三層堆的每一層。然而這並不是必要的。為了讓我們的堆有效,我們只需要填充最底層之上的每一行。最底層自身可以是任意數值的元素,同時,新的元素按照從左到右的順序新增。這篇文章描述的方法就是這樣做的,所以你不必多慮。
向堆中新增元素
當我們實際在尋路演算法中使用二叉堆的時候,還需要考慮更多,但是現在我們只是學習一下如何使用二叉堆。我跳過這部分以便更容易理解基本的東西。我會在文章後面的部分給出處理這一切的完整公式,但了解這些細節仍然十分重要。
大致的,為了往堆裡新增元素,我們把它放在陣列的末尾。然後和它在 當前位置/2 處的父節點比較,分數部分被圓整。如果新元素的f值更低,我們就交換這兩個元素。然後我們比較這個元素和它的新父節點,在 (當前位置)/2 ,小數部分圓整,的地方。如果它的f值更低,我們再次交換。我們重複這個過程直到這個元素不再比它的父節點低,或者這個元素已經到達頂端,處於陣列的位置1。
我們來看如何把乙個f值為17的元素新增到已經存在的堆中。我們的堆裡現在有7個元素,新元素將被新增到第8個位置。這就是堆看起來的樣子,新元素被加了下劃線。 10 30 20 34 38 30 24 17
接下來我們比較它和它的父節點,在 8/2 也就是 4的位置上。位置4當前元素的f值是34。既然17比34低,我們交換兩元素的位置。現在我們的堆看起來是這樣的:
10 30 20 17 38 30 24 34
然後我們把它和新的父節點比較。因為我們在位置4,我們就把它和 4/2 = 2 這個位置上的元素比較。那個元素的f值是30。因為17比30低,我們再次交換,現在堆看起來是這樣的: 10 17 20 30 38 30 24 34
接著我們比較它和新的父節點。現在我們在第二個位置,我們把它和 2/2 = 1,也就是堆頂端的比較。這次,17不比10更低,我們停止,堆保持成現在的樣子。
從堆中刪除元素
從堆中刪除元素是個類似的過程,但是差不多是反過來的。首先,我們刪除位置1的元素,現在它空了。然後,我們取堆的最後乙個元素,移動到位置1。在堆中,這是結束的條件。以前的末元素被加了下劃線。
34 17 20 30 38 30 24
然後我們比較它和兩個子節點,它們分別在位置(當前位置*2)和(當前位置* 2 + 1)。如果它比兩個子節點的f值都低,就保持原位。反之,就把它和較低的子節點交換。那麼,在這裡,該元素的兩個子節點的位置在 1 * 2 = 2和 1*2 + 1 = 3。顯然,34不比任何乙個子節點低,所以我們把它和較低的子節點,也就是17,交換。結果看起來是這樣:
17 34 20 30 38 30 24
接著我們把它和新的子節點比較,它們在 2*2 = 4,和2*2 + 1 = 5的位置上。它不比任何乙個子節點低,所以我們把它和較低的乙個子節點交換(位置4上的30)。現在是這樣:
17 30 20 34 38 30 24
最後一次,我們比較它和新的子節點。照例,子節點在位置 4*2 = 8和4*2+1 = 9的位置上。但是那些位置上並沒有元素,因為列表沒那麼長。我們已經到達了堆的底端,所以我們停下來。
二叉堆為什麼這麼快?
現在你知道了堆基本的插入和刪除方法,你應該明白為什麼它比其他方法,比如說插入排序更快。假設你有個有1000個節點的開啟列表,在一格有很多節點的相當大的地圖上,這不是不可能(記住,即使是100×100的地圖,上面也有10,000個節點)。如果你使用插入排序,從起點開始,到找到新元素恰當的位置,在把新元素插入之前,平均需要做500次比較。
使用二叉堆,你從底端開始,可能只要1-3次比較就能把新元素插入到正確的位置。你還需要9次比較用來從開啟列表中移除乙個元素,同時保持堆仍然有序。在a*中,你通常每次只需要移除乙個元素(f值最低的元素),在任意位置新增0到5個新節點(就像主文章裡描述的2d尋路)。這總共花費的時間大約是同樣數量節點進行插入排序的1%。差別隨你地圖的增大(也就是節點更多)呈幾何增長。地圖越小,就越沒優勢,這也是為什麼你的地圖和節點越少,二叉堆的價值就越低的原因。
光圈範圍,F值
鏡頭還有乙個很重要的指標就是光圈值。它的大小決定通過鏡頭進入感光單元的光線多少,直接影響著影象的亮度。這樣在光線不足的環境 如室內 拍攝動態的 就會需要有大光圈配合,而夜景的拍攝就更不用提了。可能選擇大光圈的相機,對於數位相機而言,f 4就不錯了,而f 2.8算是很好,奧林巴斯的c 50zoom,c...
PR曲線與F值
pr曲線與roc曲線都是評價乙個機器學習演算法的標準,那麼不同點在 呢,首先我們還是看下面幾個值 tp 真正 實際正 fp 假正 實際負 tn 真負 實際負 fn 假負 實際正 與roc曲線不同的是,pr曲線用 precision 準確率和 recall 召回率分別作為橫縱座標。precision ...
STC15F2K61S2儲存結構
目錄特殊功能暫存器 stc15f2k61s2的儲存結構由程式儲存器和資料儲存器構成。該微控制器的程式儲存器和資料儲存器是獨立編址的,特殊功能暫存器與高128b ram共用相同的位址範圍,都使用 80h ffh,特殊功能暫存器必須用直接定址指令訪問。程式儲存器用於存放使用者程式 資料和 等資訊。stc...