1. 程式人生 > >演算法導論筆記:25所有節點對的最短路徑問題

演算法導論筆記:25所有節點對的最短路徑問題

       本章考慮在給定的有向加權圖G=(V, E),對於所有的節點u,v∈V,找到一條從節點u到節點v的最短路徑。希望以表格的形式表示輸出:第u行第v列給出的是節點u到節點v的最短路徑權重。

       對於這個問題,如果是執行|V|次單源最短路徑演算法來解決所有節點對的最短路徑問題,每一次使用一個不同的節點做為源節點。如果所有邊的權值是非負的,可以採用Dijkstra演算法。如果採用陣列來實現最小優先佇列,演算法的執行時間為O()=O()。使用二叉堆實現的最小優先佇列將使演算法的執行時間降低到O(VE lg V),這個時間在稀疏圖的情況下有很大改進,因為稀疏圖E <。如果採用斐波那契堆來實現最小優先佇列,其演算法執行時間為O(

)。

       如果圖中有權重為負值的邊,就必須採用效率更低的Bellman-Ford演算法,這樣的執行時間將使O(),在稠密圖的情況下,該執行時間為O()。

       本章研究的演算法將能做到更好,同時,本章還討論所有節點對最短路徑問題與矩陣乘法之間的關係。

       本章的多數演算法採用鄰接矩陣表示圖,假定節點的編號為1,2,...,|V|,因此,演算法的輸入是n*n的矩陣W,該矩陣代表一個有n個節點的有向圖的邊的權重:

演算法的輸出也是一個n*n的矩陣D = ()。其中 = δ(i, j)。

為了解決所有頂點間最短路徑問題,不僅要算出最短路徑的權值,而且要計算出一個前驅節點矩陣

,其中在i=j或從i到j沒有通路時為NIL,其他情況下表示從i到j的某條最短路徑上j的前驅頂點。由矩陣的第i行匯出的子圖應是根節點為i的一棵最短路徑樹。下面的演算法將打印出從節點i到節點j的一條最短路徑,該演算法類似於22章的PRINT-PATH過程:

PRINT-ALL-PAIRS-SHORTEST-PATH(, i, j)

       if i == j

              print  i

       else  if  == NIL

              print  “no path from i to j exists”

       else  PRINT-ALL-PAIRS-SHORTEST-PATH(

, i, )

              print  j

本章使用大寫字母表示矩陣,如W, L或D,使用帶下標的小寫字母表示矩陣中的個體元素,如

一:最短路徑和矩陣乘法

       本節討論一種動態規劃演算法,動態規劃演算法的步驟是:分析最優解的結構,遞迴定義最優解的值,自底向上計算最優解的值。

      之前討論單源最短路徑問題時,已經證明一條最短路徑的所有子路徑都是最短路徑。考慮從i到j的一條最短路徑p,假定p最多包含m條邊,假定沒有權值為負值的環路,且m為有限值。如果i=j,則p的權重為0且不包含任何邊。如果i和j不同,則將p分解為,其中路徑p’最多包含m-1條邊,所以δ(i, j) =δ(i, k)+

       設為從i到j的最多包含m條邊的任意路徑中的最小權重。當m=0時,從i到j之間存在一條沒有邊的最短路徑當且僅當i=j。所以:

對於m>0,有 =

       如果i到j之間最短路徑,則該路徑為簡單路徑,其中包含的邊最多為n-1,所以有:

       對於所有的節點i和j,有 = (),所以, = W。下面的演算法假定已經知道了W的情況下,給出如何計算,其中,L表示 L’表示該演算法的時間複雜度為O()


       現在可以看到上面的演算法與矩陣乘法的關係了。假定希望計算矩陣乘積C = A*B,對於i,j=1,2,...,n,有 = 。如果在 = 做出下面的替換:

就可以得到 = 。因此,如果對演算法EXTEND-SHORTEST-PATHS做出上面的替換,就可以得到標準的矩陣乘法的演算法。

       如前所述,矩陣包含的是最短路徑權重,所以,下面的演算法可以在O()時間內,計算出該矩陣陣列:


       我們的目標並不是要計算所有的矩陣,我們感興趣的僅僅是矩陣。因為有:正如傳統的矩陣乘法是相關的,所以由EXTEND-SHORTEST-PATHS過程所定義的演算法也是相關的。所以可以採用重複平方的技術計算該矩陣序列,該演算法的時間複雜度可以減少為O();


二:Floyd-Warshall演算法

       Floyd-Warshall演算法採用不同的動態規劃公式解決所有節點對的最短路徑問題,它的執行時間是O(),該演算法同樣假設:可以存在負權重的邊,但不能存在權重為負值的環路。

       Floyd-Warshall演算法考慮一條最短路徑上的中間節點,這裡簡單路徑p=<>上的中間節點指的是路徑p上除之外的任意結點。

       假定圖G的所有節點V={1,2,...,n},考慮其中的子集{1,2,...,k}。對於任意的結點i,j∈V,考慮從i到j的所有中間節點均取自集合{1,2,...,k}的路徑。並設p為其中的最短路徑,分兩種情況討論:

       a:如果結點k不是p上的中間節點,則路徑p上的所有中間節點都屬於集合{1,2,...,k-1}。因此,從i到j的中間節點均取自集合{1,2,...,k}的一條最短路徑,同樣也是從i到j的中間節點均取自集合{1,2,...,k-1}的一條最短路徑。

       b:如果結點k是路徑p上的中間節點,則p可以分解為,所以,p1上的所有中間節點都屬於集合{1,2,...,k-1},同理p2也是。

       根據上面的觀察,為從ij的所有中間節點全部取自集合{12...,k}的一條最短路徑的權重。當k=0時,i到j的路徑沒有中間節點,這樣的路徑最多隻有一條邊,所以有 = 。根據上面的討論,可以有:

    

因為對於所有的路徑,所有的中間節點都屬於集合{1,2,...,n},矩陣=給出的就是最終的矩陣: (i, j)演算法如下,該演算法的執行時間為O():

       

       另外,可以在計算的同時計算前驅矩陣,具體來說,需要計算一個矩陣序列:,這裡 =,並且定義為從ij的一條所有中間節點都取自集合{1,2,...,k}的最短路徑上,j的前驅結點。

       所以,可以給出的一個遞迴公式,當k=0時,從i到j的一條最短路徑上沒有中間節點,所以:

如果k>1,則有:

,這樣,就可以在計算的同時,計算前驅矩陣了,下面是一個運算過程:

       給定有向圖G=(V, E),結點集合V={1,2,..., n},希望判斷對於所有的i和j,圖G中是否包含一條從節點i到j的路徑,這稱為G的傳遞閉包問題。

       一種計算圖G的傳遞閉包的辦法是給E中的每條邊賦予權重1,然後執行Floyd-Warshall演算法,如果存在一條從i到j的路徑,則有< n;否則,。這種演算法的時間複雜度為O()。

       可以有另外一種演算法,該演算法使用邏輯或操作和邏輯與操作替換Floyd-Warshall演算法中的算術操作min和+。對於i,j,k=1,2,...,n,定義:如果圖G中存在一條從節點i到結點j的所有中間節點都取自集合{1,2,...,k}的路徑,則 = 1,否則, = 0所以,當k=0時,有:

,對於k>=1,有:

 = ,所以演算法如下:

       

    該演算法從結構上類似於Floyd-Warshall演算法,執行時間為O(),但是邏輯運算通常要比算術運算要快,而且TRANSITIVE-CLOSURE所需要的空間更少。

三:用於稀疏圖的johnson演算法

      johnson演算法可以在O()時間內找到所有節點對之間的最短路徑。對於稀疏圖來說,演算法在漸近意義上要好於矩陣的重複平方法或Floyd-Warshall演算法。演算法要麼返回一個包含所有節點對的最短路徑權重的矩陣,要麼報告輸人圖中存在一個負權值的迴路。演算法需要使用Dijkstra演算法和Bellman-Ford演算法作為其子程式。

      Johnson演算法使用的技術稱為重新賦予權重。如果圖G=(V, E)中所有的邊權重w皆為非負值,則可以採用對每個節點執行Dijkstra演算法找到所有節點對的最短路徑,如果該圖包含權重為負值的邊,但沒有權重為負值的環路,則只要計算出一組新的非負權重值,然後使用同樣的方法即可。新賦予的權重w’必須滿足下面的性質:

      a:多於所有頂點對u、v∈V,一條路徑p是利用加權函式w時從u到v的一條最短路徑,當且僅當p也是利用加權函式w’時從u到v的一條最短路徑。

      b:對於所有的邊(u, v),新的權重w’(u, v)是非負值。

      給定帶權重的有向圖G=(V, E),其權重函式為w,設h: v->R為任意函式,該函式將節點對映到實數上。對於每條邊(u,v),定義w’(u,v)= w(u, v) + h(u) – h(v)設p = <>為從節點到節點的任意一條路徑,那麼p是在使用權重函式w時從節點到節點的一條最短路徑,當且僅當p是在使用權重函式w’時從節點到節點的一條最短路徑。即w(p) = δ()當且僅當w’(p) =δ'()而且,圖G在使用權重函式w時不包含負值環路,當且僅當G在使用權重函式w’時不包含負值環路。

      下一個目標是確保第二個屬性成立,也就是所有邊(u, v),w’(u,v)>=0。如果給定圖G=(V, E),製作一幅新圖G’ = (V’, E’)。其中V’ = V∪{s}s是一個新節點。E’ = E {(s,v): v∈V},並且,對於所有節點vV, w(s, v) = 0所以G’不包含權重為負值的環路當且僅當圖G不包含權重為負值的環路。現在定義函式h,對於所有節點v∈V’,h(v) =(s)。所以w’(u, v) = w(u, v)+ h(u) – h(v)。根據三角不等式定理,可以得到w’(u, v)>=0

      Johnson演算法執行過程中需要使用Bellman-ford演算法和Dijkstra演算法。該演算法假定所有的邊都儲存在鄰接連結串列裡,返回一個|V|*|V|的矩陣D = ,其中 =δ(i, j)。

 

如果使用斐波那契堆來實現Dijkstra演算法,則Johnson演算法的執行時間為O(),使用更簡單的二叉堆實現,則時間為O(VE lg V),在稀疏圖的情況下,仍然比Floyd-warshall演算法快。