算法概論筆記 - 分治法

  1. 將原問題分解為一組子問題,每個(gè)子問題都與原問題類型相同,但是比原問題的規(guī)模小
  2. 遞歸求解這些子問題
  3. 將子問題的求解結(jié)果恰當(dāng)合并,得到原問題的解

分治算法更多地是使已經(jīng)能在多項(xiàng)式時(shí)間內(nèi)解決的問題求解得更快。

二進(jìn)制乘法

假設(shè)x和y是兩個(gè)n位二進(jìn)制整數(shù),我們將每個(gè)數(shù)都一分為二,每個(gè)數(shù)的左半部分和右半部分都是n/2位二進(jìn)制數(shù):

![](http://latex.codecogs.com/svg.latex?xy = (2^{n/2}x_L + x_R)(2^{n/2}y_L + y_R) = 2^nx_Ly_L + 2^{n/2}(x_Ly_R + x_Ry_L) + x_Ry_R)

此時(shí),T(n) = 4T(n/2) + O(n),時(shí)間復(fù)雜度為O(n^2)

![](http://latex.codecogs.com/svg.latex?x_Ly_R + x_Ry_L = (x_L + x_R)(y_L + y_R) - x_Ly_L - x_Ry_R)

這時(shí),T(n) <= 3T(n/2 + 1) + O(n),時(shí)間復(fù)雜度為
矩陣乘法

兩個(gè)nn的矩陣X和Y的乘積得到另一個(gè)nn的矩陣Z=XY

直接計(jì)算

時(shí)間復(fù)雜度=![](http://latex.codecogs.com/svg.latex?n^2*O(n) = O(n^3)?)

元素?cái)?shù)目*每個(gè)元素的計(jì)算時(shí)間

分治優(yōu)化

利用矩陣乘法能夠分塊進(jìn)行的特性

![](http://latex.codecogs.com/svg.latex?X =\begin{pmatrix}A & B\C & D\\end{pmatrix})

![](http://latex.codecogs.com/svg.latex?Y =\begin{pmatrix}E & F\G & H\\end{pmatrix})

從而,

![](http://latex.codecogs.com/svg.latex?XY =\begin{pmatrix}A & B\C & D\\end{pmatrix}\begin{pmatrix}E & F\G & H\\end{pmatrix}=\begin{pmatrix}AE+BG & AF+BH\CE+DG & CF+DH\\end{pmatrix}=\begin{pmatrix}P_5+P_4-P_2+P_6 & P_1+P_2\P_3+P_4 & P_1+P_5-P_3-P_7\\end{pmatrix})
其中,
![](http://latex.codecogs.com/svg.latex?\quad P_1=A(F-H)\quad P_2=(A+B)H\quad P_3=(C+D)E\quad P_4=D(G-E)\quad P_5=(A+D)(E+H)\quad P_6=(B-D)(G+H)\quad P_7=(A-C)(E+F))

算法T(n) = 7T(n/2) + O(n^2),根據(jù)主定理可得,

時(shí)間復(fù)雜度=
遞歸樹視角:

算法的遞歸調(diào)用構(gòu)成一個(gè)樹狀結(jié)構(gòu)。在深度為k的層次上,共有

個(gè)子問題,每一個(gè)的規(guī)模都是
,該層次花費(fèi)的時(shí)間為
。在
的層次上,子問題的規(guī)模降為1,此時(shí)![](http://latex.codecogs.com/svg.latex?(\frac32)^{log_2n}O(n) = 3^{log_2n} = n^{log_23})

換底公式![](http://latex.codecogs.com/svg.latex?log_ab = \frac {log_cb}{log_ca})

對(duì)于二進(jìn)制乘法,通常不需要將子問題的規(guī)模降至1。對(duì)于大多數(shù)處理器而言,16位或32位的二進(jìn)制乘法都被視為一次單獨(dú)的操作

通用模式

在解決規(guī)模為n的問題時(shí),總是先遞歸地求解a個(gè)規(guī)模為n/b的子問題,然后再

時(shí)間內(nèi)將子問題的解合并起來,其中a>0,b>1,d>=0是一些特定的整數(shù)。
此時(shí),主定理:![](http://latex.codecogs.com/svg.latex?T(n) = aT(\lceil n/b \rceil) + O(n^d))
時(shí)間復(fù)雜度為
![](http://latex.codecogs.com/svg.latex?T(n) =\begin{cases}O(n^d) & if ; d > log_ba \O(n^dlogn) & if ; d = log_ba \O(n^{log_ba}) & if ; d < log_ba \\end{cases})

歸并排序

將該數(shù)的序列分為兩部分,遞歸地對(duì)每一部分進(jìn)行排序,最后將兩個(gè)有序子序列進(jìn)行合并

merge
function merge(x[1...k], y[1...l])
if k=0: return y[1...l]
if l=0: return x[1...k]
if x[1] <= y[1]:
    return x[1]+merge(x[2...k],y[1...l])
else:
    return y[1]+merge(x[1...k],y[2...l])

merge()時(shí)間復(fù)雜度為O(k+l),即線性時(shí)間
mergesort()滿足T(n) = 2T(n/2) + O(n),根據(jù)主定理可得時(shí)間復(fù)雜度為O(nlogn)

merge()的實(shí)現(xiàn)通常面臨兩個(gè)選擇:

  1. 線性附加內(nèi)存,花費(fèi)將數(shù)據(jù)拷貝到臨時(shí)數(shù)組再拷貝回來的附加工作
  2. 交換位置(類似插入排序),若y半段對(duì)應(yīng)元素較小,則面臨將x半段對(duì)應(yīng)元素至x半段末尾的元素的這一子段全體右移一位的代價(jià)

做法2可能會(huì)使歸并排序退化為插入排序,因此通常選擇線性附加內(nèi)存

function merge(x[1...k], y[1...l])
init empty z[1...k+l]
xPos, yPos, zPos -> 1

while xPos <= k && yPose <= l:
    if x[xPos] <= y[yPos]:
        z[zPos++] = x[xPos++]
    else:
        z[zPos++] = y[yPos++]:

while xPos <= k
    z[zPos++] = x[xPos++]
while yPos <= l
    z[zPos++] = y[yPos++]
    
copy temp array z back

任一時(shí)刻只需要一個(gè)臨時(shí)數(shù)組,因此該臨時(shí)數(shù)組可以僅存有一個(gè),merge()使用該臨時(shí)數(shù)組的任意部分

遞歸版
function mergesort(a[1...n])
Input: An array of number a[1...n]
Output: A sorted version of this array

if n > 1:
    return merge(mergesort(a[1...n/2]),
                mergesort(a[n/2+1...n]))
else:
    return a

see implement: divide.MergeSortRecur

迭代版

可以發(fā)現(xiàn),合并操作直到遞歸進(jìn)入到單元素?cái)?shù)組的層次時(shí)才真正開始,1->2->4->8...依次類推(類似自底向上)

function iterative-mergesort(a[1...n])
Input: elements a1, a2, ..., an to be sorted

Q = [] (an empty queue whose elment's type is array)
for i = 1 to n:
    inject(Q, [ai])
while |Q| > 1:
    inject(Q, merge(eject(Q), eject(Q)))
return eject(Q)

see implement: divide.MergeSortIter

擴(kuò)展

在Java的泛型排序(使用Comparator)中,進(jìn)行一次元素比較可能比較昂貴(因?yàn)楸容^可能不容易被內(nèi)嵌,從而動(dòng)態(tài)調(diào)度的開銷可能會(huì)減慢執(zhí)行的速度),但是移動(dòng)元素則是省時(shí)的(因?yàn)樗鼈兪且玫馁x值,而不是龐大對(duì)象的拷貝)。歸并算法使用所有流行的排序算法中最少的比較次數(shù),因此,它就是標(biāo)準(zhǔn)Java類庫(kù)中泛型排序所使用的的算法。

通過兩兩元素之間的比較進(jìn)行排序,必須要執(zhí)行O(nlogn)次比較操作。
原因
通過兩兩元素之間的比較進(jìn)行排序的算法可以通過樹結(jié)構(gòu)來描述,樹的每個(gè)葉節(jié)點(diǎn)都標(biāo)記一個(gè)關(guān)于原輸入元素序列的排列。從樹根節(jié)點(diǎn)到樹葉節(jié)點(diǎn)的最長(zhǎng)路徑上的比較次數(shù)為該算法時(shí)間復(fù)雜度的最差情況。

該二叉樹至少包含有n!個(gè)葉節(jié)點(diǎn)(排列數(shù)目),因此這棵樹的寬度至少是

,因此最差情況下必須要執(zhí)行O(nlogn)次比較操作,即算法復(fù)雜度為O(nlogn)

選擇S中第k小元素
普通策略
  1. 排序問題:對(duì)S進(jìn)行排序,返回相應(yīng)位置k的元素
  2. 取S中k個(gè)最小數(shù)的問題:將S中前k個(gè)元素讀入(某數(shù)據(jù)結(jié)構(gòu))并以遞減順序?qū)ζ溥M(jìn)行排序。接著,逐個(gè)讀入剩下的元素,若該元素大于第k個(gè)元素,則忽略它;否則將其放至(某數(shù)據(jù)結(jié)構(gòu))中正確的位置,同時(shí)將第k個(gè)元素?cái)D出。當(dāng)算法終止時(shí),位于第k個(gè)位置上的元素作為答案返回

其中某數(shù)據(jù)結(jié)構(gòu)可以是數(shù)組、二叉堆、等等

分治策略

對(duì)于任意給定的數(shù)v,S中的數(shù)被分成三組:

  1. ,比v小的數(shù)

  2. ,與v相等的數(shù)

  3. ,比v大的數(shù)
    搜索范圍縮小,轉(zhuǎn)而在S的三個(gè)子集中進(jìn)行:
    ![](http://latex.codecogs.com/svg.latex?selection(S, k) =\begin{cases}selection(S_L, k) & if ; k<=|S_L| \v & if ; |S_L| < k <= |S_L| + |S_V| \selection(S_R, k-|S_L|-|S_V|) & if ; k > |S_L| + |S_V| \\end{cases})

在理想情況下,算法T(n) = T(n/2) + O(n),時(shí)間復(fù)雜度為O(n)

基準(zhǔn)v的選擇see also 快速排序

分治策略的實(shí)現(xiàn)

由于需要多維護(hù)一個(gè)

,partition()中將S分為
,
,

三個(gè)子集不易實(shí)現(xiàn)。


其中基準(zhǔn)v為5,指針l左側(cè)l為比基準(zhǔn)v小的數(shù),而指針m左側(cè)為不超過基準(zhǔn)的數(shù)。當(dāng)分割時(shí)遇到比基準(zhǔn)小的數(shù)時(shí),需要將

兩個(gè)子集整體向右移動(dòng)一位,耗費(fèi)極大時(shí)間

因此,實(shí)現(xiàn)時(shí)可將上述的三組放寬為:

  1. ,不超過v的數(shù)

  2. v
  3. ,不小于v的數(shù)

顯然,該變形不改變正確性

see implement: divide.FindKMin

快速傅里葉(Fourier)變換
值表示法

多項(xiàng)式具有如下性質(zhì)

一個(gè)d次多項(xiàng)式被其在d+1個(gè)不同點(diǎn)處的取值所唯一確定
如:d=1時(shí),即任意兩點(diǎn)確定一條直線

該性質(zhì)引出d次多項(xiàng)式的值表示法。

因此,對(duì)于一個(gè)d次多項(xiàng)式
![](http://latex.codecogs.com/svg.latex?A(x) = a_0 + a_1x^1 + a_2x^2 + ... + a_dx^d)
有如下兩種表示法(該表示法能夠唯一確定該多項(xiàng)式):

  1. 系數(shù)表示法:多項(xiàng)式的系數(shù)a_0, a_1, a_2 .... a_d
  2. 值表示法:A(x_0), A(x_1), A(x_2) .... A(x_d)的值

在值表示法下,對(duì)于多項(xiàng)式相乘問題,只要把

相乘,即可得到
的值,多項(xiàng)式相乘成為線性問題

在多項(xiàng)式乘法中,只要將多項(xiàng)式的變量x換成基數(shù)2,并留意進(jìn)位,即可得到二進(jìn)制乘法
多項(xiàng)式乘法的應(yīng)用:信號(hào)處理
系數(shù)表示法和值表示法可以相互轉(zhuǎn)換,系數(shù)到值的過程稱為計(jì)算,值到系數(shù)的過程稱為插值

求解多項(xiàng)式相乘(A*B=C)

計(jì)算這步時(shí)間復(fù)雜度為

。

若對(duì)![](http://latex.codecogs.com/svg.latex?x_0, x_1, ..., x{n-1})的選取有一定技巧,則可使計(jì)算過程之間產(chǎn)生重復(fù)步驟,從而節(jié)省算法的時(shí)間??焖?/p>

變換就是基于此將時(shí)間復(fù)雜度降為

若選擇它們?yōu)檎?fù)數(shù)對(duì),即![](http://latex.codecogs.com/svg.latex?+-x_0, +-x_1, ...., +-x_{n/2-1})。若以

為例,將它的奇次冪和偶次冪分離,則,

![](http://latex.codecogs.com/svg.latex?= (3+4x+6x^2) + x(4+2x2+10x4) = A_e(x^2) + xA_o(x^2))

則,

![](http://latex.codecogs.com/svg.latex?A(x_i) = A_e(x_i^2) + x_iA_o(x_i^2))

![](http://latex.codecogs.com/svg.latex?A(-x_i) = A_e(x_i^2) - x_iA_o(x_i^2))

若對(duì)于正負(fù)數(shù)對(duì)的使用從遞歸頂層一直到到底層,那么其運(yùn)算時(shí)間滿足

![](http://latex.codecogs.com/svg.latex?T(n) = 2T(n/2) + O(n))

假設(shè)我們底層選擇的數(shù)選擇為1,那么遞歸頂層選擇的n個(gè)數(shù),它們應(yīng)該是,1的n次復(fù)根,即等式![](http://latex.codecogs.com/svg.latex?z^n = 1) 的n個(gè)復(fù)數(shù)解。

復(fù)根的理解如下:

插值TODO
寫在最后
  • 立個(gè)Flag,TODO will be done some day。
  • 渣代碼,且輕噴?:worried:?。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 算法和數(shù)據(jù)結(jié)構(gòu) [TOC] 算法 函數(shù)的增長(zhǎng) 漸近記號(hào) 用來描述算法漸近運(yùn)行時(shí)間的記號(hào),根據(jù)定義域?yàn)樽匀粩?shù)集$N=...
    wxainn閱讀 1,237評(píng)論 0 0
  • 1 序 2016年6月25日夜,帝都,天下著大雨,拖著行李箱和同學(xué)在校門口照了最后一張合照,搬離寢室打車去了提前租...
    RichardJieChen閱讀 5,373評(píng)論 0 12
  • 概述:排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,823評(píng)論 0 15
  • #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((...
    keelZJP閱讀 212評(píng)論 0 0

友情鏈接更多精彩內(nèi)容