1. 程式人生 > >《資料結構與演算法分析》學習筆記-第十章-演算法設計技巧

《資料結構與演算法分析》學習筆記-第十章-演算法設計技巧

[toc] *** ## 10.1 貪婪演算法 貪婪演算法分階段的工作,在每個階段,可以認為所做決定是最好的,而不考慮將來的後果。一般來說,這意味著選擇的是某個區域性的最優。當演算法終止時,我們希望區域性最優就是全域性最優。如果是這樣的話,那麼演算法就是正確的,否則,演算法得到的是一個次最優解。如果不要求絕對最佳答案,那麼有時用簡單的貪婪演算法生成近似答案,而不是使用一般來說產生準確答案所需要的複雜演算法。 ### 10.1.1 排程問題 ### 10.1.1.1 單處理器 設有作業j1,j2,j3,j4,其對應的執行時間分別為t1,t2,t3,t4.而處理器只有一個。為了把作業的平均完成的時間最小化,排程這些作業最優的順序是什麼。如果按照順序排程,那麼排程作業的平均時間為: ``` t1 t1+t2 t1+t2+t3 t1+t2+t3+t4 求和:4t1+3t2+2t3+t4 求平均排程時間:4t1+3t2+2t3+t4/4 ``` 顯而易見,如果希望平均排程時間最優,那麼就要優先做耗時較短的工作,因此作業系統排程程式一般把優先權賦予那些更短的作業。 ### 10.1.1.2 多處理器 讓最短的作業先執行,按照作業執行時間從短到長的順序,依次輪流讓不同的處理器進行處理 ``` CPU1 j1 j4 j7 CPU2 j2 j5 j8 CPU3 j3 j6 j9 ``` 之前的操作都是將平均排程時間最小化,如果想將最後完成時間最小化就不是很容易,即讓整個序列完成的時間更早。 ### 10.1.2 Huffman編碼 檔案壓縮中常見。ASCII碼中有100個左右可列印字元。那麼可以用log100個bit來表示。對於壓縮檔案中只使用了某些字元,那麼可以通過更少的Bit來表示。例如圖中向左分支是0,向右分支是1,那麼a為000,c為001,以此類推。 [![rICzsH.png](https://s3.ax1x.com/2020/12/27/rICzsH.png)](https://imgchr.com/i/rICzsH) [![rIPlF0.png](https://s3.ax1x.com/2020/12/27/rIPlF0.png)](https://imgchr.com/i/rIPlF0) 由於newline沒有右兄弟,因此上移,將樹變成滿樹 [![rIPdT1.png](https://s3.ax1x.com/2020/12/27/rIPdT1.png)](https://imgchr.com/i/rIPdT1) 滿樹:所有的節點,要麼是樹葉,要麼有兩個兒子。一種最優的編碼將總具有這個性質,否則就像上面,具有一個兒子的節點可以向上移動一層。如果字元都只放在==樹葉==上,那麼任何位元序列總能夠被毫無歧義的譯碼。並且,這些字元程式碼的長度是否不同並不要緊,只要沒有字元程式碼是別的字元程式碼的字首即可。這種編碼叫做==字首碼==。反之,如果一個字元放在非樹葉節點上,那就不能夠保證譯碼沒有二義性。可以想見,如果想要以最小的空間表示最多的字元,那麼就要將==出現頻率高的字元,放到儘可能淺的深度,而出現頻率低的字元,可以放到深的深度==。 [![rIijDH.png](https://s3.ax1x.com/2020/12/27/rIijDH.png)](https://imgchr.com/i/rIijDH) #### 哈夫曼演算法 假設字元的個數為C,哈夫曼演算法可以描述如下:演算法對一個由樹組成的森林進行。一棵樹的權等於它的樹葉的頻率的和。任意選取最小權的兩棵樹T1和T2,並任意形成以T1和T2為子樹的新樹,將這樣的過程進行C-1次。在演算法的開始,存在C棵單節點數。每個字元一棵。在演算法結束時得到一棵樹,這棵樹就是最優哈弗曼編碼樹。 [![ro3tEt.png](https://s3.ax1x.com/2020/12/28/ro3tEt.png)](https://imgchr.com/i/ro3tEt) [![ro3TbR.png](https://s3.ax1x.com/2020/12/28/ro3TbR.png)](https://imgchr.com/i/ro3TbR) 1. 初始階段,每個元素看成一棵單節點樹。每個節點有自己的value和權重。 2. 將當前森林中,權值最低的兩棵樹進行合併,合併後新樹的權值是老樹權值的和 3. 繼續進行第二步,不斷將當前森林中權值最低的兩棵樹進行合併,合併時,左右分支任意,可以互換 4. 可以看出,權值(出現頻率)越低,其深度越深;權值越高,其深度越淺。這樣就能保證總開銷最小。 該演算法是貪婪演算法的原因在於,在每一階段我們都進行一次合併而沒有進行全域性的考慮,我們只是選擇兩棵權值最小的樹進行合併。我們可以依權排序將這些樹儲存在一個優先佇列中。那麼對於元素個數不超過C的優先佇列將進行一次BuildHeap, 2C-2次DeleteMin和C-2次Insert,因此執行時間偉O(ClogC)。如果不使用優先佇列,而是連結串列的話,將給出一個O(C^2)的演算法。優先佇列實現方法的選擇取決於C有多大。 ### 10.1.3 近似裝箱問題 1. 聯機:必須解決當前問題,流程才能繼續 2. 離線:必須瞭解完所有的問題,流程才能開始 #### 10.1.3.1 聯機演算法 對於聯機裝箱問題不存在==最優演算法==。聯機演算法從不知道==輸入何時會結束==,因此它提供的效能保證必須在整個演算法的每一時刻成立。 - 定理:存在使得任意聯機裝箱演算法至少使用4/3最優箱子數的輸入 ##### 1. 下項適合演算法 當處理任何一項物品時,我們檢檢視它是否能裝進剛剛裝進物品的同一個箱子中去。如果能夠裝進去,那麼就把它放入該箱中。否則就開闢一個新箱子。該演算法能夠以線性時間執行。 - 令M是將一列物品I裝箱所需的最優裝箱數,則下項適合演算法所用箱數絕不超過2M個箱子。存在一些順序使得下項適合演算法用箱2M-2個 [![roab9I.png](https://s3.ax1x.com/2020/12/28/roab9I.png)](https://imgchr.com/i/roab9I) ##### 2. 首次適合演算法 雖然下項適合演算法有一個合理的效能保證。但是它的實踐效果卻很差,因為在不需要開闢新箱子的時候,它卻開闢了新的箱子。首次適合演算法的策略是==依序掃描這些箱子==,但把==新的一項物品==放入足夠盛下它的==第一個箱子中==。因此,只有當先前放置物品的結果已經沒有再容下當前物品餘地的時候,我們才開闢一個新的箱子。首次適合演算法保證其解最多包含最優裝箱數的二倍。當首次適合演算法對大量其大小均勻分佈在0和1之間的物品進行運算時,經驗結果指出,首次適合演算法用到大約比最優裝箱方法多2%的箱子,這是完全可以接受的 [![roBU54.png](https://s3.ax1x.com/2020/12/28/roBU54.png)](https://imgchr.com/i/roBU54) ##### 3. 最佳適合演算法 該法不是把一項新物品放入所發現的第一個能容納它的箱子,而是放到所有箱子中能容納它的==最滿==的箱子中。最佳適合演算法比起最優演算法,絕不會壞過1.7倍左右 [![ror64e.png](https://s3.ax1x.com/2020/12/28/ror64e.png)](https://imgchr.com/i/ror64e) #### 10.1.3.2 離線演算法 如果能夠觀察全部物品之後再算出答案,那麼應該會做的更好。所有聯機演算法的主要問題在於將大項物品裝箱困難,特別是當他們在輸入的晚期出現的時候。因此解決該問題的方法時將==各項物品排序==,將==最大的物品放在最先==。此時可以應用首次適合演算法或最佳適合演算法,分別得到首次適合遞減演算法和最佳適合遞減演算法。最佳適合遞減演算法和首次適合遞減演算法的效果差不多。 - 令N項物品的輸入大小(以遞減順序排序)分別為s1, s2, ... , sN。並設最優裝箱方法使用M個箱子。那麼,首次適合遞減演算法放到外加的箱子中的所有物品的大小最多為1/3 - 放入外加的箱子中的物品的個數最多是M-1 - 令M時物品集I裝箱所需的最優箱子數,則首次適合遞減演算法所用箱子數絕不超過(4M+1)/3 - 令M是將物品集I裝箱所需要的最優箱子數,則首次適合遞減演算法所用箱子數絕不超過11/9 * M + 4。存在使得首次適合遞減演算法用到11/9 * M個箱子的序列 ## 10.2 分治演算法 - 分:遞迴解決較小的問題(基本情況除外) - 治:從子問題的解,構建原問題的解 ### 10.2.1 分治演算法的執行時間 所有有效的分治演算法都是把問題分成一些子問題,每個子問題都是原問題的一部分。然後進行某些附加的工作以算出最後的答案。 ``` 方程T(N)=aT(N/b)+Θ(N^k)的解為 T(N)= O(N^(log(b)a)), 若a>b^k O(N^k * logN), 若a=b^k O(N^