iT邦幫忙

2021 iThome 鐵人賽

DAY 18
0

最壞情況為https://chart.googleapis.com/chart?cht=tx&chl=%5CTheta(n),BFPRT演算法

在由隨機數決定陣列的分割的情況下,我們如何避免產生出最差情況(雖然出現的機率很小),或是讓最差的情況時間複雜度也是https://chart.googleapis.com/chart?cht=tx&chl=%5CTheta%20(n)

BFPRT演算法(由 Blum, Floyd, Pratt, Rivest 與 Tarjan 創造)可以實現出這一件事情,這是一個具有確定性(deterministic)的演算法,也就是不含任何隨機的成分。

我們會產生出最差的情況為分割極度不平衡,也就是產生出https://chart.googleapis.com/chart?cht=tx&chl=0%3An-1這種分割,而會產生出這種分割,和我們的主元(pivot)的選擇很有關係,如果我們可以保證選擇到的主元(pivot)都是好的,確保不會產生出最差分割,這個主元是確定是最好的,也就是在樣本空間無限大時,每一次的主元都是好的,而不是機率上趨近於最好。那麼這個演算法就可以避免掉最差情況的發生。而這就是BFPRT演算法的主要想法。

BFPRT演算法遵循以下五個步驟

  1. 將n個元素的https://chart.googleapis.com/chart?cht=tx&chl=A陣列(不一定要是陣列)拆分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D組,每一組含有5個元素,最後一組由https://chart.googleapis.com/chart?cht=tx&chl=n%5C%20%7Bmod%7D%5C%205組成,如果https://chart.googleapis.com/chart?cht=tx&chl=n%3D1,則直接回傳https://chart.googleapis.com/chart?cht=tx&chl=A%5Bn%5D
  2. 尋找這https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D組中每一組的中位數,方法為對每一組元素使用insertion sort,排序後找出中位數,每一組都有五個元素,因此經過排序後地三個元素即為中位數。
  3. 在2.中會找出https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D個中位數,把這些中位數也組成一個陣列,在這個陣列中找到中位數https://chart.googleapis.com/chart?cht=tx&chl=x(透過遞迴呼叫BFPRT)。
  4. 在3.產生出由中位數產生的陣列,以https://chart.googleapis.com/chart?cht=tx&chl=x作為主元(pivot)對陣列進行劃分。比https://chart.googleapis.com/chart?cht=tx&chl=x小的元素劃分到https://chart.googleapis.com/chart?cht=tx&chl=x的左邊,https://chart.googleapis.com/chart?cht=tx&chl=x本身被劃分到左邊,而比https://chart.googleapis.com/chart?cht=tx&chl=x大的元素劃分到左邊。
  5. https://chart.googleapis.com/chart?cht=tx&chl=k定義為https://chart.googleapis.com/chart?cht=tx&chl=k%20%3D%20rank(x),也就是將https://chart.googleapis.com/chart?cht=tx&chl=k當作https://chart.googleapis.com/chart?cht=tx&chl=x在陣列中大小的排名,而輸入的參數https://chart.googleapis.com/chart?cht=tx&chl=i表示我們要尋找陣列中第https://chart.googleapis.com/chart?cht=tx&chl=i大的元素。
    如果https://chart.googleapis.com/chart?cht=tx&chl=i%3Dk,則直接回傳https://chart.googleapis.com/chart?cht=tx&chl=x
    如果https://chart.googleapis.com/chart?cht=tx&chl=i%20%3C%20k,則在陣列中左分割的部分,也就是比https://chart.googleapis.com/chart?cht=tx&chl=x小的部分遞迴呼叫BFPRT
    如果https://chart.googleapis.com/chart?cht=tx&chl=i%20%3E%20k,則在陣列中右分割的部分,也就是比https://chart.googleapis.com/chart?cht=tx&chl=x大的部分遞迴呼叫BFPRT

範例 : 輸入一個有34個元素的A陣列,https://chart.googleapis.com/chart?cht=tx&chl=A%5B34%5D

  1. 將n個元素的https://chart.googleapis.com/chart?cht=tx&chl=A陣列拆分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D組,每一組含有https://chart.googleapis.com/chart?cht=tx&chl=5個元素,最後一組由https://chart.googleapis.com/chart?cht=tx&chl=n%5C%20%7Bmod%7D%5C%205個元素組成。
  2. 尋找這https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D%3D7組中每一組的中位數
  3. 在2.中會找出https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clceil%7Dn%2F5%7B%5Crceil%7D%3D8個中位數,把這些中位數也組成一個陣列,在這個陣列中找到中位數https://chart.googleapis.com/chart?cht=tx&chl=x(透過遞迴呼叫BFPRT)。
  4. 在3.產生出由中位數產生的陣列,以https://chart.googleapis.com/chart?cht=tx&chl=x作為主元(pivot)對陣列進行劃分。比https://chart.googleapis.com/chart?cht=tx&chl=x小的元素劃分到https://chart.googleapis.com/chart?cht=tx&chl=x的左邊,https://chart.googleapis.com/chart?cht=tx&chl=x本身被劃分到左邊,而比https://chart.googleapis.com/chart?cht=tx&chl=x大的元素劃分到左邊。

    https://chart.googleapis.com/chart?cht=tx&chl=x的排名為第3名,令https://chart.googleapis.com/chart?cht=tx&chl=k%3Drank(x),中位數組成的子陣列有7個元素,https://chart.googleapis.com/chart?cht=tx&chl=n%3D7,右邊的分割有https://chart.googleapis.com/chart?cht=tx&chl=n-k%3D4個元素,左邊分割有https://chart.googleapis.com/chart?cht=tx&chl=n-k-1%3D3個元素
  5. 接著通過遞迴呼叫找到我們要的元素

我們把上面這個陣列看作是一張圖(graph),我們在兩個節點(或是元素)定義以下符號,表示https://chart.googleapis.com/chart?cht=tx&chl=a%20%3C%20b

我們把這樣的符號,加到上面的圖上

我們也對中位數構成的子陣列加上這樣的符號

得到這張非常混亂,就像是我的麵包版的圖

我們使用箭頭,表示是一個有向路徑,也就是我們走訪的路線,我們可以透過任一條路徑,得知兩個元素之間的大小關係,且通過這個路徑,我們可以知道陣列每一個區塊和https://chart.googleapis.com/chart?cht=tx&chl=x之間的關係。

由箭頭我們可以知道https://chart.googleapis.com/chart?cht=tx&chl=a%20%3C%20bhttps://chart.googleapis.com/chart?cht=tx&chl=b%20%3C%20x,因此https://chart.googleapis.com/chart?cht=tx&chl=a%20%3C%20xhttps://chart.googleapis.com/chart?cht=tx&chl=a上面的元素也必定小於https://chart.googleapis.com/chart?cht=tx&chl=x,旁邊的分組也是如此,因此我們可以知道,灰色區域框住的陣列區塊中所有元素皆小於等於https://chart.googleapis.com/chart?cht=tx&chl=x

而橘色部分中所有元素,必定大於等於https://chart.googleapis.com/chart?cht=tx&chl=x

由上面這張圖我們可以知道,每一個分組中有https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205的元素會小於等於https://chart.googleapis.com/chart?cht=tx&chl=x,而我們將這個含有https://chart.googleapis.com/chart?cht=tx&chl=34個元素的陣列拆分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2B1組(https://chart.googleapis.com/chart?cht=tx&chl=%2B1為含https://chart.googleapis.com/chart?cht=tx&chl=n%20%5C%20mod%5C%205個元素的組),其中有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D的組會存在https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205的元素會小於等於https://chart.googleapis.com/chart?cht=tx&chl=x這個性質,因此,我們可以推導出以下性質:

給定https://chart.googleapis.com/chart?cht=tx&chl=n個元素的https://chart.googleapis.com/chart?cht=tx&chl=A陣列 :

  • https://chart.googleapis.com/chart?cht=tx&chl=3%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D個元素小於等於https://chart.googleapis.com/chart?cht=tx&chl=x,(在由中位數劃分出的子陣列中,有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2個元素小於等於https://chart.googleapis.com/chart?cht=tx&chl=x)

而每一個分組中,也至少會有https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205個元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x,而我們將含有https://chart.googleapis.com/chart?cht=tx&chl=34個元素的陣列猜分成https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%3D6%2B1組,其中有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D%2B2會具有https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%203%205的元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x這個性質,

給定https://chart.googleapis.com/chart?cht=tx&chl=n個元素的https://chart.googleapis.com/chart?cht=tx&chl=A陣列 :

  • https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%203%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%2B2%7B%5Crfloor%7D個元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x,(在由中位數劃分出的子陣列中,有https://chart.googleapis.com/chart?cht=tx&chl=%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%2B2個元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x)

BFPRT效率分析

由上面的推論,我們可以得到以下性質

  • https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%203%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%7B%5Crfloor%7D%20%3E%3D%20%5Cfrac%20%7B3n%7D%20%7B10%7D個元素小於等於https://chart.googleapis.com/chart?cht=tx&chl=x
  • https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%203%7B%5Clfloor%7D%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D%2F2%20%2B%202%7B%5Crfloor%7D%20%3E%3D%20%5Cfrac%20%7B3n%7D%20%7B10%7D%20%2B%206個元素大於等於https://chart.googleapis.com/chart?cht=tx&chl=x

由上面這個性質我們可以知道,最多我們可以產生出一個https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%200%3A%5Cfrac%20%7B3n%7D%20%7B10%7D%20%2B%20%5Cfrac%20%7B3n%7D%20%7B10%7D%20%2B%206個元素的分割情況,為了求上界,我們簡化成最多產生出https://chart.googleapis.com/chart?cht=tx&chl=0%3A%5Cfrac%20%7B7n%7D%20%7B10%7D的分割情況,也就是在第5步中,BFPRT遞迴呼叫做多作用在https://chart.googleapis.com/chart?cht=tx&chl=%5Cfrac%20%7B7n%7D%20%7B10%7D個元素上。

下面推導BFPRT演算法最壞情況的執行時間https://chart.googleapis.com/chart?cht=tx&chl=T(n)

  1. 拆分陣列需要https://chart.googleapis.com/chart?cht=tx&chl=O(n)
  2. 固定陣列大小的排序,需要的時間是固定的,因此為https://chart.googleapis.com/chart?cht=tx&chl=O(n)
  3. 遞迴呼叫需要https://chart.googleapis.com/chart?cht=tx&chl=T(%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D)
  4. 劃分需要https://chart.googleapis.com/chart?cht=tx&chl=O(n)
  5. 遞迴呼叫最多需要https://chart.googleapis.com/chart?cht=tx&chl=T(%5Cfrac%20%7B7n%7D%20%7B10%7D)

因此,https://chart.googleapis.com/chart?cht=tx&chl=T(n)的上限為以下關係式
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20T(n)%3C%3DT(%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D)%2BT(%5Cfrac%20%7B7n%7D%20%7B10%7D)%2BO(n),使用代換法求解

假設https://chart.googleapis.com/chart?cht=tx&chl=T(n)%3C%3Dcn,則
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20T(n)%3C%3DT(%7B%5Clfloor%7Dn%2F5%7B%5Crfloor%7D)%2BT(%5Cfrac%20%7B7n%7D%20%7B10%7D)%2BO(n)
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20%5C%5C%3D1%2F5cn%2B7%2F10cn%2BO(n)
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20%5C%5C%3D9%2F10*cn%2BO(n)
https://chart.googleapis.com/chart?cht=tx&chl=%5Cdisplaystyle%20%5C%5C%3Dcn-(1%2F10*cn-O(n)),如果常數https://chart.googleapis.com/chart?cht=tx&chl=c足夠大,則https://chart.googleapis.com/chart?cht=tx&chl=(1%2F10*cn-O(n))為非負的項,觀察可以發現,只要我們https://chart.googleapis.com/chart?cht=tx&chl=chttps://chart.googleapis.com/chart?cht=tx&chl=10的任意倍數,就可以實現了,而當https://chart.googleapis.com/chart?cht=tx&chl=n%20%3C%3D%2050,則https://chart.googleapis.com/chart?cht=tx&chl=T(n)https://chart.googleapis.com/chart?cht=tx&chl=O(1),如果https://chart.googleapis.com/chart?cht=tx&chl=c足夠大,則https://chart.googleapis.com/chart?cht=tx&chl=T(n)的上限就是https://chart.googleapis.com/chart?cht=tx&chl=cn,也就是線性時間。


上一篇
Day-17 中位數與順序統計
下一篇
Day-19 ADT與鏈結串列(linked list)
系列文
從0開始啃Introduction of algorithms心得與記錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言