1. 程式人生 > >從floyd演算法學習動態規劃

從floyd演算法學習動態規劃

    floyd的相關概念看這裡

    floyd的程式碼實現其實很簡單:

     

    這裡我一直好奇的是這三個漂亮的迴圈是怎麼完成最終正確的結果的,如何證明它們是正確的。直到看了這裡的解析,才算弄懂了。

    floyd演算法是一個經典的動態規劃演算法。用通俗的語言來描述的話,首先我們的目標是尋找從點i到點j的最短路徑。從動態規劃的角度看問題,我們需要為這個目標重新做一個詮釋(這個詮釋正是動態規劃最富創造力的精華所在),floyd演算法加入了這個概念

    Ak(i,j):表示從i到j中途不經過索引比k大的點的最短路徑

    這個限制的重要之處在於,它將最短路徑的概念做了限制,使得該限制有機會滿足迭代關係,這個迭代關係就在於研究:假設Ak(i,j)已知,是否可以藉此推匯出Ak-1(i,j)。

    假設我現在要得到Ak(i,j),而此時Ak(i,j)已知,那麼我可以分兩種情況來看待問題:1. Ak(i,j)沿途經過點k;2. Ak(i,j)不經過點k。如果經過點k,那麼很顯然,Ak(i,j) = Ak-1(i,k) + Ak-1(k,j),為什麼是Ak-1呢?因為對(i,k)和(k,j),由於k本身就是源點(或者說終點),加上我們求的是Ak

(i,j),所以滿足不經過比k大的點的條件限制,且已經不會經過點k,故得出了Ak-1這個值。那麼遇到第二種情況,Ak(i,j)不經過點k時,由於沒有經過點k,所以根據概念,可以得出Ak(i,j)=Ak-1(i,j)。現在,我們確信有且只有這兩種情況---不是經過點k,就是不經過點k,沒有第三種情況了,條件很完整,那麼是選擇哪一個呢?很簡單,求的是最短路徑,當然是哪個最短,求取哪個,故得出式子:

    Ak(i,j) = min( Ak-1(i,j), Ak-1(i,k) + Ak-1(k,j) )

    現在已經得出了Ak(i,j) = Ak-1(i,k) + Ak-1(k,j)這個遞迴式,但顯然該遞迴還沒有一個出口,也就是說,必須定義一個初始狀態,事實上,這個初始狀態取決於索引k是從0開始還是從1開始,上面的程式碼是C寫的,是以0為開始索引,但一般描述演算法似乎習慣用1做開始索引,如果是以1為開始索引,那麼初始狀態值應設定為A0

了,A0(i,j)的含義不難理解,即從i到j的邊的距離。也就是說,A0(i,j) = cost(i,j) 。由於存在i到j不存在邊的情況,也就是說,在這種情況下,cost(i,j)無限大,故A0(i,j) = oo(當i到j無邊時)

    到這裡,已經列出了求取Ak(i,j)的整個演算法了,但是,最終的目標是求dist(i,j),即i到j的最短路徑,如何把Ak(i,j)轉換為dist(i,j)?這個其實很簡單,當k=n(n表示索引的個數)的時候,即是說,An(i,j)=dist(i,j)。那是因為當k已經最大時,已經不存在索引比k大的點了,那這時候的An(i,j)其實就已經是i到j的最短路徑了。

    從floyd演算法中不難看出,要設計一個好的動態規劃演算法,首先需要研究是否能把目標進行重新詮釋(這一步是最關鍵最富創造力的一步),轉化為一個可以被分解的子目標,如果可以轉化,就要想辦法尋找數學等式使目標收斂為子目標,如果這一步可以實現了,還需要研究該遞迴收斂式的出口,即初始狀態是否明確(這一步往往已經簡單了)。

    看了一些網文關於floyd的資料,發現溫故知新,還是從中補漏了之前沒有留意的知識。