從無有 到無窮——演算法之道(讓你學不會演算法都難)

hzbook2008發表於2010-01-29

演算法之道演算法之道(讓你學不會演算法都難)
作者:鄒恆明
定價:39.00元
出版社:機械工業出版
ISBN:978-7-111-29494-8
互動網預訂地址:http://www.china-pub.com/196344
豆瓣討論:http://www.douban.com/subject/4249686/

 

【內容簡介】本書追求的目標是演算法背後的邏輯,是一本啟示書,而不是一本包羅永珍的演算法大全。因此,本書甄選了那些最能夠展現演算法思想、戰略和精華,並能夠有效訓練演算法思維的內容。本書將演算法的討論分為五大部分:演算法基礎篇、演算法設計篇、演算法分析篇、經典演算法篇、難解與無解篇。每一個部分分別討論演算法的一大方面:基礎、設計、分析、經典和難解問題。本書既可以作為大學本科或研究生的演算法教材或參考書,也可以作為對演算法有興趣的讀者提升認知深度的讀物。

前言

起初神創造天地。地是空虛混沌,淵面黑暗;神的靈執行在水面上。神說:“要有光”。就有了光。神看光是好的,就把光與暗分開了。神稱光為晝,暗為夜。有晚上,有早晨,這是頭一日。

......

神就照著自己的形象造人,

......

神說:“看哪!我將遍地上一切結種子的菜蔬,和一切樹上所結有核的果子,全賜給你們作食物。至於地上的走獸和空中的飛鳥,以及各樣爬在地上有生命的物,我將青草賜給它們作食物”。事就這樣成了。

神看著一切所造的都甚好。有晚上,有早晨,是第6日。天地萬物都造齊了。

圖1  米開朗基羅創作的西斯廷教堂穹頂畫《創世紀》。這幅畫裡隱含著演算法

6天 

聖經上寫著:神6天創造天地萬有,第7日安歇。

對於神創論者來說,這是不可懷疑的事實。但對於進化論者來說,6天創造一切根本就不可能。

作為一本演算法書,我們當然不打算加入到神創論者和進化論者的永無休止的爭論當中去。我們關心的是這麼一個問題:聖經上為什麼給出的是6天,而不是其他的時間長度。不管是神創論者還是進化論者,弄清楚6這個數字的來歷很可能會對己方的觀點有所幫助。在這6天裡,神將他的創作方程式重複了6次,每天1次。對於全能的神來說,他完全可以在1天、1秒或者任何他所願意的時間長度裡創造天地萬物,但卻為什麼是不多不少的6天呢?而不管聖經上的 “1天”是多長,這個問題都是值得討論的。

我們知道,任何一個自然數的約數中都有1和它本身,而所有小於它本身的因數叫做這個自然數的真約數。例如,6的所有真約數是1、2、3;8的真約數是1、2、4。如果一個數的真約數之和等於這個自然數本身,則這個自然數就稱為完全數,或者完美數。例如,6 = 1+2+3,因此6是完美數;而8 ( 1+2+4,因此8不是完美數。因此,神6天創造世界,暗示著該創造是完美的!

以完美數來昭示創造的完美,似乎合情合理。但問題是,完美數只有6這一個數嗎?如果不是,為什麼不使用其他的完美數呢?答案是,完美數雖然不止有6這一個,但確實數量稀少。一直到現在(2009年6月),數學家們探索了2600年,並且現代數學家們還藉助了超級計算機,但也僅僅找到了47個完美數。其中第1個完美數是6,接下來的4個完美數分別是: 28、496、8128、33 550 336。而第47個完美數有25 956 377個數位,(注意,是數位,不是數值!)它的數值為:243 112 608 × (243 112 609 ? 1)。

完美數的稀少昭示著達到完美的難度,而神選擇6天來創造天地萬有也許是因為6是最小的完美數,即創造天地萬有對於神來說是輕而易舉的一件事情…

完美與演算法

完美數由於其各種神祕屬性(真約數之和等於自身只是其中的一個性質)而受到了特殊的關注。但到底哪些數是完美數則不是一件容易判斷的事情。顯然,按照完美數的定義,判斷一個數是否是完美數的不二法則是找出它的所有真約數,然後求和看看其是否等於自身。然而這種方法效率太過低下,因為這意味著因式分解,而這是十分困難的(本書後面將會討論到這個問題)。

如果判斷一個數是否是完美數就已經非常困難,那麼要找出所有的完美數則更是一個難上加難的任務。因為這就意味著將所有的數進行上面描述的判斷驗證:因式分解。這似乎是人類不可能完成的任務。即使用世界上超大的計算機來進行計算,情況也不會有任何數量級的改善。

顯然,我們需要新的解決方案,而不是發明或使用新的計算工具!研究這樣的問題就可以歸結到演算法的範疇裡,因為如何高效地解決問題正是演算法要研究的核心課題。

有意思的是,判斷和搜尋完美數是演算法的研究範疇,而演算法本身的追求卻也是“完美”(見圖2)。

圖2  演算法所追求的理想就是完美

演算法無處不在

如果你覺得演算法只是用來研究解決找出完美數之類的“漫無邊際的問題”,那就大錯特錯了。

也許演算法這個名詞聽上去很抽象,讓人聯想不到任何具體的物體。也許你會覺得演算法與自己的生活並無太多關係,它只不過存在於那些閒得無聊的數學家或計算機專業人士的腦海中。

但事實真是這樣嗎?當然不是。如果我們告訴你演算法就是解決各種問題的方法,你就不會覺得它太抽象,與生活無關了吧。事實是,演算法無處不在。每個人每天都在使用不同的演算法來活出自己的人生,比如你去食堂買飯會選擇一個較短的佇列,而有人則可能選擇一個推進速度更快的佇列。每天起床後,你可能先讀一會兒書,再去吃早飯;另外一個人則可能先去吃早飯,然後讀書。所有這些行為都是演算法或演算法一部分的體現。也許執行這些演算法並不在你的思想意識裡,也許你並不知道演算法在幫助著自己的生活,但它確實是存在的。這些演算法也許沒有經過精心設計,沒有經過仔細分析,但它還是演算法!

2009年7月23日下午,我在遊覽雲南省大理市的蝴蝶泉時由於泉水邊的石頭很滑,在用泉水洗手時(導遊金花說用該泉水洗手會帶來好運)不慎滑落到蝴蝶泉水(見圖3)裡面,全身溼透。(據說一天至多隻會有一個人滑落到泉裡,可見本人運氣不錯!看來“蝴蝶泉邊好梳妝”的歌詞也許應該改為“蝴蝶泉裡好沖涼”。)泉水冰冷透涼,而大理的氣溫又低。這樣,我就面臨一個是否更換全身衣服的決定。問題是,旅遊團需要馬上趕去登遊船遊覽洱海風光,而若找地方或者回旅店換衣服就將趕不上游船。

如何處理這件事情就是一個演算法問題:是先上游船再在船上找地方換衣服,還是找個地方換衣服而放棄遊覽蒼山洱海。顯然不同的演算法有著不同的收益和代價。如果能夠在遊船上找到合適的地方更換衣服,則採用先上游船再換衣服的演算法為佳;否則就是放棄遊覽的演算法更好,因為如果凍病了顯然就不划算了。最後,我選擇了在遊船上更換衣服的演算法:在遊船上找到了一個貴賓室更衣。

圖3  在蝴蝶泉水下洗個手也會涉及演算法

演算法由問題驅動

演算法的發現總是由相關的問題驅動的。拿排序來說,因為生活中到處都充滿次序,每個人都要接受自己在某個次序裡的位置。比如,各種排名、評優、民意調查等,最後的結果都體現為一個次序!看來,“沒有次序無以成方圓”並不是空穴來風!而談到排序用的方法,人們很自然地想到插入法,因為這種樸素的演算法和人的思維方式非常類似:它就是人們打牌時整理手中撲克牌的演算法。

但是隨著資料量的增大,插入排序的效率缺陷迅速變為人們無法容忍的缺點。於是人們發明了歸併排序、堆排序、快速排序等,這些排序的方法大大改善了速度,但是人們卻並不滿足於此。因此又發明了效率更高的線性排序。表1給出的是各種排序演算法平均情況下的效率比較:最上面一行的數字代表輸入的規模,如10表示一共有10個資料項,1M表示一共有100萬個資料項。其他格子裡面的資料為相應演算法在相應輸入規模下完成排序所需要的時間,單位為毫秒。所有輸入資料為隨機產生。
表1  部分排序演算法的時間效率比較              (單位:毫秒)
排序演算法 10 100 1K 10K 100K 1M
氣泡排序 0.000276 0.005643 0.545 61 8174 549 432
選擇排序 0.000237 0.006438 0.488 47 4717 478 694
插入排序 0.000258 0.008619 0.764 56 5145 515 621
雜湊排序/增量3 0.000522 0.003372 0.036 0.518 4.152 61
堆排序 0.00045 0.002991 0.041 0.531 6.506 79
歸併排序 0.000723 0.006225 0.066 0.561   5.48 70
快速排序 0.000291 0.003051 0.030 0.311 3.634 39
基數排序/進位制100 0.005181 0.021 0.165 1.65 11.428 117
基數排序/進位制1000 0.016134 0.026 0.139 1.264 8.394 89

注:

  1. 演算法執行環境為Intel 酷睿2雙核E8400,3.0GHz,Windows 7*64。
  2. 本表資料由作者所授“資料結構”課的胡嘉斌同學測試所得。

一個個新的演算法都是為了解決前面演算法遺留的問題而產生的。從表1裡的數字可以看出,一般來說,隨著新的演算法的出現,排序效率在不斷提高。不過,雖然每個演算法似乎解決了前面演算法的遺留問題,但新的問題也會被有意或無意地引入。例如,線性排序雖然將排序的時間複雜性降低到線性級,但各種前提條件極大地限制了其應用範圍。也許這就是演算法永遠也不能或不會停止發展的一個原因吧。

演算法是計算機的靈魂

因為人不是全能的,一個時刻只能做一件事情,因此做事情就要有一個步驟。由於演算法要滿足人的這種特性,它通常被表示為一個做事情的行為序列。因此,從一般意義上說,演算法就是求解問題的步驟。由於計算機的計算操作完全是一步一步地進行,因此演算法的上述性質用於計算機是再合適不過了,可以說演算法瀰漫在計算機的一切行為上。如果說作業系統是計算機的心智,那麼演算法就是計算機的靈魂。

理解靈魂當然不是件容易的事情,由於它高度抽象與簡潔,許多學生都望而卻步。我們先看一個紙牌魔術(見圖4):

  1. 任選一位觀眾將一副撲克牌充分洗好。
  2. 背對觀眾,請觀眾隨機抽出一張牌,記住牌面,然後將這張牌放回整副牌的最上面。
  3. 接過牌後,洗牌幾次。洗的時候保持最上面一張牌不動。
  4. 對觀眾說:“我來教你魔法,只要吹一口氣,就能把剛才你抽的牌吹到任意位置上”。
  5. 請觀眾說出一個數字,比如說10,然後一邊吹氣,一邊想著剛才說的數字10。
  6. 在吹完氣後,請觀眾一張一張地將上面的牌取出放在桌上。
  7. 到第10張時,將牌翻開,發現並不是其原來抽的牌。
  8. 接回整副牌,並把上一個步驟裡取出堆放在桌上的牌收起,仍放在整副牌的最上面。
  9. 然後洗牌幾次,洗的時候保持上面放回來的那堆牌不動。
  10. 從觀眾手上拿回剛才翻開的那張牌,插入到最上面9個位置中的任意一個。
  11. 對觀眾說:“你剛才不是在想著那個數字的時候吹的氣,而是在吹氣的時候想著那個數字,而這是完全不同的兩回事。我現在演示一下如何吹氣”。對著牌吹一口氣。
  12. 請觀眾從上到下數牌,到第10張時翻開。
  13. 這張翻開的牌就是觀眾一開始抽的那張牌。

圖4  演算法無處不在,就連紙牌魔術都有其背後支援的演算法

讀者看明白了上面的這個魔術了嗎?這裡面隱藏著一個演算法。如果看懂了就可以在朋友面前一顯身手了。當然,如果沒有看懂也沒有什麼關係。演算法本來就不是輕易讓人看懂的嘛。

對於一些吹毛求疵的人來說,也許會說這個紙牌魔術不是演算法。至少這跟我們研究演算法的人所打交道的常見演算法不太一樣。這沒有什麼關係,來看下面的一段虛擬碼:
PARTITION(A,p,q)   // A是一個實數陣列, p, q是該陣列的上下限
x←A[p]      // A[p]被選中
i←p
for j←p+1 to q do
   If A[j]≤ x then
  i←i+1
  A[i]?A[j]   // 交換A[i]和A[j]的內容
A[p]?A[i]
return i

讀者能看出來這個虛擬碼程式片段完成的是什麼功能嗎?

要分析一個演算法,似乎就更難了。讀者能看出下面的C程式片段裡面“laugh++”語句執行多少次嗎?
for (i=1; i<=n; i*=2)
       for (j=1; j<=i; j++)
            laugh++;

如果這些問題讀者都能回答,那恭喜你。看來演算法分析對於你來說將是很容易的事情,不過可能也不一定。如果你回答不出這些問題,不用擔心,因為回答諸如此類的問題就是本書的目的。當然了,本書回答的遠不止這麼幾個簡單問題,而是會闡述更重要的演算法精髓:演算法思想、戰略和分析!

本書內容安排

本書追求的目標是演算法背後的邏輯。所以,它不可能是一本包羅永珍的演算法大全,而是一本啟示書。因此,本書甄選了那些最能夠展現演算法思想、戰略和精華,並能夠有效訓練演算法思維的內容。本書的選材遵循的規則是:書中選取的每個演算法都在某個方面具有獨特性,能夠彰顯演算法的精髓。

本書將演算法的討論分為五大部分:演算法基礎篇、演算法設計篇、演算法分析篇、經典演算法篇、難解與無解篇。每一個部分分別討論演算法的一大方面:基礎、設計、分析、經典和難解問題。如圖5所示。

圖5  本書內容框架

本書第一部分是演算法基礎篇,討論基本的演算法設計思想與演算法分析方法和手段。內容包括從無有到無窮、計數與漸近、分治與遞迴三章。第1章討論意念與現實、什麼是演算法、演算法的表示、演算法之魂、演算法與計算機的關係、演算法的範疇和為什麼學習演算法。第2章討論演算法的正確性、時空效率和時空特性分析、計數分析方法、演算法設計、漸近表示與分析。第3章闡述演算法設計的最基本戰略:分治與遞迴。具體內容包括分而治之為上策、分治策略中的遞迴、求解遞迴表示式、乘法及乘方運算、矩陣乘法、斐波那契數的計算、VLSI 佈線和多項式乘法。

第二部分為演算法設計篇,討論演算法中常見且重要的戰略或思想,內容包括動態規劃思想、貪婪選擇思想和隨機化思想三章。第4章討論什麼是動態規劃、流水裝配線問題、最長共用之序列問題、最優二叉搜尋樹問題、記憶遞迴法、最優子結構、重疊子問題、動態規劃與靜態規劃之間的關係。第5章介紹人在生活中潛意識地遵守的一種行為:貪婪。具體內容包括什麼是貪婪、貪婪選擇屬性、揹包問題、教室規劃(排課)問題、最小生成樹問題、霍夫曼編碼問題、標準分治、動態規劃和貪婪策略的比較。第6章討論人生及演算法中無處不在的隨機,內容包括為什麼要隨機化、隨機的平方,拉斯維加斯演算法、蒙特卡羅演算法、素性測試、矩陣乘積驗證器、隨機化最小生成樹演算法等。

第三部分為演算法分析篇,主要介紹計數(漸近)分析之上的演算法分析的另外三種重要手段:概率分析、攤銷分析和競爭分析。第7章包括一切都在概率中、什麼是概率分析、夢幻情人的代價、概率分析結果的有效性、正確概率分析的保障、夢幻情人的概率、隨機排列問題和從無窮到無有。第8章討論什麼是攤銷分析、攤銷分析與資料結構、攤銷分析的方法、聚類分析、會計分析、勢能分析、動態表擴張及其攤銷。第9章討論競爭無處不在、線上演算法和離線演算法、競爭力、健忘對手和優良對手、線性表更新問題、前置移動演算法及其競爭分析、聚類問題及其競爭分析、競爭分析與普通演算法分析的比較。

第四部分是經典演算法篇,內容包括排序與次序、搜尋與雜湊、最短路徑問題三章。第10章內容包括插入排序、折半插入排序、歸併排序、快速排序、隨機化快速排序、排序下限、線性排序、求最大值、求最小值、求中值及任意次序選擇。第11章內容包括搜尋問題定義、順序搜素、折半搜尋、常數搜尋、雜湊搜尋、雜湊函式選擇、雜湊衝突的解決、隨機化雜湊、全域雜湊和完美雜湊。第12章內容包括單源單點最短路徑、單源多點最短路徑、多源多點最短路徑。具體演算法則包括窮舉搜尋演算法、Dijkstra演算法、Bellman-Ford演算法、Floyd-Warshall演算法和Johnson演算法。

最後一部分是難解與無解篇,內容包括可解與不可解、NP完全問題和無解與近似三章。第13章討論易解與難解、決策問題和優化問題、P和NP、確定性與非確定性。第14章討論NP完全問題、多項式時間規約、如何證明一個問題是NP完全、庫克定理、3-SAT問題、整數規劃問題、獨立集問題、漢密爾頓迴路問題、弱NP完全、強NP完全和中NP完全。第15章內容包括難解問題、不可決定問題、程式終結的判斷、難解之題的求解、智慧窮舉、近似演算法、本地搜尋、回溯策略、分支限界、貪婪近似策略、啟發式搜尋和模擬淬火演算法。

本書以創世紀的6天為起點,寓意演算法也有創始的一刻,將人類演算法體系中優美而有代表性的內容囊括書中,最後以演算法之道的隨想作為結尾,構成了邏輯上一氣呵成,思想上韻味深長的演算法知識體系。

本書的特點

我相信,寫書的目的是對讀者有所啟示,而不是用一大堆的公式或繁瑣的推導來煩死或嚇倒讀者。雖然在一定的時候我們也會迫不得已地使用複雜的公式,但不必到處都引用複雜繁瑣的表述,甚至將簡單的東西也以繁瑣的式子來表達。複雜的式子或許能給部分人帶來一絲莫名其妙的快感,但對於大多數讀者來說,也許就是“裝腔作勢罷了,真可憎惡”。基於此種認識,本書將以簡潔的方式來表示複雜的概念。

在我讀小學的時候,有一天在街上聽到一個人吆喝“快來看,快來看開膛破肚表演!”有人問:“怎麼開膛破肚?”賣藝的人說:“用刀將活人的胸膛破開,將裡面的內臟拿出來給大家看,然後再縫上。”開膛破肚?這可是難得一見的事情。於是我按照藝人的指引進入一個很小的黑屋裡。裡面已經擠進了一些人,站在屋子四周。屋中間的床架上躺著一個上身赤裸的年輕人,旁邊則站著一個手拿尖刀的“屠夫”。“屠夫”先叫每個觀眾交了一筆錢,然後開始了他的開膛破肚表演。

只見“屠夫”將尖刀舉起,對著躺在床架上的年輕人喊道:“你要錢還是要命?” 年輕人很堅定地回答:“要錢!”“屠夫”連喊了三遍,得到的回答都是“要錢”。於是“屠夫”將尖刀快速砍下,刺進了年輕人的肚臍,血從尖刀的四周往外滲出。然後“屠夫”對著四周的觀眾喊道:“你們看這個人要錢不要命,你們給他一些錢吧。”很多人看到這個流血的場面,出於同情或害怕又向外掏錢。

令人遺憾的是,“屠夫”並沒有遵守許諾將年輕人的腹部或者胸膛劃開,也沒有將臟器拿出來給大家看……

這是江湖雜耍的一些小伎倆,但本書對演算法進行的“開膛破肚”的分析卻是實打實的!這也是本書最顯著的特點:對演算法的分析深入到以往沒有深入的境界。本書更關注的是演算法後面的邏輯脈絡,強調一個演算法為什麼會出現,又為什麼會是現在呈現的樣子。通過挖掘演算法背後的思維過程,本書淋漓盡致地展現了演算法的精妙絕倫。

本書的第二個特點是結構緊湊:摒棄臃腫繁瑣的內容堆砌,通過邏輯關係將演算法各部分內容進行遞進演繹,形成一個層次感強、由表入裡的有機體。

本書的第三個特點是全新的角度:也許講的是同樣的演算法、相似的問題,但是本書採納的是完全不同的角度,這種獨特的視角能把我們對演算法的理解帶到新的高度。

本書的第四個特點是新穎的結構:不同一般的章節組織,使條理更為清晰,內容上也包含了不少新的概念和理念。

本書的最後一個特點是寫作風格輕鬆活潑:以講故事的形式將概念和演算法的精華娓娓道來,由淺入深,非常易於理解和消化。

上述這些特點賦予了本書與一般演算法書或其他科技書籍的巨大不同(見圖6)。

當然,本書也沒有走另一個極端:過分強調語言的生動而忽視了嚴謹性。恰恰相反,本書力求兼顧這兩個看似矛盾的方面。在書中我們看不到繁多的數學公式,取而代之的是精確的文字敘述。我認為,這種用嚴謹的語言代替數學形式化的方法更容易被讀者接受。因為讀者需要知道的,通常是蘊涵在各種公式或演算法虛擬碼(或程式程式碼)背後的思想,而正是這些思想促成了所有精巧的演算法。
本書幾乎沒有講述資料結構的內容,也不會討論演算法的程式設計實現,而是集中精力論述演算法本身的特點。雖然有的人認為演算法與資料結構和程式設計關係密切,但畢竟演算法可以獨立於它們而單獨存在。事實上,早在計算機出現之前,演算法就已經存在了。從相對的角度來看,演算法是抽象的,資料結構與程式設計是具體的(當然從絕對意義上看,資料結構與程式設計也是很抽象的)。而越抽象就越具有普遍意義,也只有在比較抽象的層面上學習演算法,才能看穿演算法的精妙。

圖6  本書的特點

當然,本書的討論也不可能完全脫離資料結構或程式,有時候也會提到它們,有時候甚至直接給出某個演算法的具體程式實現片段,但所有對資料結構和程式的論及皆點到為止,以有利於對演算法的掌握和體現演算法實現為目的。而資料結構和程式設計的精妙講解就留給資料結構和程式設計的課程來完成吧。

本書的使用方法

本書既可以作為大學本科或研究生的演算法教材或參考書,也可以作為對演算法有興趣的讀者提升認知深度的讀物。如果作為教材使用,建議課程為4個學分,如果學時限制只有3個學分,建議將攤銷分析、競爭分析和無解與近似這三章內容跳過。建議課堂講解順序按書中安排進行,因為本書內容是按照邏輯演繹順序環環相扣的。按這種順序講解條理清晰、邏輯明朗、前後連貫,學生比較容易接受。

對於一般讀者,本書可以作為一種演算法思維的修養書來看,讀者應當可以按照自己的時間和計劃自行安排。或者休閒時翻看此書,斟酌演算法,品味精妙,這不也是一件美事嗎?

本書隱含7個悖論。如果讀者能夠發現這些悖論並找出答案,那恭喜你!因為你有一雙發現真理的眼睛。不,應該說,你有一顆發現真理的心!如果讀者沒有發現這些悖論,也沒關係,因為能夠發現這些悖論的人畢竟不多,更不用說尋找到答案。而不管發現與否,這些悖論都不會妨礙讀者對本書內容的理解。因為如果你發現了這些悖論,它們施加的是正面影響:讀者對演算法的理解深度會大大增加!而如果沒有發現這些悖論,則對讀者來說,這些悖論相當於不存在,自然也就無法對讀者施加任何正面或負面的影響了。

另外,本書章節之間以隱示的方式留下了7個重要的演算法奧祕,但能否察覺就看個人的理解了(見圖7)。

圖7  本書隱含著7個悖論和7個奧祕

邏輯演繹、生活歸納、趣味交織,入木三分地揭示演算法的奧妙;新的角度、新的分析、新的境界,耳目一新地闡述演算法的精華。就讓我們即刻開始精彩紛呈的演算法之旅,Let the Fun Begin!

鄒恆明

2009年9月於上海莘莊

 

 

目錄

前言

第一篇  演算法基礎篇
第1章  從無有到無窮 2
1.1  意念與現實 3
1.2  什麼是演算法 4
1.3  演算法的表示 6
1.4  演算法之魂 7
1.5  如何比較速度 8
1.6  演算法與計算機的關係 9
1.7  演算法的範疇 10
1.8  為什麼學習演算法 10
思考題 11
第2章  計數與漸近 12
2.1  演算法的分析 12
2.2  計數:演算法分析的核心 14
2.3  演算法設計 15
2.4  演算法效率表示 16
2.5  漸近分析 17
2.6  O、(、(表示 18
2.7  最好、最壞、平均 19
2.8  O、(、(的另一類定義 21
2.9  O、(、(的性質 22
2.10  要更快的計算機還是要更快的演算法 22
思考題 23
第3章  分治與遞迴 25
3.1  分而治之為上策 26
3.2  分治策略 28
3.3  遞迴表示式求解 29
3.4  分治策略舉例1:乘方運算 35
3.5  生命不能承受之重:矩陣乘法 36
3.6  魔鬼序列:斐波那契序列 38
3.7  VLSI 佈線 41
3.8  多項式乘法 43
3.9  分治就在潛意識深處 43
思考題 43

第二篇  演算法設計篇
第4章  動態規劃思想 46
4.1  什麼是動態規劃 47
4.2  流水裝配線問題 48
4.3  最長公共子序列 52
4.4  最長公共子序列變種 55
4.5  記憶遞迴法 55
4.6  空間效率改善 56
4.7  最優二叉搜尋樹 56
4.8  最優子結構與重疊子問題 62
4.9  動態規劃與靜態規劃的關係 63
4.10  動態規劃與靜態規劃的相互轉換 64
思考題 65
第5章  貪婪選擇思想 67
5.1  僅有動態規劃是不夠的 67
5.2  什麼是貪婪 68
5.3  揹包問題 68
5.4  貪婪選擇屬性 71
5.5  教室規劃問題 72
5.6  最小生成樹 76
5.7  Prim演算法 80
5.8  霍夫曼樹和霍夫曼編碼 83
5.9  貪婪選擇屬性 88
5.10  標準分治、動態規劃和貪婪選擇的比較 89
思考題 90
第6章  隨機化思想 92
6.1  為什麼要隨機化 93
6.2  隨機的平方 94
6.3  什麼是隨機化演算法 95
6.4  拉斯維加斯演算法 96
6.5  蒙特卡羅演算法 97
6.6  素性測試 97
6.7  矩陣乘積驗證器 100
6.8  隨機化最小生成樹演算法 102
6.9  隨機數的生成 105
6.10  隨機化演算法的應用 105
思考題 106

第三篇  演算法分析篇
第7章  概率分析 108
7.1  一切都在概率中 109
7.2  什麼是概率分析 109
7.3  夢幻情人的代價 110
7.4  概率分析結果的有效性 114
7.5  正確概率分析的保障 115
7.6  夢幻情人的概率 115
7.7  隨機排列問題 117
7.8  南柯一夢:從無窮到無有 119
7.9  概率分析的其他應用 120
思考題 121
第8章  攤銷分析 122
8.1  什麼是攤銷分析 123
8.2  攤銷分析與資料結構 124
8.3  攤銷分析的幾種方法 124
8.4  聚類分析 125
8.5  會計分析 128
8.6  勢能分析 130
8.7  攤銷分析應用:表格擴充套件的代價 131
8.8  運氣不好就攤銷 137
思考題 138
第9章  競爭分析 139
9.1  什麼是競爭分析 139
9.2  線上演算法和離線演算法 141
9.3  競爭力 142
9.4  健忘對手和優良對手 142
9.5  線性表更新問題 143
9.6  前置移動演算法的競爭分析 145
9.7  聚類問題 147
9.8  競爭分析與普通演算法分析 149
思考題 149
第四篇  經典演算法篇
第10章  排序和次序 152
10.1  排序無處不在 152
10.2  插入排序 153
10.3  歸併排序 156
10.4  快速排序 158
10.5  隨機化快速排序 162
10.6  排序的下限 164
10.7  線性排序 165
10.8  計數排序 166
10.9  基數排序 168
10.10  桶排序 171
10.11  次序選擇 175
10.12  快速次序選擇演算法 176
10.13  隨機快速次序選擇演算法 178
10.14  最壞情況下的線性選擇演算法 179
思考題 181
第11章  搜尋與雜湊 183
11.1  搜尋問題 184
11.2  順序搜尋 184
11.3  折半搜尋 185
11.4  常數搜尋 186
11.5  雜湊搜尋 187
11.6  雜湊函式選擇 189
11.7  雜湊演算法的碰撞問題 193
11.8  雜湊表元素刪除 201
11.9  隨機化雜湊 202
11.10  全域雜湊 203
11.11  全域雜湊構造 204
11.12  完美雜湊 206
思考題 208
第12章  最短路徑 211
12.1  劍指羅馬 211
12.2  最短路徑問題 213
12.3  單源單點最短路徑問題 215
12.4  單源多點最短路徑問題 218
12.4.1  最短路徑的性質 219
12.5  Bellman-Ford演算法 226
12.6  多源多點最短路徑問題 232
12.7  天意難違 240
思考題 240

第五篇  難解與無解篇
第13章  可解與不可解 244
13.1  我們戰無不勝嗎 245
13.2  易解與難解 245
13.3  決策問題和優化問題 246
13.4  決策問題 247
13.5  P類問題 247
13.6  NP類問題 248
13.7 (確定性)圖靈機 249
13.8  非確定性圖靈機 249
13.9  非確定性演算法 250
13.10  回到NP類問題 251
13.11  P和NP 252
13.12  搜尋問題、決策問題和優化問題 253
13.13  有沒有解和是否可決定 253
思考題 254
第14章  NP完全問題 256
14.1  玉龍雪山下的審判 256
14.2  NP完全問題的定義 257
14.3  NP完全的重要性 258
14.4  多項式時間規約 259
14.5  如何證明一個問題S是NP完全 259
14.6  第1個NP完全問題的證明 260
14.7  庫克定理 260
14.8  3-SAT問題 263
14.9  證明NP難的技巧 264
14.10  整數規劃 265
14.11  獨立集問題 266
14.12  漢密爾頓迴路問題 268
14.13  討論:弱NP完全、強NP完全和中NP完全 271
思考題 272
第15章  無解與近似 273
15.1  難解問題 274
15.2  不可決定問題 274
15.3  程式終結的判斷 275
15.4  難解之題的求解 276
15.5  智慧窮舉、近似演算法和本地搜尋 277
15.6  智慧窮舉之回溯策略 279
15.7  智慧窮舉之分支限界 280
15.8  貪婪近似策略 280
15.9  啟發式搜尋策略 281
15.10  模擬淬火演算法 282
思考題 286
結語  演算法之道 288
附錄  演算法隨想 290
參考文獻 293

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16502878/viewspace-626415/,如需轉載,請註明出處,否則將追究法律責任。

相關文章