1. 程式人生 > >【3】分治法(divide-and-conquer)

【3】分治法(divide-and-conquer)

分治法

顧名思義,分治法是將一塊完整的領土分解為若干小的領土,然後一塊塊征服。

  • 分,把一個問題的例項劃分為若干個子問題(原問題規模變小)
  • 治,遞迴地解決每個子問題
  • 把每一個子問題的解整合為一個大問題的解

舉例

歸併排序(Merge sort)

  • 將待排序的陣列一分為二
  • 遞迴地對每一個子陣列排序
  • 合併兩個有序的子陣列為一個有序的陣列(線上性時間n內)

在這裡插入圖片描述

歸併排序可以用a=2,b=2,k=0的主方法求解

二分查詢方法(Binary search)

演算法描述:設某個元素為x,需要在一個已經排序的陣列中找到這個x。

  • 分,把x與陣列中間的元素比較
  • 治,如果x小於中間元素,取前一半陣列,否則取後一半,每次只在一個數組遞迴
  • 合併,無計算量

在這裡插入圖片描述

乘方問題(Powering a number)

演算法描述:給出一個數aa(實數或者浮點數)和正整數nn,計算ana^n。 傳統的Naive演算法直接按照ana^n的定義,nnaa連乘計算,演算法複雜度為Θ(n)\Theta(n)。 利用分治法:

    • nn為偶數,將nn分為n/2n/2,即an=an/2an/2a^n=a^{n/2}a^{n/2},兩個an/2a^{n/2}只要算一個就可以,所以子規模的規模變為n/2n/2
    • nn為奇數,將nn分為n/2n/2,即an=an1/2an1/2aa^n=a^{n-1/2}a^{n-1/2}a
      ,兩個an1/2a^{n-1/2}只要算一個就可以,所以子規模的規模變為n/2n/2
  • 治,類似二分查詢,只要在一部分遞迴即可
  • 合併

在這裡插入圖片描述

斐波那契數(Fibonacci numbers)

傳統的Naive演算法

傳統的Naive演算法按照定義向前逐步遞迴。 在這裡插入圖片描述

樸素平方遞迴式

斐波那契數列特性之一:ϕn/5\phi^n/\sqrt{5}取最近的整數就是第n個斐波那契數。即F(n)=[ϕn/5]F(n)=[\phi^n/\sqrt{5}],此時斐波那契數列變成了乘方問題。演算法複雜度為Θ(lgn)\Theta(lgn)

此演算法的問題在於,在計算的儲存中,用浮點數儲存ϕ

\phi5\sqrt{5},因此會影響到結果。 真正的平方遞迴式 利用特性同樣能將斐波那契問題轉化為乘方問題。演算法複雜度為Θ(lgn)\Theta(lgn)在這裡插入圖片描述 在這裡插入圖片描述

矩陣乘法(Matrix multiplication)

在這裡插入圖片描述

樸素演算法

樸素演算法複雜度Θ(n3)\Theta(n^3)

在這裡插入圖片描述

分治法

  • 分,矩陣分塊,如圖,分為8塊
  • 治,遞迴每一個子矩陣
  • 合併,將子矩陣合併 在這裡插入圖片描述

矩陣分為8個子矩陣,規模變為n/2n/2,合併的時間為矩陣相加的演算法複雜度為Θ(n2)\Theta(n^2),最終的複雜度為Θ(n3)\Theta(n^3),並沒有更優。 在這裡插入圖片描述

分治法 Strassen’s idea

分析思路:我們不在乎矩陣加法的演算法複雜度(n2n^2是可以接受的),演算法優化應該從分塊迭代部分考慮(減少乘法的次數)。 在這裡插入圖片描述

在這裡插入圖片描述

利用主方法的Case1情況求解,演算法複雜度為T(n)=Θ(nlg7)T(n) = \Theta(n^{lg 7}),比原來的Θ(n3)\Theta(n^3)大大優化(特別是在n較大的情況下效果顯著)。

超大規模積體電路(VLSI layout)

假設電路是個二叉樹,現在有個n個葉結點的完全二叉樹,演算法目的在於找到網格佔空間最小的方法。 在這裡插入圖片描述

改變佈局,充分利用空白區域。 在這裡插入圖片描述