Michael Tsai 2013/9/12
Algorithm Design Strategy
不是教你”某種演算法”
而是怎麼用”某些策略”來”設計演算法”
第一課 : 各個擊破 =Divide & Conquer
什麼是 Divide-and-Conquer
當碰到一個問題的時候 :
1. 把問題分解 (Divide) 成一些比較小的同樣問題
2. if 問題小到可以直接解決 (Conquer),
then 直接解決
else 遞迴地呼叫自己的分身解決較小的這些問題
3. 把已解決的小問題解答結合 (Combine) 起來變 成原來的問題的解答
Base
case Recursive
case
Divide and Conquer 的好處
容易能解決困難的問題
思考模式 : 解決最簡單的 case + 整合小問題的答案變成 大問題的答案
通常也容易因此想出更有效率的演算法
執行時間的複雜度比較低
適合平行運算 (Multi-core systems!)
更有效的記憶體存取
小的 subprogram 與它的 subprogram 們的資料都可以放在在 CPU 的 cache 裡面 , 而不需要存取速度比較慢的主記憶體
例子 1: 河內塔
規則 :
1. 每次可以移動每根棍子上最上面的盤子 , 到其 他棍子已有的盤子上 .
2. 大盤子不能放在小盤子上面
3. 一次只能移動一個盤子
例子 1: 河內塔
目標 : n 個盤子 , 柱子 1 移到 柱子 3
1 2 3
n 個盤子
例子 1: 河內塔
1 2 3
n=1 時 , 直接可以把盤子移過去 Base
Case:
例子 1: 河內塔
1 2 3
1. n-1 個盤子 , 從柱子 1 移到柱子 23. n-1 個盤子 , 從柱子 2 移到柱子 3
2. 把最大的盤子從柱子 1 移到柱子 3 n>1 時 :
Recursive Case:
例子 1: 河內塔
Divide 在哪裡 ?
原本 : n 個盤子從柱子 1 移到柱子 3
分成 :
1. n-1 個盤子從柱子 1 移到柱子 2
2. 1 個盤子從柱子 1 移到柱子 3
3. n-1 個盤子從柱子 2 移到柱子 3
Combine 在哪裡 ?
這個例子不需要額外 combine
比較小的同樣問題
例子 2: Merge Sort
Input: n 個數字
Output: 照順序由大排到小
n 個數字
n 個排好順序的數字
例子 2: Merge Sort
1 個數字
1 個排好順序的數字 n=1 時 , 沒有排序的問題 , 直接輸出 Base
Case:
啥都不用做
例子 2: Merge Sort
n 個數字
n 個排好順序的數字 n>1 時
Recursive Case:
n/2 個數字 n/2 個數字
Divide: 分成兩等分 , 分別排 序
Combine: 兩個排序好的數列合併成一個
分別排序 : 比較小的同 樣問題
例子 2: Merge Sort
Divide 在哪裡 ?
原本 : 把 n 個數字排序
分成 :
2 個 (n/2 個數字排序 )
Combine 在哪裡 ?
把兩個排好的數列合併成一個數列
計算 divide-and-conquer 的演算法執行時間
用遞迴式最自然
例 1. 河內塔 , 移動 n 個盤子 :
如果的話 , 直接移動過去 .
如果的話 , 分為以下步驟 :
1. n-1 個盤子從柱子 1 移到柱子 2
2. 1 個盤子從柱子 1 移到柱子 3
3. n-1 個盤子從柱子 2 移到柱子 3
� (�− 1)
� (�− 1)
� (1)
�
( 1 ) =Θ(1)
�
( � )
� (� )=
{
2� (� −1)+� (1)Θ(1),if
,if
Recurrences
例 2. Merge Sort, n 個數字排序 :
如果 n=1 時 : 直接輸出 .
如果 n>1 時 :
分成 2 個 (n/2 個數字排序 )
把兩個排好的數列合併成一個數列
� (�)
2
� (�/2) Θ(�)
Θ(1)
� (�)=
{
2�(
Θ (1)�2)
+Θ (�) ,if ,if細節
例 : Merge Sort 的 n 不是偶數時 , 就會變成
通常我們卻很豪爽的使用
甚至
� (�)=
{
�(
⌈ �2 ⌉)
+� ( ⌊ �Θ(1)2 ⌋ )+Θ(�) ,if ,if� (�)=
{
2�(
Θ (1)�2)
+Θ (�) ,if ,if� (�)=2�
(
�2)
+Θ (�)假設 : ( 大部分時候都成 立 )
1. Ceiling & floor functions 不影響 recurrence 的解 2. Boundary case -
n 很小的時候通常 execution
time=constant ( 不 一定只是 n=1 時 )
股市大亨
菜瓜布股份有限公司股票股價
未卜先知 , 已知未來的股價走勢 ( 內線 ?)
問 : 如何找出可以使獲利最大的買進賣出時機 ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0
20 40 60 80 100 120
股價
股價
股市大亨 : 嘗試一
嘗試一 : 有沒有什麼絕招 ? ( 的方法 )
找最低點當買入點 , 往後找之後的最高點當賣 出點
找最高點當賣出點 , 往前找之前的對低點當買 入點
以上找出的是否為正確解 ?
答 : 否 .
股市大亨 : 嘗試二
嘗試二 : 暴力法
不用大腦的方法
每種可能性都試試看 ( 窮舉法 )
如此的話要花多少時間 ?
有幾種可能性 :
就算每種可能性都只花也是要
能不能更好 ?
股市大亨 : 嘗試三
嘗試三 : Divide-and-Conquer 的方法
首先先把原本的問題稍微轉換
題目變成在一列中找出此一數列的一連續子數列 , 使其總合為最大 , 又稱 Maximum Subarray Problem
Day 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Pric
e 10
0 113 110 85 105 102 86 63 81 101 94 106 101 79 94 90 97 13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7 Day 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Pric
e 10
0 113 110 85 105 102 86 63 81 101 94 106 101 79 94 90 97 13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7
大刀一砍再來想
n 個數字
n/2 個數字 n/2 個數字
n 個數字的 maximum subarray
n/2 個數字的 maximum subarray n/2 個數字的 maximum subarray
假設可以找到兩個 n/2 大 小的 maximum
subarray
如何找到 n 個數字的 maximum subarray?
Recursive Case:
low 中間點
………..
mid
mid+1 ………..
…. high
股市大亨 : 嘗試三
Maximum subarray 可能出現的情形 :
n/2 個數字 n/2 個數字
1. 只包含左半部數字 (low to mid)
2. 只包含右半部數字 (mid+1 to high)
3. 兩邊的數字都包含 . 因為必須是 連續的 , 所以必須跨過中間點
最後三種比較 , 找出總和最大的一個即 可 .
1. 和 2. 可從 n/2 個數字的結果 得到 .
3. 必須另外計算 . Recursive
Case:
股市大亨 : 嘗試三
3. 如何找出包含中間點的 maximum subarray 呢 ?
n/2 個數字 n/2 個數字
中間點
(1) 尋找以中點開始 , 左邊 的 maximum subarray
(2) 尋找以中點開始 , 右邊 的 maximum subarray
(3) 合併 (1) 和 (2) 即為包含中間點的 maximum subarray
所花時間 ?
Θ(�)
low
………..
mid
mid+1 ………..
…. high
股市大亨 : 嘗試三
n=1 的時候
maximum subarray?
就是它自己 .
Base Case:
股市大亨 之 酥多扣的 (pseudo-code)
Find_Max_Crossing_Subarray(A, low, mid, high) left_sum=-
sum=0
for i=mid downto low sum=sum+A[i]
if sum>left_sum left_sum=sum max_left=i right_sum=-
sum=0
for j=mid+1 to high sum=sum+A[i]
if sum>right_sum right_sum=sum max_right=j
return (max_left, max_right, left_sum+right_sum)
A: array 本身
low: array 最小的 index
mid: 左半部 array 的最大 index high: array 最大的 index
股市大亨 之 酥多扣的 (pseudo-code)
Find_Maximum_Subarray(A,low,high) if high==low
return (low,high,A[low]) else
mid=
(left_low,left_high,left_sum)=Find_Maximum_Subarray(A,low,mid)
(right_low,right_high,right_sum)=Find_Maximum_Subarray(A,mid+1,high) (cross_low,cross_high,cross_sum)=Find_Max_Crossing_Subarray(A,low,mi d,high)
if left_sum>=right_sum and left_sum>=cross_sum return (left_low,left_high,left_sum)
else if right_sum>=left_sum and right_sum>=cross_sum return (right_low,right_high,right_sum)
else
return (cross_low,cross_high,cross_sum)
Base
CaseRecursive Case Divide
Combin e
Conque r
執行時間分析
n 個數字找 max-subarray
Base case: n=1 的時候直接 return.
Recursive case:
Divide: 2 個 n/2 個數字找 max subarray
Combine:
1. 確認 n>1, 計算中間點位置等等
2. 尋找通過 mid 的 max subarray
3. 比較三個 max subarray 的大小決定最後結果
�
( 1 ) =Θ(1)
�
( � )
2
� ( �/2 )
Θ(�)
Θ(1)
� (�)=Θ(1)+2�
(
�2)
+Θ(�)+Θ(1)=2�(
�2)
+Θ(�)Θ(1)
� (�)=
{
2�(
Θ (1)�2)
+Θ (�) ,if ,if 解 :Today’s Reading Assignment
Cormen ch 4 – 4.1
下次…
其他 Divide-and-Conquer 的例子
矩陣相乘
找中位數
如何解遞迴式