【算法複習】分治算法

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*********ide

  這裏咱們假設有兩個大整數X、Y,分別設X=123四、Y=5678。如今要求X*Y的乘積,小學的算法就是把X與Y中的每一項去乘,可是這樣的乘法所需的時間複雜度爲O(n^2),效率低下,咱們能夠嘗試使用分治來解決。函數

 XY = (A2n/2 + B)(C2n/2 + D)優化

= AC2n + (AD+BC)2n/2 + BDui

= AC2n + ((A-B)(D-C)+AC+BD)2n/2 + BDidea

  • 算法分析:
    • 首先將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)】spa

樸素矩陣相乘算法,思想明瞭,編程實現簡單。時間複雜度是Θ(n^3)。僞碼以下3d

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)

 

## 凸包

相關文章
相關標籤/搜索