Algorithm Design Methods Greedy Algorithm
by Chin Huang Lin
什麼是「貪心」?
什麼是「貪心」?
• 常見的貪心:
1. 爸媽買了布丁,偷偷吃掉老哥的 2. 國中生追女生,一個不夠追兩個 3. 期末公佈成績, A 不夠還要 A+
4. 廠商收入縮減,先降低員工薪資 5. 參加朋友喜宴,自備塑膠袋打包 6. ……
• Want more and better!
看不見的樹 看不見的樹
• 對於一個問題,我們考慮的每一組方案事實上對應到一組「決策」
• ex. 數獨問題中有 格空格,依序為編號為 ,那麼我們事實上是在決策 ,其中 為 第 格的值,可以為 1~9 之間的數字。
• 整個決策的過程可以畫成一棵樹,每個葉節點對應到一個可能的方案,其 中有些滿足所有條件形成解,其他則否
• ex.
•
�1=1
�1=2
�1=…
�1=9
�2=1
�2=2
�2=…
�2=9
��=1
��=…
��=9
(-1)
(-1)
(-1)
��=…
枚舉在幹麼?
枚舉在幹麼?
• 枚舉事實上就是試圖拜訪整棵決策樹,找出樹裡的解節點
• 剪枝事實上就是根據一些推理排除絕對不可能的分支
• 有時候,我們可以根據一些問題的特性,確定解節點所在的分支!
• 總是選擇當前看起來最有利的分支,然後義無反顧
�1=1
�1=2
�1=…
�1=9
�2=1
�2=2
�2=…
�2=9
��=1
��=…
��=9
(-1)
(-1)
(-1)
��=…
5 8
2 7
3 1
5 8
2 7
3 1
生活中的貪心 ( 一)
生活中的貪心 ( 一)
• 黑猩國是一個由大量的黑腥猩建立的國度,裡面共有 種面額的貨幣。其 中必定有一種面額是 1 元,剩餘的面額不一定,但對於任意兩種面額 () ,必定有 。那麼當黑腥猩要付 元購物的時候,最少需要用到幾個貨幣 呢?
•
這麼簡單,我幼稚園就會了!
這麼簡單,我幼稚園就會了!
• 現行的貨幣在不考慮 20 元的情況下事實上就滿足條件
• 生活中你都怎麼付款?
• 當然都盡量以大面額的付阿!
• 為什麼呢?
♪ 想一想:如果沒有「任意兩面額必定有一者是另一者的因數」這個條件,
這樣做還會正確嘛?為什麼?
生活中的貪心 ( 二 ) 生活中的貪心 ( 二 )
• Zerojudge b105 誰先晚餐 (2005 NPSC 國中組決賽 )
•
http://zerojudge.tw/ShowProblem?problemid=b105
• 生活中,你會怎麼做?
• 當然是先讓吃最慢的人吃阿!
• 為什麼呢?
貪心法的簡單證明 貪心法的簡單證明
老師,我這題這樣做有什麼問題?
嗯……你怎麼知道你這作法是對的?
可是
可是我範測和自己產的測資都對阿
可是
而且我也想不到更好的方法了
貪心法的簡單證明 貪心法的簡單證明
• 一時找不到反例,不代表這個作法是對的
• 輸入有這麼多種可能,有測到的佔不到 1% ,這樣就說正確的話……
• 不就跟只訪問 4 個人他們總統選舉會投誰,就斷定誰會當選一樣?
• 想不到更好的方法,不構成這個作法的證明
• 怎麼知道不是運氣不好或者資質駑鈍,恰好沒有想到更好的方案?
• 貪心法需要更積極的證明方式!
貪心法的簡單證明 貪心法的簡單證明
• 對於一個問題 P ,提出一個作法 A
• 目標:證明 A 給出的解 S 總是最優的
• 先假設 A 給出的解 S 在某種情況下不是最優的
• 假設此時 P 的真正最優解應為 S’ ,那麼證明 S’ 不存在 ( 利用矛 盾 )
• 通常有兩個方法:
1. 對於任意的 ,都構造出一組相異於 的解 ,並且滿足 比 還好 2. 設法證明 不比 還糟
• 於是 A 給出的解 S 總是最優的
•
貪心法的簡單證明 貪心法的簡單證明
• Zerojudge b105 誰先晚餐 (2005 NPSC 國中組決賽 )
• 符號定義:對於一組方案 ,用 表示 方案下全部的人都吃完飯的時間
• 證明架構:
1. 為了方便表示與比較,先用代數表達出問題與解答的數學模型
2. 假設我們的算法給出的解 是錯的,那麼存在真正的最優解 ,且
3. 如果有兩個人編號分別為 ( 編號就代表吃飯的順序 ) 滿足「 且 吃得比 快」,我們就說這兩人形成一組「逆序對」。既然最優解 ,那麼 裡面一定存在 相鄰的兩元素形成逆序對 ( 想想看,為什麼 )
4. 透過把這兩個人的順序交換,獲得方案 ,並證明 ≦
5. 如果 中還存在相鄰逆序,則再把該對逆序元素交換形成 ,類似 4 的證明可知 6. 不斷重複類似 5 的步驟,直到當前的解 中不存在相鄰逆序。此時 必定不存
在任何逆序對 ( 想想看,為什麼 ) ,從而有
7. 於是有 ,即 ,與 的假設矛盾,從而證明 是最佳解
•
貪心法的簡單證明 貪心法的簡單證明
1. 為了方便表示與比較,先用代數表達出問題與解答的數學模型
• 一組方案中,設吃飯順序依序為 ,則
• 吃飯所需時間依序以 來代表
• 料理所需時間依序以 來代表
• 由於廚師一定不會停手,第 個人吃完飯的時間為
• 題目要求 盡量小
•
貪心法的簡單證明 貪心法的簡單證明
2. 假設存在真正的最優解 ,且
3. 既然最優解 ,那麼 裡面一定存在相鄰的兩元素形成逆序對
• 不妨假設就是第 個人和第 個人形成相鄰逆序對
•
貪心法的簡單證明 貪心法的簡單證明
4. 透過把這兩個人的順序交換,獲得方案 ,並證明 ≦
5. 如果 中還存在相鄰逆序,則再把該對逆序元素交換形成 ,類似 4 的 證明可知
6. 不斷重複類似 5 的步驟,直到當前的解 中不存在相鄰逆序。此時 必 定不存在任何逆序對,從而有
7. 於是有 ,即 ,與 的假設矛盾,從而證明 是最佳解
• 事實上要證明的只有兩件事情:
1) 對於一個存在相鄰逆序對的方案 ,可以透過交換該組逆序對獲得 ,且 2) 不斷重複地把相鄰逆序對交換,總有一天會變成一組不存在相鄰逆序的方案
•
貪心法的簡單證明 貪心法的簡單證明
1) 對於一個存在相鄰逆序對的方案 ,可以透過交換該組逆序對獲得
• ,且
• 的情形:
• 吃飯順序依序為
• 吃飯所需時間依序為
• 料理所需時間依序為
• 第 個人吃完飯的時間為
• 第 個人和第 個人形成相鄰逆序對
•
• 的情形:
• 吃飯順序依序為
• 吃飯所需時間依序為
• 料理所需時間依序為
• 第 個人吃完飯的時間為 ,等下討論
• 第 個人和第 個人不再是相鄰逆序對
•
貪心法的簡單證明 貪心法的簡單證明
1) 對於一個存在相鄰逆序對的方案 ,可以透過交換該組逆序對獲得
• ,且
• :
• 對於 ,
• 對於 ,
• 發現
• 的情形:
• 吃飯順序依序為
• 吃飯所需時間依序為
• 料理所需時間依序為
• 第 個人吃完飯的時間為 ,等下討論
• 第 個人和第 個人不再是相鄰逆序對
•
貪心法的簡單證明 貪心法的簡單證明
1) 對於一個存在相鄰逆序對的方案 ,可以透過交換該組逆序對獲得
• ,且
• :
• 對於 ,
• 對於 ,
• 發現
• 比較 與 :
• 為了簡潔,用 代表
• 問題:,誰大?
貪心法的簡單證明 貪心法的簡單證明
1) 對於一個存在相鄰逆序對的方案 ,可以透過交換該組逆序對獲得
• ,且
• :
• 已知條件:
• 為了簡潔,用 代表
♪ 比其他三者都還大,從而有
• 比較 與 :
• 為了簡潔,用 代表
• 問題:,誰大?
貪心法的簡單證明 貪心法的簡單證明
2) 不斷重複地把相鄰逆序對交換,總有一天會變成一組不存在相鄰逆 序的方案
• 根據逆序對的定義可知,每次交換後,逆序對總數會 -1
• 由「逆序對數不為 0 則存在相鄰逆序對」可知,永遠找得到相鄰逆序 對交換
• 逆序對數為 0 時,顯然相鄰逆序對數也為 0 ,得証
不直接的貪心 不直接的貪心
• UVa 714 Copying Books
• http://uva.onlinejudge.org/external/7/714.html
• 為了懲罰你和你的朋友們 ( 共 個人 ) 一起把垃圾偷偷丟到隔壁班的花盆裡,老師精選 了 篇的文章給你們在午休時站著罰抄。
• 這 篇文章在排成一列的 個人面前排成一列,為了作業方便你們決定每個人都只能負責 抄寫其中連續排列的若干篇文章 ( 例如某個人可以負責抄寫第 2~5 篇文章,卻不能夠負 責抄寫第 2,3,5,8 篇文章 ) ;而且為了工整性,老師要求一篇文章一定只能由一個人抄 寫,否則會有不同的字跡。
• 由於你們的感情很好,所以就算每個人負責的份量不同也不會吵架。但是為了早點抄完回 去打鬧,你們希望負責份量最多的人負責的份量盡量少。
• 在你們已經知道這 篇文章的頁數依序為 ,請問負責份量最多的那位同學最少要負責幾頁 的抄寫量呢?
•
一點瓶頸 一點瓶頸
• 如果後面人還很多,就應該抄少一點;如果後面人少,就應該抄多一點
• 但是怎麼拿捏呢?
• 如果自己不是第一名,那應該盡量抄多一點;如果自己是第一名,那應該盡量抄少一點;
但是第一名又必須要是第一名(?)
• 如果我們知道到底第一名最後抄多少書就好了……
• 最優化決策很難 ( 要把決策樹上所有葉節點拿出來比較 ) ,但是判定問題簡單很多──怎 麼把最優化決策問題轉為判定問題呢?
• 當然就是枚舉啦!
• 直接枚舉第一名要抄多少頁 ( 其實就是枚舉每個人最多不能夠超過多少頁 ) ,然後貪心地 盡量抄到不能再抄
• 如果抄不完,代表當前枚舉得太理想
• 複雜度 ,好慢!
•
有點單調 有點單調
• 我們要求的是「抄得完書的情況下,負擔最重的人要抄的最少頁數 」
• 如果我們枚舉負擔最重的人要抄 頁,那麼對於所有 ,一定都抄不完;對 於所有 ,一定都抄得完
• 不就是摔蛋問題嘛?
• 馬上把枚舉的部份改成二分枚舉,複雜度降為
•
霍夫曼編碼 霍夫曼編碼
• 一般而言,電腦中的每個字元都以相同的位元數來儲存,舉例而言只要是 ch ar 都用 8 bits 來儲存,因此有 個字元,就會佔用 bits
• 如果已經知道哪些字元各會出現幾次,事實上我們有更好的辦法!舉例來說:
• 基因序列裡只會有四種字元: ATCG
• 已知 A 出現 185 次, T 出現 47 次, C 出現 59 次, G 出現 308 次
• 兩種不同的表達法:
• 左邊的表達法總共需要 1198 bits ,右邊的表達法只需要 996 bits
•
A T C G
00 01 10 11
A T C G
10 101 100 0
非固定長度編碼 非固定長度編碼
• 問:為什麼不要用更短的編碼?
• ex.
• 答:這樣子在解釋 000 時會有歧異,可解釋為 CG 或 GC
• 所以任意字元的編碼不能是另一字元編碼的前綴 (prefix)
• 這代表所有字元的編碼 ( 即一個編碼方案 ) 可以形成一棵 tree !
• 其中最優 ( 最省空間 ) 的編碼方案對應到的 tree ,稱為最優編碼樹
A T C G
1 01 00 0
最優編碼樹 最優編碼樹
• 右邊是一棵當文本是「 THIS IS THE TEST T EXT XDDD 」時的最優編碼樹
• 最優編碼樹的一些性質
1. 一定不會有只有一個兒子的節點
2. 頻率越高的字元對應的葉節點深度越淺
3. 必定存在一棵最優編碼樹,使得頻率最低的兩個字 元對應的葉節點形成兄弟
4. 定義 為節點 形成的子樹中,所有葉節點的頻率 和。則某編碼樹為 ,把 中某節點 形成的子樹整 棵替換為一個頻率為 的字元形成新樹 ,那麼 為 新字元集的最優編碼樹當起僅當 為最優編碼樹。
•
圖片來源 : http://huffman.ooz.ie/
最優編碼樹 最優編碼樹
1. 一定不會有只有一個兒子的節點
• 否則我們直接用這個節點的子節點取代它自己,依舊是合法的編碼樹但空間消耗更小
最優編碼樹 最優編碼樹
2. 頻率越高的字元對應的葉節點深度越淺
• 否則我們交換兩者的位置,空間消耗更小
最優編碼樹 最優編碼樹
3. 必定存在一棵最優編碼樹,使得頻率最低的兩個字元對應的葉節點形 成兄弟
• 由性質 1 可知深度最大的一層至少有兩個葉節點
• 再配合性質 2 可確定頻率最低的兩個字元皆位於深度最大的一層
• 如果兩者並非兄弟,則把他們位置換成兄弟,解的優劣不變
最優編碼樹 最優編碼樹
4. 某編碼樹為 ,把 中某節點 形成的子樹整棵替換為一個頻率為 的字元形成新樹 ,那麼 為新字元集的最優編碼樹當起僅當 為最 優編碼樹
•
圖片來源 : http://huffman.ooz.ie/
最優編碼樹 最優編碼樹
4. 某編碼樹為 ,把 中某節點 形成的子樹整棵替換為一個頻率為 的字元形成新樹 ,那麼 為新字元集的最優編碼樹當起僅當 為最 優編碼樹
• 每個葉節點貢獻的空間消耗量為「字元頻率 × 節點深度」
• 設節點 形成的子樹中有 個葉節點,頻率分別為 ,與節點 的深度差分別為
• 當節點 的深度為 時,整棵子樹的空間消耗量為
• 假如把 形成的子樹整棵替換為一個頻率為 的字元形成的葉節點,新得到的樹 卻不是最優編碼樹的話……
•
最優編碼樹 最優編碼樹
�
原先的最優編碼樹 總空間消耗子樹消耗
其他部份消耗 1
變換節點後的編碼樹
總空間消耗 子樹消耗
其他部份消耗 1 -
頻率 頻率
假設中比 更好的最優編碼樹 總空間消耗 子樹消耗
其他部份消耗 2 -
把 中的紅點再次換回 子樹的 總空間消耗 子樹消耗
其他部份消耗 2
�
• 根據假設,必須有 其他部份消耗 1 其他部份消耗 2
• 從而 優於 ,與假設矛盾!
•
證明時間 證明時間
4. 某編碼樹為 ,把 中某節點 形成的子樹整棵替換為一個頻率為 的字元形成新樹 ,那麼 為新字元集的最優編碼樹當起僅當 為最 優編碼樹
• 每個葉節點貢獻的空間消耗量為「字元頻率 × 節點深度」
• 設節點 形成的子樹中有 個葉節點,頻率分別為 ,與節點 的深度差分別為
• 當節點 的深度為 時,整棵子樹的空間消耗量為
• 假如把 形成的子樹整棵替換為一個頻率為 的字元形成的葉節點,新得到的樹 卻不是最優編碼樹的話……
• 類似地如果 是最優編碼樹,但 不是最優編碼樹的話……
•
最優編碼樹 最優編碼樹
�
原先的編碼樹
總空間消耗子樹消耗
其他部份消耗 1 變換節點後的最優編碼樹
總空間消耗 子樹消耗
其他部份消耗 1 -
頻率
假設中比 更好的最優編碼樹 總空間消耗 子樹消耗
其他部份消耗 2
• 根據假設,必須有 其他部份消耗 1 其他部份消耗 2
• 從而 優於 ,與假設矛盾!
•
�
頻率把 中紅點換回 子樹的 總空間消耗 子樹消耗
其他部份消耗 2 -
霍夫曼編碼 霍夫曼編碼
1. 把所有字元都視為一棵單節點樹,初始權重為字元頻率,全部的字元形成 一個森林
2. 每次從森林裡面取出權重最小的兩棵樹,並把它們合併成一棵樹,權重為 兩棵樹的權重和
3. 把合併後獲得的樹再加回去森林中,重複 2 直到森林中剩下一棵樹
4. 此時該樹即為最優編碼樹
霍夫曼編碼 霍夫曼編碼
F: 2 O: 3 E: 5 R: 4 G: 4 T: 7
圖片來源 : http://zh.wikipedia.org/wiki/File:Huffman_algorithm.gif
野生的新證明手法 野生的新證明手法
• 我們目前已經學過:
• 數學歸納法
• 直接反證法
• 遞迴證明法!
1) 定義 為輸入規模為 時的子問題 2) 證明當 時,命題成立
3) 證明已知包含 的解的情況下,貪心策略正確 4) 證明存在 的解包含 的解
5) 命題得證
•
野生的新證明手法 野生的新證明手法
1) 定義 為輸入規模為 時的子問題
• 定義 為共有 種字元時的最佳解
2) 證明當 時,命題成立
• 不能更明顯了 XD
3) 證明已知包含 的解的情況下,貪心策略正確
• 由性質 3 ,至少存在一組解包含此貪心策略
4) 證明存在 的解包含 的解
• 根據性質 4 ,找一個恰包含兩葉節點的子樹 ( 根據性質 1 ,只要 這樣的子樹必定 存在 ) 進行縮點,此時 的解必定包含 的解