Dijkstar算法的數學原理


Dijkstar算法是荷蘭數學家迪克斯屈拉(or迪傑斯特拉?)在1959年發現的一個算法。是現有的幾個求帶權圖中兩個頂點之間最短通路的算法之一。算是一個相當經典的算法了。

迪克斯屈拉算法應用於無向連通簡單帶權圖中,求出頂點a 與z 之間的最短通路的長度。我感覺其算法精髓就是:找到第一個與a 最靠近的頂點,然后找第二個,續行此法,直到找到的頂點是z 為止。該算法依賴於一系列的迭代。通過在每次迭代中添加一個頂點來構造出特殊頂點的集合。在每次迭代中完成一個標記過程。在這個標記過程中,用只包含特殊頂點的從a 到w 的最短通路的長度來標記w。添加到特殊頂點集合中的頂點是在還沒有成為特殊頂點的那些頂點中帶有最小標記的那個頂點。

算法細節是:首先用0 來標記a 而用∞ 標記其余的頂點。用記號L0(a) = 0 和 L0(v) = ∞ 表示在沒有發生任何迭代之前的這些標記(下標0表示第零次迭代)。這些標記是從a 到這些頂點的最短通路的長度。迪克斯屈拉算法是通過形成特殊頂點的集合來進行的。設Sk 表示在標記過程中k 次迭代之后的特殊頂點的集合。首先令S0 = Ø。集合Sk 是通過把不屬於Sk-1 的帶最小標記的頂點u 添加到Sk-1 里來形成的。一旦把u 添加到Sk 中,就更新所有不屬於Sk 的頂點的標記,使得頂點v 在第k 個階段的標記Lk(v) 是只包含Sk 中頂點(即已有的特殊頂點的集合再加上u )的從a 到v 的最短通路的長度。設v 是不屬於SK 的一個頂點。為了更新v 的標記,注意Lk(v) 是只包含Sk 中頂點的從a 到v 的最短通路的長度。當利用下面這個觀察結果時,就可以有效地完成這個更新:從a 到v 的只包含Sk 中頂點的最短通路,或者是從a 到v 的只包含Sk-1 中頂點(即不包括u 在內的特殊頂點)的最短通路,或者是在k-1 階段從a 到u 的最短通路上加上邊(u, v)。簡言之:Lk(a, v) = min{Lk-1(a, v), Lk-1(a, u) + w(u, v)}。這個過程這樣迭代:相繼地添加頂點到特殊頂點集合里, 直到添加到z 為止。當把z 添加到特殊頂點集合里時,它的標記就是從a 到z 的最短通路的長度。

下面,我們用數學歸納法來證明這個算法的正確性。

基礎步驟:在第零次迭代,即k = 0 時,S = {a},所以從a 到除a 外的頂點的最短通路的長度是∞,而從a 到a 本身的最短長度為0(這里允許一個通路不包含任何邊在內)。因此基本情況是正確的。

歸納步驟:用下列斷言做歸納假設:在第k 次迭代中

  1. 在S 中的頂點v (v ≠ 0)的標記是從a 到這個頂點的最短通路的長度。
  2. 不在S中的頂點的標記是(除了這個頂點自身之外)只包含S 中頂點的從a 到這個頂點的最短通路的長度。

  假定歸納假設對k 次迭代成立。令v 是在第k+1 次迭代中添加到S 中的頂點,使得v 是在第k 次迭代結束時帶最小標記的不在S 中的頂點(如果該頂點不唯一,可以采用帶最小標記的任意頂點)。根據歸納假設,可以看出在第k+1 次迭代之前,S 中的頂點都用從a 出發的最短通路的長度來標記。另外,必須用從a 到v 的最短通路的長度來標記v。假如情況不是這樣,那么在第k 次迭代結束時,就可能存在包含不在S 中的頂點的長度小於Lk(v) 的通路(因為Lk(v) 是在第k 次迭代之后,只包含S 中頂點的從a 到v 的最短通路的長度)。設u 是在這樣的通路里不屬於S 的第一個頂點。則存在一條從a 到u 的只包含S 中頂點的長度小於Lk(v) 的通路。這與對v 的選擇矛盾。因此,在第k+1 次迭代結束時1 成立。設u 是在第k+1 次迭代之后不屬於S 的一個頂點。從a 到u 的只包含S 里頂點的最短通路要么包含v,要么不包含v。若它不包含v,根據歸納假設,它的長度是Lk(u)。若它確實包含v,則它必然是這樣組成的:一條邊從a 到v 的具有最短可能長度的通路,其中包含S 中不同於v 的元素,后面接着從v 到u 的邊。在這種情況中它的長度是Lk(v) + w(v, u)。這樣就證明了2 成立,因為Lk+1(u) = min{Lk(u), Lk(v)+w(v, u)}。

根據數學歸納法原理,我們證明了迪克斯屈拉算法的正確性。

學習算法的過程中,我們常常考察算法的時間復雜度並依此衡量算法的優劣。因此一個很自然的問題就是:迪克斯屈拉算法的時間復雜度是多少?
  因為有n 個頂點,所以算法最多使用n-1 次迭代,因為每次迭代添加一個頂點到特殊頂點集合里。而在每個迭代的過程中,可以用不超過n-1 次比較來找出不在S 中的帶最小標記的頂點。而每次迭代里要更新的標記也不超過n-1 個。因此每次迭代使用不超過2(n-1) 次運算,粗略估計上界的話,可以得到O(n^2) 是其最壞時間復雜度。嚴格點的話,可以算一下:

  ∑(i~1,n)n-i + ∑(i~1,n)2(n-i-1) = (3n^2)/2 - 9n/2。

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM