分治 Divide and Conquer
Lecture & modified by baluteshih Credit by yp155136
Credit by TreapKing
Q & A
• 影片有什麼問題嗎?
何時使用分治
• 經驗!
• 刷題!
何時使用分治
• 經驗!
• 刷題!
• 面對一個問題時,怎麼枚舉都找不到優化的切入點
• 突發奇想從中間切一半,就可能找到很棒的 conquer 性質
• 當這個性質足夠讓我們在良好的複雜度解決 conquer 時,就 可能可以在犧牲頂多一個 log 的情況下完成整個問題
• 「在 conquer 時能夠省去維護儲存所有資訊的力氣,只需要專 心處理跨分隔線的資訊」,就是分治的精髓
分治小技巧
• 養成良好習慣
• 左閉右閉(我個人常用的方式)
• 左閉右開
• ...(?)
• divide 完之後遞迴下去,直接假設遞迴後得到的結果是理想 的,這樣 conquer 的時候會比較好思考
• 當 n 很小的時候,可以暴力做,可能會減少遞迴的 cost
• EX: merge sort 到 n 很小時,就隨便用一個 O(n^2) 的 sort
• 多寫題目(?)
分析遞迴的工具
• 為什麼要學這個
• 可以分析你寫的程式的時間複雜度
• 可以耍帥
• Credit to Algorithm Design and Analysis, 2019 Fall
先來看看符號的定義
• big-O 定義
• 存在一個常數 c, n_0,使得當 n 比 n_0 大時,f(n) 都會比 c * g(n) 小
• 例如,當 f(n) = x^2 + 10x , g(n) = x^3 時,我們可以 選擇 c = 0.5, n_0 = 10
分析遞迴的常見手法
• Substitution Method (取代法)
• Recursion-Tree Method (遞迴樹法)
• Master Method (套公式大法/大師法)
Substitution Method
• 取代法
• 三個大步驟: Guess 、 Verify 、 Solve
• Guess:
• 猜 f(n) 是屬於哪個複雜度
• Verify:
• 驗證猜想是否正確
• 常用 數學歸納法 驗證
• Solve :
• 解出正確的常數
• 證明 big-O
Substitution Method
• 範例:
• 三個大步驟: Guess 、 Verify 、 Solve
Substitution Method
• 範例:
• 先來做個簡單的化簡,把 big-O 給去掉
• 其中 a, b > 0
Substitution Method
• 範例:
• 猜測
Substitution Method
• 猜測
• 使用數學歸納法
• n = 1: trivial
Substitution Method
• 猜測
• 使用數學歸納法
• n = 1: trivial
• n > 1:
Substitution Method
• 猜測
• 使用數學歸納法
• n = 1: trivial
• n > 1:
Substitution Method
• 猜測
• 使用數學歸納法
• n = 1: trivial
• n > 1:
• 因此,由數學歸納法,可以得到 T(n) 是 O(N log N) 的
Substitution Method
• 剛剛的例子似乎沒有用到「解出正確常數」的樣子
• 其實只是運氣特別好(?)
• 讓我們來看更多例子吧
Substitution Method
• 範例:
• 試證明: T(n) 是 O(n^3)
• Hint:
Substitution Method
• 來用用數學歸納法吧
• n = 1:trivial
• n > 1:
Substitution Method
• 來用用數學歸納法吧
• n = 1:trivial
• n > 1:
Substitution Method
• 來用用數學歸納法吧
• n = 1:trivial
• n > 1:
為了讓不等式成立,
也就是說,cn^3 > 2bn
Substitution Method
• 來用用數學歸納法吧
• n = 1:trivial
• n > 1:
• 所以,當 時,
T(n) 就是 O(n^3)
為了讓不等式成立,
也就是說,cn^3 > 2bn 我們可以設 c >= 2b, n >= 1
Substitution Method
• 範例:
• 試證明: T(n) 是 O(n^2)
Substitution Method
•
• n = 1 時, trivial
• n > 1 時,
Substitution Method
•
• n = 1 時, trivial
• n > 1 時,
• 哇,好像沒辦法繼續推了
• 我們真的猜錯了嗎?
Substitution Method
•
• n = 1 時, trivial
• n > 1 時,
• 哇,好像沒辦法繼續推了
• 我們真的猜錯了嗎?
• 其實不全然,但這也是取代法的一個盲點:有的時候猜錯就證不 出來了
Substitution Method
•
• n = 1 時,
Substitution Method
•
• n = 1 時,
• n > 1 時,
Substitution Method
•
• n = 1 時,
• n > 1 時,
為了讓不等式成立,
也就是說,c_2n > bn
Substitution Method
•
• n = 1 時,
• n > 1 時,
• 所以,當
時,T(n) 就是 O(n^2) !
為了讓不等式成立,
也就是說,c_2n > bn 我們可以設c_2 >= b, n >= 1
Substitution Method
• 除了上面那個技巧之外,Substitution 還有一個經典用法:變 數變換
• 例:
Substitution Method
• 例:
• 變數變換1:
Substitution Method
• 例:
• 變數變換1:
• 變數變換2:
Substitution Method
• 例:
• 變數變換1:
• 變數變換2:
• 所以:
Recursion-Tree Method
• 遞迴樹法
• 顧名思義,就是把遞迴樹畫出來 之後,去把花費的時間總和
算出來
• 算出來之後,在用前面介紹的 substitution method 檢查
• 遞迴樹:遞迴的過程
• 遞迴樹法並不嚴謹,他只能幫你「猜」複雜度
reference:http://kkc47.blogspot .com/2014/08/learning-dynamic- programming-day-1.html
Recursion-Tree Method
• 來看個例子
• 例:
• 試試看把遞迴樹畫出來
Recursion-Tree Method
•
Recursion-Tree Method
•
Recursion-Tree Method
•
Recursion-Tree Method
• 例:
• 可以得到
Recursion-Tree Method
• 例:
• 可以得到
• 根據等比級數,可得
• 因此,T(n) 是 O(n^2)
• 但這不足以證明 T(n) 是 O(n^2)
• 真的要分析的話,可以試著用數學歸納法
Master Theorem
• 主定理
• 很好用的工具!
Master Theorem
• 假設
• Case 1: s.t.
• Case 2: s.t.
• Case 3: s.t.
且 s.t. 在 n 足夠大時
Master Theorem
• 白話一點 QQ
Master Theorem
• 白話一點 QQ
• 不嚴謹的說,就是比較 f(n) 跟 的關係
• 如果一樣的話,就加個 log,否則就是比較大的那個
• 以下會分三種情況說明,順便會舉點例子
• 小於
• 等於
• 大於
Master Theorem
• 比較 f(n) 跟 的關係
• 如果 ,那麼
• 例如,
• 根據主定理,
所以 T(n) 是 O(n^2)
• 可以試著用 Substitution method 寫寫看
• 也可以試著畫畫看 Recursion tree
Master Theorem
Master Theorem
• 比較 f(n) 跟 的關係
• 如果 ,那麼
• 例如,
• T(n) = O(n log n) ,前面也有用 substitution 證明過
• 可以想像成原本的複雜度多一個 log
• 在這個 case,f(n) 如果多乘好幾個 log 也沒關係,依然可 以直接多補一個上去
Master Theorem
• 比較 f(n) 跟 的關係
• 如果 ,那麼
• 例如,
• T(n) = O(n^2) ,前面有用 recursion-tree 證過了
• 這個 case 其實有個額外的條件,但是不常發生(?)
分析遞迴的工具
• 數學理論的部份就講到這邊啦
• 什麼東西都記不起來,也可以暫時只記得 Master Therorem 就好 XD
• 接下來要講實際上會遇到的題目,前面睡著的可以起床了
最大連續和
• 給你一個長度為 N 的序列 a_1, a_2, ..., a_N ,請 你找到 (L, R) ,滿足 a_L + ... + a_R 最大
• N <= 10^5
• 先來想想看要怎麼做
• 練習題
:https://zerojudge.tw/ShowProblem?problemid=d784
最大連續和
• O(N^3)
• O(N^2)
• O(N log N)
• O(N) (?)
• 那換一個限制,只能用「分治法」來做
• 因為我們現在在教分治嘛
最大連續和
• 答案有 O(N^2) 種
• 我們有沒有辦法好好的 divide 成一半,然後想盡辦法 conquer 起來呢?
最大連續和
• 是可以的!
• 我們先把序列切成兩半,左右遞迴求出各自半部的最佳解
• Solve(L, R) 會回傳 a_L, ..., a_R 的最佳解
最大連續和 --- conquer
• 跨過兩側,一定會拿 a[mid] 跟 a[mid + 1]
• 所以我們要求的東西 a[L] + ... + a[R],可以換成找 (a[L] + ... + a[mid]) + (a[mid + 1] + ... + a[R])
• 有沒有發現,上面的式子就是從中間點開始,往左 & 往右的走 一段路後,得到的總和
• 於是乎,取「從中間往左加的最大值」加上「從中間往右加的最大 值」,最後加起來就完成 conquer 的部份了
最大連續和
最大連續和
• 複雜度分析
最大連續和
• 複雜度分析
• 假設 T(n) 是 solve() 的長度為 n 的複雜度
• 那麼,T(n) = 2T(n / 2) + O(n)
最大連續和
• 複雜度分析
• 假設 T(n) 是 solve() 的長度為 n 的複雜度
• 那麼,T(n) = 2T(n / 2) + O(n)
• 所以,最後的複雜度是 T(n) = O(n log n)
最大連續和
• 複雜度分析
• 假設 T(n) 是 solve() 的長度為 n 的複雜度
• 那麼,T(n) = 2T(n / 2) + O(n)
• 所以,最後的複雜度是 T(n) = O(n log n)
• Challenge:用分治法可以做到 O(n) 嗎?
多項式乘法
• 給你兩個 n 次多項式,請你求出兩個多項式的乘積。
• Ex:
• 練習題:https://tioj.ck.tp.edu.tw/problems/1064
• 拿部份分數就好了(?)
• 把 x 當成 10 這樣
多項式乘法
• 給你兩個 n 次多項式,請你求出兩個多項式的乘積。
• Ex:
• 來想想看要怎麼做吧
多項式乘法
• Naïve作法
• 用個雙重迴圈跑一跑乘一乘加一加
• 時間複雜度是 O(n^2)
多項式乘法
• Naïve作法
• 用個雙重迴圈跑一跑乘一乘加一加
• 時間複雜度是 O(n^2)
• 好慢(?)
• 有沒有更快的方式?
多項式乘法
• Naïve作法
• 用個雙重迴圈跑一跑乘一乘加一加
• 時間複雜度是 O(n^2)
• 據說在古早的時代,從來沒有人質疑過乘法可以做得更快
• 可能會有人想到 FFT(Fast Fourier Transform,快速傅立 葉轉換),但我現在沒有要講這個
• 有興趣的同學可以查查相關資料呦
多項式乘法
• 把多項式表達為
• 同理:
• 順便不失一般性的假設 n+1 是 2 的次方(如果不是的話,就 加入一些係數為 0 的高次項)
• 為了方便之後的講解 :)
多項式乘法
• 既然是在教分治,那就先把多項式切兩半看看
• 令
• 同理
多項式乘法
• 既然是在教分治,那就先把多項式切兩半看看
• 令
• 所以,
多項式乘法
• A, B, C, D 都是 (n/2) 次的多項式
• 所以做四次比較小的乘法 & 一點加法就可以解決原問題了!
多項式乘法
• A, B, C, D 都是 (n/2) 次的多項式
• 所以做四次比較小的乘法 & 一點加法就可以解決原問題了!
• 成功切成子問題 => Divide & Conquer!
• 可是,這樣真的有比較快嗎?
多項式乘法
• A, B, C, D 都是 (n/2) 次的多項式
• 所以做四次比較小的乘法 & 一點加法就可以解決原問題了!
• 成功切成子問題 => Divide & Conquer!
• 可是,這樣真的有比較快嗎?
• 來分析一下複雜度,T(n) = 4T(n / 2) + O(n)
• O(n) 是加法的部份
多項式乘法
• A, B, C, D 都是 (n/2) 次的多項式
• 所以做四次比較小的乘法 & 一點加法就可以解決原問題了!
• 成功切成子問題 => Divide & Conquer!
• 可是,這樣真的有比較快嗎?
• 來分析一下複雜度,T(n) = 4T(n / 2) + O(n)
• 用前面的教的遞迴分析,或者是 Master Theorem,
可以得到 T(n) = O(n^2)
• 沒有變快 QQQ
Karatsuba
• 我們要算 AC, AD + BC, BD
Karatsuba
• 我們要算 AC, AD + BC, BD
• 靈光一閃,如果我們計算
Karatsuba
• 我們要算 AC, AD + BC, BD
• 靈光一閃,如果我們計算
• 就可以把原本的式子化為
• 乘法只剩下三次了!
Karatsuba -- 複雜度
• 剛剛上面那個演算法,被稱做 Karatsuba Algorithm
• 我們來分析一下時間複雜度吧
Karatsuba -- 複雜度
• 剛剛上面那個演算法,被稱做 Karatsuba Algorithm
• 我們來分析一下時間複雜度吧
• 根據前面教的 Master Theorem,可以得到 T(n) 就是
Karatsuba -- 複雜度
• 剛剛上面那個演算法,被稱做 Karatsuba Algorithm
• 我們來分析一下時間複雜度吧
• 根據前面教的 Master Theorem,可以得到 T(n) 就是
• 類似的做法也有被應用在矩陣乘法上,有興趣的同學可以查查
平面最近點對
• 在平面上給你 N 個點,要你找出歐式距離最短的兩個點。
• N <= 100,000
• 練習題:https://tioj.ck.tp.edu.tw/problems/1500
、 https://codeforces.com/contest/429/problem/D
平面最近點對
• 開心 C(N, 2) = O(N^2) 當然不是我們要的
• 如果在平面上想要做 divide and conquer,該怎麼分割問 題?
平面最近點對
• 我們可以先把所有輸入的點照 x 座標排序後,在中間畫一條分 隔線。
• 這樣可能的答案就分成(兩個點都在左邊)、(兩個點都在右 邊)、(橫跨分隔線) 三種情況
• 一些平面上的 D&C 題都會做類似的事。
• 顯然只有點對「橫跨分隔線」的情況需要討論,如果這個情況可 以解決的話,其他兩個情況只要遞迴下去解就好了。
平面最近點對
• 如果只是要算分隔線兩邊的最近點對,有什麼好方法嗎?
平面最近點對
• 如果只是要算分隔線兩邊的最近點對,有什麼好方法嗎?
• 因為 x 座標的大小關係已經確立了,所以可以把兩邊直接照 y 座標排序
• 然後就發現還是好困難...
平面最近點對
• 定神一想,會發現如果遞迴下去後找到的最近點對的距離是 d
,那麼我們根本就不需要考慮那些距離超過 d 的點對
• 所以離分隔線超過 d 的點都不需要去考慮。
• 對於每個點,也只有 y 座標差距不超過 d 的點可能可以讓你 找到更近的點對
• 這樣子複雜度會是好的嗎?
• 聽起來只是個壓常數的剪枝,但在什麼情況下,這個做法一樣會 退化成 O(N^2) 呢?
平面最近點對
• 因為已經知道各自的最近點對的距離是 d ,所以每個點附近的 點不會太多!
• 附近的點只會有常數個,所以直接跑下去複雜度就是好的!
平面最近點對
• 來分析一下複雜度
• T(n) = 2T(n / 2) + O(n log n)
• conquer 時要把點按照 y 座標排序
平面最近點對
• 來分析一下複雜度
• T(n) = 2T(n / 2) + O(n log n)
• conquer 時要把點按照 y 座標排序
• 根據 recursion-tree 和 master theorem ,都可以得到 O(n (log n)^2)
• 注意這裡的 master theorem 要套 Case 2
平面最近點對
• 不能做到更好嗎? O(n (log n)^2) 感覺很慢
平面最近點對
• 不能做到更好嗎? O(n (log n)^2) 感覺很慢
• 靈光一閃,想起 merge sort
平面最近點對
• 不能做到更好嗎? O(n (log n)^2) 感覺很慢
• 靈光一閃,想起 merge sort
• 後面那個 O(n log n) ,可以用 merge sort 壓到 O(n)
• T(n) = 2T(n / 2) + O(n)
• T(n) = O(n log n) !
平面最近點對
• 不能做到更好嗎? O(n (log n)^2) 感覺很慢
• 靈光一閃,想起 merge sort
• 後面那個 O(n log n) ,可以用 merge sort 壓到 O(n)
• T(n) = 2T(n / 2) + O(n)
• T(n) = O(n log n) !
• 這題也有非分治的作法,有興趣可以上網查查
尋找第 k 大
• 給你一個序列,請你找到第 k 大
• 還不簡單? sort 就好啦!
• 但我希望可以做到 O(n)
尋找第 k 大
• 神仙分治想法
• 我們首先先把序列五個五個分一組,並找到每組的中位數
尋找第 k 大
• 把所有中位數蒐集起來,再找到「中位數的中位數」
• 怎麼再找中位數?
尋找第 k 大
• 把所有中位數蒐集起來,再找到「中位數的中位數」
• 怎麼再找中位數?對規模 n/5 的問題呼叫尋找第 n/10 大
尋找第 k 大
• 令剛剛找到的數字是 p,把數字分成 >p 跟 <p 兩堆
尋找第 k 大
• 令剛剛找到的數字是 p,把數字分成 >p 跟 <p 兩堆
• 注意到至少有 3n/10 個數字比 p 小、至少有 3n/10 個數字 比 p 大
尋找第 k 大
• 令剛剛找到的數字是 p,把數字分成 >p 跟 <p 兩堆
• 注意到至少有 3n/10 個數字比 p 小、至少有 3n/10 個數字 比 p 大
• 看第 k 大在哪邊,往那邊遞迴就可以了!
• 這種神仙操作到底複雜度長怎樣呢?
尋找第 k 大
• 分析複雜度:
• 對規模 n/5 的問題呼叫尋找第 n/10 大: T(n/5)
• 遞迴找 k 大: 少掉至少 3n/10 個數字 -> T(7n/10)
• 分組、分兩堆等等: O(n)
尋找第 k 大
• 分析複雜度:
• 對規模 n/5 的問題呼叫尋找第 n/10 大: T(n/5)
• 遞迴找 k 大: 少掉至少 3n/10 個數字 -> T(7n/10)
• 分組、分兩堆等等: O(n)
尋找第 k 大
• 分析複雜度:
• 對規模 n/5 的問題呼叫尋找第 n/10 大: T(n/5)
• 遞迴找 k 大: 少掉至少 3n/10 個數字 -> T(7n/10)
• 分組、分兩堆等等: O(n)
尋找第 k 大
• 分析複雜度:
• 對規模 n/5 的問題呼叫尋找第 n/10 大: T(n/5)
• 遞迴找 k 大: 少掉至少 3n/10 個數字 -> T(7n/10)
• 分組、分兩堆等等: O(n)
by 數學歸納法!
尋找第 k 大
• 分析複雜度:
• 對規模 n/5 的問題呼叫尋找第 n/10 大: T(n/5)
• 遞迴找 k 大: 少掉至少 3n/10 個數字 -> T(7n/10)
• 分組、分兩堆等等: O(n)
by 數學歸納法!
• 這東西還蠻詭異的,但真的就是線性XD
• 主要是想讓你們感受一下分治法的強大
總結分治
• 已經看過很多在序列、平面的題目了
• 但是其實分治還能在奇怪(?) 的地方分治
• 例如樹、圖等等
• 往往分治還需要搭配很多噁心的資料結構、演算法
• 分治常常會在偏難的題目中走出一條通路
• 大家加油