Outline
- 分治思想和遞歸表達式
- 大整數乘法
- 矩陣乘法的Strassen算法
- 快速傅里葉變化
- 基於分治的排序
- merge-sort排序
- 快速排序
- 排序的下界問題
- 中位數和順序統計量
- 最鄰近點對
- 凸包
Notes
## 分治思想和遞歸表達式
【分治思想】
將一個問題分解為與原問題相似但規模更小的若干子問題,遞歸地解這些子問題,然后將這些子問題的解結合起來構成原問題的解。這種方法在每層遞歸上均包括三個步驟:
- divide(分解):將問題划分為若干個子問題
- conquer(求解):遞歸地解這些子問題;若子問題Size足夠小,則直接解決之
- Combine(組合):將子問題的解組合成原問題的解
【分治遞歸表達式】
- 設T(n)是Size為n的執行時間,若Size足夠小,如n ≤ C (常數),則直接求解的時間為θ(1)
- ①設完成划分的時間為D(n)
- ②設分解時,划分為a個子問題,每個子問題為原問題的1/b,則解各子問題的時間為aT(n/b)
- ③設組合時間C(n)
- 則有遞歸方程總結為:
- T(n)=θ(1) if n<c
- T(n)=aT(n/b)+D(n)+C(n) if n≥c
- 技術細節(注意):
- 在聲明、求解遞歸式時,常常忽略向上取整、向下取整、邊界條件
- 邊界條件可忽略,這些細節一般只影響常數因子的大小,不改變量級。求解時,先忽略細節,然后再決定其是否重要!
## 大整數乘法
*********優化划分階段,降低 T(n)=aT(n/b) + f(n) 中的 a*********
這里我們假設有兩個大整數X、Y,分別設X=1234、Y=5678。現在要求X*Y的乘積,小學的算法就是把X與Y中的每一項去乘,但是這樣的乘法所需的時間復雜度為O(n^2),效率低下,我們可以嘗試使用分治來解決。
XY = (A2n/2 + B)(C2n/2 + D)
= AC2n + (AD+BC)2n/2 + BD
= AC2n + ((A-B)(D-C)+AC+BD)2n/2 + BD
- 算法分析:
- 首先將X和Y分成A,B,C,D
- 此時將X和Y的乘積轉化為上述式子,把問題轉化為求解式子的值
- 此時遞歸式為 T(n)=4T(n/2)+θ(n)
- 算法復雜度 T(n)=θ(n2)
- 繼續優化: AD+BC=(B-A)(C-D)+AC+BD
- 算法過程:
- 划分產生A,B,C,D;
- 計算 B-A 和 C-D;
- 計算 n/2 位乘法 AC、BD、(B-A)(C-D);
- 計算 (B-A)(C-D) + AC + BD;
- AC左移n位,((B-A)(C-D) + AC + BD) 左移n/2位;
- 計算XY
- 遞推式:
- T(n)=θ(1) if n=1
- T(n)=3T(n/2)+O(n) if n>1
- 算法復雜度: T(n)=O(nlog3) =O(n1.59)
## 矩陣乘法的Strassen算法
【矩陣相乘的朴素算法 T(n) = Θ(n3)】
朴素矩陣相乘算法,思想明了,編程實現簡單。時間復雜度是Θ(n^3)。偽碼如下
1 for i ← 1 to n 2 do for j ← 1 to n 3 do c[i][j] ← 0 4 for k ← 1 to n 5 do c[i][j] ← c[i][j] + a[i][k]⋅ b[k][j]
【矩陣相乘的strassen算法 T(n)=Θ(nlog7) =Θ (n2.81)】
一般算法需要八次乘法,四次加法;算法效率是Θ(n^3);
鑒於上面的分治法方案無法有效提高算法的效率,要想提高算法效率,由主定理方法可知必須想辦法將2中遞歸式中的系數8減少。Strassen提出了一種將系數減少到7的分治法方案,如下圖所示。
我們可以看到上面只有7次乘法和多次加減法,最終達到降低復雜度為O( nlg7 ) ~= O( n2.81 );
SQUARE-MATRIX-MULTIPLY-RECURSIVE(A,B) n=A.rows let C be a new n*n matrix if n==1 c11=a11*b11 else partition A, B and C as in equation(1) C11=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A11,B11) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A12,B21) C22=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A11,B12) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A12,B22) C21=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A21,B11) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A22,B21) C22=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A21,B22) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A22,B22) return C
## 快速傅里葉變換(FFT)
問題定義:
算法思想:
偽代碼:
遞歸方程:
算法復雜度: T(n) = θ(n logn)
## 基於分治的排序
【歸並排序】
歸並排序是分治思想的典型應用,
划分策略:根據中間點將數組集合划分成兩部分,不斷遞歸
合並策略:比較a[i]和b[j]的大小,若a[i]≤b[j],則將第一個有序表中的元素a[i]復制到r[k]中,並令i和k分別加上1;否則將第二個有序表中的元素b[j]復制到r[k]中,並令j和k分別加上1,如此循環下去,直到其中一個有序表取完,然后再將另一個有序表中剩余的元素復制到r中從下標k到下標t的單元。
MergeSort(A,i,j) Input: A[i,…,j] Output:排序后的A[i,…,j] 1. k ← (i+j)/2; 2. MergeSort(A,i,k); 3. MergeSort(A,k+1,j); 4. l←i; h ← k+1; t=i; //設置指針 5. While l≤k & h< j Do 6. IF A[l] < A[h] THEN B[t] ← A[l]; l ← l+1; t ← t+1; 7. ELSE B[t] ← A[h]; h ← h+1; t ← t+1; 8. IF l<k THEH //第一個子問題有剩余元素 9. For v ← l To k Do 10. B[t] ← A[v]; t ← t+1; 11. IF h<j THEN //第二個子問題有剩余元素 12. For v ← h To j Do 13. B[t] ← A[v]; t ← t+1; 14. For v ← i To j Do //將歸並后的數據復制到A中 15. A[v] ← B[v];
復雜度分析: T(n)=2T(n/2)+O(n) T(n)=O(nlogn)
復習:歸並排序具有如下特點:
- 歸並排序的時間復雜度為O(nlogn),這是基於比較的排序算法所能達到的最高境界;
- 歸並排序是一種穩定的算法,這一點在某些場景下至關重要;
- 歸並排序是最常用的外部排序方法(當待排序的記錄放在外存上,內存裝不下全部數據時,歸並排序仍然適用,當然歸並排序同樣適用於內部排序...);
- 但其也需要O(n)的輔助空間,而與之效率相同的快排和堆排分別需要O(logn)和O(1)的輔助空間,在同類算法中歸並排序的空間復雜度略高
【快速排序】
划分策略:選取一個記錄作為樞軸,經過一趟排序,將整段序列分為兩個部分,其中一部分的值都小於樞軸,另一部分都大於樞軸。
遞歸策略:然后繼續對這兩部分繼續進行排序,從而使整個序列達到有序。
合並策略:無操作
QuickSort(A,i,j) Input: A[i,…,j], x Output: 排序后的A[i,…,j] 1. temp←rand(i,j); //產生i,j之間的隨機數 2. x ← A[temp]; //以確定的策略選擇x 3. k=partition(A,i,j,x); //用x完成划分 4. QuickSort(A,i,k); //遞歸求解子問題 5. QuickSort(A,k+1,j);
Partition(A,i,j,x)
1. low←i ; high ←j;
2. While( low< high ) Do
3. swap(A[low], A[high]);
4. While( A[low] < x ) Do
5. low←low+1;
6. While( A[low] < x ) Do
7. high←high-1;
8. return(high)
平均、最優的時間復雜度為O(nlogn),最差的時間復雜度為O(n^2)
平均的空間復雜度為O(logn),最差的空間復雜度為O(n)
排序的下界是:Ω(n log n)
## 中位數和順序統計量
【最大值最小值】
算法MaxMin(A) 輸入: 數組A[i,…,j] 輸出:數組A[i,…,j]中的max和min 1. If j-i+1 =1 Then 輸出A[i],A[i],算法結束 2. If j-i+1 =2 Then 3. If A[i]< A[j] Then輸出A[i],A[j];算法結束 4. k←(j-i+1)/2 5. m1,M1 ←MaxMin(A[i:k]); 6. m2,M2 ←MaxMin(A[k+1:j]); 7. m ←min(m1,m2); 8. M ←max(M1,M2); 9. 輸出m,M
時間復雜度分析:T(n) = 3n/2 - 2
所以時間復雜度為:O( ⌊3n/2⌋ )
【中位數的線性時間選擇算法】
- 這種算法在最壞的情況下的時間復雜度為O(n),其具體過程如下:
- 將輸入數組划分為n/5組,每組有5個元素,且剩下的至多有一組的元素小於5個。
- 尋找這n/5個組中每個組的中位數,可以將每組做一次排序,然后選取每組的第三個元素。
- 對於第2部找出的n/5個中位數遞歸的調用Select函數求出其中位數x.(約定偶數個中位數為其較小的中位數)
- 按照找到的中位數x將數組划分為兩個部分,求得小於或者等於x的元素有q個
- 如果k==q則返回x,若k<q則在x的左區間找第k小的數,否則在x的有區間找第k-q大的數
Input: 數組A[1:n], 1≤i≤n Output: A[1:n]中的第i-大的數 1. for j←1 to n/5 2. InsertSort(A[(j-1)*5+1 : (j-1)*5+5]); 3. swap(A[j], A[[(j-1)*5+3]); 4. x ←Select(A[1: n/5], n/10 ); 5. k ←partition(A[1:n], x); 6. if k=i then return x; 7. else if k>i then retrun Select(A[1:k-1],i); 8. else retrun Select(A[k+1:n],i-k);
遞歸方程式:T(n) ≤ T( ⌈n/5⌉ ) +T(7n/10+6) + O(n)
時間復雜度:T(n) = O(n)
## 最鄰近點對
- 輸入:Euclidean空間上的n個點的集合Q
- 輸出:P1, P2∈Q, Dis(P1, P2)=Min{Dis(X, Y) | X, Y∈Q}
- 算法過程:
- 如果Q中僅包含一個點,則算法結束;
- 把Q中點分別按x-坐標值和y-坐標值排序.
- 划分:
- 計算Q中各點x-坐標的中位數m;
- 用垂線 L:x=m 把Q划分成兩個大小相等的子集合QL 和QR, QL中點在L左邊, QR 中點在L右邊.
- 求解:
- 遞歸地在QL、QR中找出最接近點對:(p1, p2)∈QL , (q1, q2)∈QR
- d=min{Dis(p1, p2), Dis(q1, q2)};
- 合並:
- 在臨界區查找距離小於d的點對(pl, qr), pl∈QL,qr∈QR;
- 如果找到,則(pl, qr)是Q中最接近點對,否則(p1, p2)和(q1, q2) 中距離最小者為Q中最接近點對
- 時間復雜度:
- Divide階段需要O(n)時間
- Conquer階段需要2T(n/2)時間
- Merge階段需要O(n)時間
- 遞歸方程
- T(n)= O(1) n = 2
- T(n) = 2T(n/2) + O(n) n ≥ 3
- 用Master定理求解T(n)
- T(n) = O(nlogn)
## 凸包