算法設計常見的5種基本技巧,有貪婪算法、分治算法、動態規划、隨機化算法和回溯算法。
一.貪婪算法
雖然每次的選擇都是局部最優,當在算法結束的時候,其期望是全局最優才是正確的。不過有時,在不同條件與要求下時,最優解的答案可能不止有一個或不一樣,而貪婪算法也可以得出一個近似的答案。
1.多處理器作業調度
在多處理器的調度完成作業的問題中,表1-1中的作業可也按圖1-1和圖1-2的方式進行調度的到平均時間最小的最優答案。
作業 |
J1 |
J2 |
J3 |
J4 |
J5 |
J6 |
J7 |
J8 |
J9 |
時間 |
3 |
5 |
6 |
10 |
11 |
14 |
15 |
18 |
20 |
表1-1 作業與時間表
圖1-1 作業調度方式1 圖1-2 作業調度方式2
在這兩種不同的作業調度方式中,第一種調度方式使用貪婪算法,每個處理器開始按序首先調度處理作業時間要求最少的先進行處理,得到平均處理時間為18.33。第二種調度方式中,不改變作業調度的相對順序,而改變其在其他處理器中進行處理,得出的平均處理時間同樣為18.33。
然而,在第二種調度方式中,作業的最后完成時間為38,而第一種的最后完成時間為40。如果在考慮作業的最后完成時間的情況下,第一種調度方式所使用的貪婪算法則不算是得到最優的答案。
貪婪算法的另一項應用就時裝箱問題,運用貪婪算法,裝箱問題可以得到較快的解決速度,而得到的答案未必最優,但卻也近似最優。
2.哈夫曼算法
哈夫曼算法也是一種典型的運用貪婪算法的算法。
在森林中,選取兩棵權值最小的數形成新的樹,再在其中選取兩棵權值最小的數形成新的樹,直到森林中最后只剩一棵樹,即為權值最小的樹。哈夫曼的代碼實現如下,
圖1-3 哈夫曼樹算法代碼實現
若用優先隊列實現哈夫曼算法,可將其運行時間優化為O(NlogN)。
二.分治算法
分治算法不是簡單的遞歸,而是將大的問題遞歸解決較小的問題,然后從子問題的解構建原問題的解。比如,快速排序和歸並排序算分分治算法,而圖的遞歸深度搜索和二叉樹的遞歸遍歷則不是分治算法的運用。
分治算法運行時間的計算有一重要定理
分治算法有較廣泛的運用,很多規模較大的問題都可以用分治算法分解成獨立的小問題解決。
快速選擇算法是基於快速比較的一種分治算法的運用。對於規模較大的選擇問題,在快速選擇算法中用五元中值組取中值分割法使遞歸的子問題最多為原問題的規模的70%,以加快算法的效率,使運行時間為O(N)。
五元中值組取中值分割法即將N個元素分成每5個元素一組,運用8次比較找出每組的中值,得到約(N/5)個中值組,再對中值組調用分割的快速選擇算法,直至找到第k小的元素。
在位數較多的整數相乘中,使用分治算法對其的運算進行優化,可將其運行時間優化為亞二次方時間。同樣,在繁瑣的矩陣乘法中,分治算法可將其運行時間優化為亞立方時間。
三.動態規划
動態規划與分治算法的區別是,兩種算法同樣是將較大的問題分解成較小問題,而動態規划對這些較小的問題並不是對原問題明晰的分割,其中一部分是被重復求解的,因此動態規划將較小問題的解記錄下來,使得在處理較大問題的時候,可以不用重復去處理較小的問題,而是直接利用所記錄的較小問題的答案來求解。
動態規划的典型運用就是對斐波那契數T(N)=T(N-1)+T(N-2)的求解,斐波那契數的求解同樣可以運用分治算法,不過在其使分治算法中,其運行時間是隨着N呈指數增長的,這顯然會造成程序效率的低效。
而對斐波那契數使用動態規划求解的時候,計算T(N)只需將前面求解過的T(N-1)和T(N-2)進行相加即可,這種算法的實現也比較簡單,代碼可以實現如下,
圖3-1 使用動態規划設計的斐波那契數求解算法代碼
Floyd算法在求圖的每一個頂點到其他所有頂點的最短路徑中也應用了動態規划的設計技巧。雖然dijkstra算法通過迭代V次也可以以O(V^3)求出,但Floyd算法在求解稠密圖的時候更快,且能應對存在負邊值無圈的圖。
該算法在求解頂點i和j之間的最短路徑時,嘗試繞過其他頂點k來求最短路徑,並保留相應的結果,以備其他頂點在求解時使用,其代碼的實現也相對容易,設置兩個矩陣以保留最短路徑權值和路徑通路,實現如下
圖3-2 Floyd算法的實現
四.隨機化算法
隨機化算法的一個應用是在快速排序中對樞紐元素的選擇,使得算法的運行時間不止依賴於特定的輸入,還而且還依賴於所出現的隨機數。雖然一個隨機化算法的最壞情形運行時間常常與非隨機化算法的最壞情形運行時間相同,但兩者還是有區別的,好的隨機化算法沒有壞的輸入,而只有壞的隨機數。
即在非隨機化算法中,存在特定的輸入序列總使得算法產生最壞的O(N^2)運行時間。如果運用隨機化算法,且將該特定的序列輸入運行,該算法的每一次運行將會得到不同的運行時間。
隨機化算法的第二個應用是測試大數是否為素數,雖然該算法會出錯,但隨着測試次數的增多,那么錯誤的概率可以小到忽略不計。
五.回溯算法
回溯算法相當於窮舉搜索的巧妙實現,對比蠻力的窮舉搜索,回溯算法可以對一些不符合要求的或者是重復的情況進行裁剪,不再對其進行搜索,以減少搜索的工作量提高效率。比如,在圖運用回溯算法的深度優先搜索遍歷中,會對已搜索遍歷過的頂點進行標記,避免下次的回溯搜索中對再次出現的該頂點進行重復遍歷。
書中一例子是收費公路重建問題的應用。
對距離集合D,在一條路徑上重構出符合距離集合D的點集X。比如,對距離集合D={1,2,2,2,3,3,3,4,5,5,5,6,7,8,10}重構出點集X,而重建所的到的決策樹如下圖5-1,對於下x4=7的決策點,其下的決策都的得不到符合距離集合D的要求結果,因此需要回溯到x5=8的決策點,再對其下同下x4=7對等的決策點x2=3進行測試,以求最終結果。
圖5-1 收費公路重建距離集D對應的決策樹
重建完后,可以得到如下結果,
圖5-2 距離集合D對應的點集重建