算法之道—形而之上謂之道

算法之道—形而之上謂之道

作者: baiyuzhong分類:架構實踐  閱讀:9,239 次添加評論

文 / 鄒恆明

1966年3月的一天,美國加州大學洛杉磯分校的Andrew J. Viterbi教授在給研究生講解纏繞編碼的時序譯碼算法SDCD。但不管他如何講解,學生就是聽不明白。思來想去,Viterbi覺得學生不能理解的原因是該算法的證明過於複雜。於是他開始考慮如何簡化這個證明。在經歷了持久的煩躁和困惑後,他靈感頓現:需要簡化的不是算法的證明,而是算法本身。於是Viterbi對SDCD算法進行了少許修改,提出了基於Trellis的概率譯碼算法。這個算法就是後來著名的CDMA技術的基石。Viterbi也因此而身價暴漲(創立了高通公司,賺取了數十億美元)。

一種新算法引來革命性的技術和財富的暴漲,算法的作用不可謂不大。但理解算法、改變人生,或者以算法的思維來進行思考,卻對很多人來說是鏡中花、水中月,難以觸摸。

從廣義上定義,算法就是求解問題的步驟(指令)。由於計算機的程序是一條指令接一條指令(步驟)地進行,它實際上就是算法的逐步展開。因此,算法瀰漫在所有的軟件程序裏,堪稱計算機的靈魂。

理解靈魂當然不是件容易的事情。除了高度抽象外,算法背後的邏輯也非常纏繞。在看過一個問題的算法解答後,人們感到的不一定是輕鬆,反而可能是困惑。這些算法是如何被發現或發明的呢?這些問題的解答者是如何想到特定的算法呢?在某些情況下,人們還不一定問得出這樣的問題,因爲對算法本身可能還沒有看懂。其實,深刻掌握算法的人並不多見。儘管很多人會在各種場合指點江山、激揚文字,儼然一副大師的架勢,但他們對算法的理解可能十分膚淺。很多經常需要使用算法的人,也不過停留在自發,而不是自覺的階段:對於見過的問題或者與見過的問題類似的問題知道如何作答,而對於那些與見過的問題沒有相似性或相似性很少或相似性不容易看出的新問題就一籌莫展了。

而各種算法書籍中的內容堆積、枯燥陳述、邏輯凌亂甚至理解錯誤等現象則加劇了人們對算法的畏懼。

市場上的算法書籍琳琅滿目,但存在共同的問題:講述一大堆問題,羅列諸多算法,但從根本上卻是就事論事;各種算法設計或分析戰略之間沒有什麼邏輯遞進或層次關係,算法各種戰略的順序在安排上非常隨意,與它們之間存在的因果關聯不相符合。比如,這些書籍在講動態規劃、靜態規劃、貪婪選擇、近似算法等時,沒有考慮到不同戰略之間的邏輯遞進關係,只是隨意安排章節,用一些具體問題來講解這些戰略而已。結果就是沒有邏輯主線貫通,讀起來費力、分散,不能形成有機的整體。看這些書的唯一收穫是獲得各種具體問題的解答,但在見到一個新問題時,對到底應該使用何種設計戰略和分析戰略,或者應該以何種順序來嘗試各種戰略卻模糊不清甚至不得章法。此外,這些算法書對算法戰略並沒有進行提煉,而是凌亂地分散在各種問題的具體解答中,形不成一種高度,形散神更散,無法系統性地訓練讀者的算法思維。

這些書作爲工具書查閱倒也可以,但作爲訓練算法思維的讀物或者教材顯然力有不敵。

那麼如何培養算法思維呢?答案就是算法背後的邏輯。不同的算法戰略看似不同,實則一脈相承,甚至從更高的層次上看就是同一種思維。所有的算法戰略如分而治之、動態規劃、貪婪選擇、隨機化、近似算法等只不過是同一思維的不同方面而已!它們之間存在邏輯和效率的遞進關係。明白了這一點,對算法的把握就會達到一個新的境界。我們用衆所周知的最小生成樹問題爲例來加以說明。

請按回車鍵進行升級! 1.修正更新系統補丁導致歡迎首頁出錯問題。 2.優化磨皮祛痘,新增智能磨皮。 3.新增一鍵發送至騰訊微博。

圖1 最小生成樹問題,圖中粗線組成一棵最小生成樹

最小生成樹問題的定義如下。

給定輸入爲:帶權重的連通無向圖,每條邊的權重爲。

要求輸出:一棵連接所有節點的樹,並且爲最小。

例如,圖1裏面的粗線條組成了該圖的一棵最小生成樹。

我們是如何獲得這棵最小生成樹的呢?或者最小生成樹問題該用什麼方法來解決呢?

最簡單的辦法當然是暴力戰略,即將所有的生成樹找出來,計算它們的權重,取出最小的樹即可。但此種戰略的成本高昂,其數量級爲,這裏代表圖中的邊的條數,代表圖的節點數量。而這是一個難以令人興奮起來的階乘級。顯然,我們需要對算法進行改進。那麼如何改進呢?

在面臨複雜問題時,人類通常選擇將問題簡化,即將複雜的大問題分解爲簡單的小問題。在解決了小問題後,再將小問題的解合併爲大問題的解。這就是所謂的“分而治之”。由於小問題比大問題更易解決,分治就成了上策,並演化爲算法設計的基礎戰略。對最小生成樹問題來說,就是將整個圖分解爲兩(也可以是其他數量)個尺寸(節點數)相等或相近的子圖,分別在這兩個子圖上尋找最小生成樹,然後將尋找出的兩個最小生成樹合併起來即可。顯然,分解的成本爲線性,但合併的成本是,這樣我們得到分治算法的成本遞歸式爲。按照大師解法,該遞歸式的解爲。

這是最優解法嗎?仔細分析上述分治算法的成本構成可以發現,在分解個子問題時會出現很多重複的下級子問題,重複解決這些相同的子問題顯然不是明智之舉。改進的辦法就是將重複的子問題解決一次,然後將結果存起來供以後使用,這就是動態規劃戰略。採用此種戰略後,最保守也可以將成本降低至(實際上,在略爲優化後可將成本降低到)。

但這是最優解法嗎?其實還不是。仔細分析可以發現,由於構建的是最小生成樹,我們可以依次選擇圖裏面最小的邊作爲最小成樹的邊,條件是新選擇的邊不與已經選擇的邊構成環路,直到有條邊入選爲止。而這正是貪婪選擇戰略。由於將所有的邊排序的時間複雜性爲,檢查一條邊被選中後是否與前面選擇的邊形成環路的時間複雜性爲線性,因此整個算法的時間成本爲。如果,此算法的效率將高於前面討論的標準分治戰略的效率。如果再使用改進的數據結構來支持貪婪選擇戰略,則該時間複雜性可以降低到(使用斐波拉契堆的攤銷時間)。

但貪婪選擇戰略還不是最優戰略。仔細分析上面最小生成樹的構造算法,我們發現它的成本在於邊的選擇(排序是爲選擇做的準備),而構造最小生成樹本身的成本只有。我們爲什麼要花多於構造本身成本的成本來構造最小生成樹呢?假如我們知道哪些邊屬於一棵最小生成樹,則構造起來就不費力氣了。但要想知道哪些邊屬於最小生成樹,不是需要與其他邊進行比對嗎?也許我們並不需要。我們可以用一個隨機數來告訴我們一條邊是否屬於最小生成樹。準確地說,我們用拋硬幣來決定一條邊是否應該被納入到最小生成樹裏。這樣就可以將時間成本降低到線性。這就是隨機化戰略。

表1 算法戰略遞進中的最小生成樹構建成本

表1 算法戰略遞進中的最小生成樹構建成本

這樣,隨着算法戰略的遞進,尋找最小生成樹的成本不斷降低,且在隨機化戰略達到線性的最低點(表1)!

不過,細心的讀者可能會產生諸多問題。例如,貪婪選擇戰略下,爲什麼會想到使用斐波拉契堆呢?斐波拉契堆的攤銷分析結果是如何得出的?在隨機化戰略下,如何保證所選的邊確實是最小生成樹的邊呢?另外,表1中的貪婪選擇戰略似乎與隨機化戰略不相上下:和不是一個數量級嗎(通常是大於的)?怎麼說隨着算法戰略的遞進,成本不斷降低呢?

在貪婪選擇戰略下,我們每次選擇權重最小的邊加入到一棵初始爲空的最小生成樹裏,被加入的邊不能與已加入的邊形成環路。在這種戰略下,算法的最大成本在每次選擇最小的邊上。而堆是支持此種操作的最佳數據結構。但對普通的二叉堆來說,每次刪除堆頂(我們當然使用最小堆)元素時,需要對堆進行調整以保持堆的屬性,從而爲後續的取最小邊操作打下基礎。由於此種調整的時間爲對數級,使得整個最小生成樹的操作成本爲。但如果我們改變思維,取消二叉堆要求每個節點度數不超過2的限制,則調整堆的操作成本將降低到常數級。在這種情況下,每次刪除堆頂元素後,直接將元素較大的子堆掛在元素較小的子堆下即可。這樣,最小生成樹的選邊操作的總成本似乎爲。加上降距操作的成本,整個最小生成樹的成本就是。

等等,這似乎有一個問題:前面所述的堆調整操作爲常數級的前提是刪除堆頂元素後出現的子堆個數爲常數。否則,在一大堆的子堆中間選出最小的元素(從而將別的子堆掛在其下)則可能不是常數時間。因此,問題的關鍵在於確保刪除堆頂元素所產生的子堆個數爲常數或者有限。而這種思路就導致了斐波拉契堆的出現。事實上,斐波拉契堆比這更進一步:合併操作也不是馬上進行,而是留下這些子堆,在子堆數超出一定的限制時才進行合併操作。此外,出現度數相同的堆(堆頂元素具有同樣子節點數)時也進行合併操作:將度數相同的堆合併成新的堆。由此,我們可以得出下面的斐波拉契堆的定義。

  • 斐波拉契堆由一組普通的堆(非二叉堆)構成。
  • 度爲k的節點的任意一棵子樹的規模最大爲(每個節點的子節點數不能超過)。
  • 所有子堆的度數都不相同。

圖2給出的是一個斐波拉契堆。

圖2 斐波拉契堆結構示意圖

圖2 斐波拉契堆結構示意圖

在斐波拉契堆下,刪除一個堆頂所產生的子堆個數不會超過。因此,在留下來的子堆裏面尋找最小元素的時間成本也不會超過。但這樣似乎得不到我們想要的的時間。不過,仔細分析發現,在斐波拉契堆的限制下,不可能每個節點的子節點數都是。事實上,絕大部分節點的子節點數都很少。這樣少數幾個節點的高度數導致的高成本可以攤薄到大量的低度數節點的低操作成本上,從而將整個最小生成樹的操作成本降低到每次選邊操作爲常數成本的境界。這就是攤銷分析的中心思想。

對於隨機化戰略的最小生成樹算法,不會因爲邊的選擇是隨機的,這條邊就一定屬於某棵最小生成樹。因此,我們在隨機挑選邊的時候也需要進行某種測試,以衡量此邊是否合適。而爲了進行此種衡量,我們需要對邊進行某種劃分,但這種劃分和測試本身必須在線性級別上。如何做到這一點呢?鑑於篇幅限制,有興趣的讀者請參閱《算法之道:從無有到無窮》。

對於數量級和的比較,在稠密圖的情況下,它們確實是一個數量級。但如果圖是稀疏的或者和是一個數量級,則歸結爲,歸結爲。如果節點數很多,將顯著高於。而且,貪婪選擇戰略的時間成本是在使用複雜的斐波拉契堆結構情況下,而且是攤銷時間!因此,從多方面考慮,隨機化戰略優於貪婪選擇戰略。

由本文可見,對最小生成樹問題的反覆推敲可以串起算法裏面的全部設計戰略。當這些戰略被串起時,我們所看到的就不僅僅是獨立分散的個體戰略,而是一條平時所不見的算法之道!雖然若隱若現,但對慧眼來說,它確確實實存在。這就是“形而之上謂之道”的算法境界。

作者鄒恆明,美國密歇根大學博士。曾任職於美國IBM、國家數據公司、朗訊、EMC公司。目前執教於上海交通大學,著有《算法之道》、《計算機的心智:操作系統之哲學原理》、《有備無患:信息系統之災難應對》


發佈了33 篇原創文章 · 獲贊 2 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章