【算法學習筆記五】平攤分析

平攤分析

平攤分析是分析一個操作序列以顯示每個操作的平均成本很小的任何策略,即使序列中的單個操作可能很昂貴。不同於平均案例分析:1)不涉及概率;2)保證最壞情況下各操作的平均性能。

三種常見的平攤方法:聚集方法;會計方法;勢能方法。

一.聚集方法

聚集方法:先求n個操作總共花費的總和T(n),每一步操作的平攤代價T(n)/n,這個成本適用於每個操作,即使在序列中有幾種類型的操作。後面兩種方法可以將不同的平攤代價分配給不同的操作類型,即平攤到每一步的代價是不一樣的。

棧操作

棧中有三種操作,PUSH,POP和MULTIPOP(一次性連續取出多個元素)

MULTIPOP(S,k)
    while not STACK-EMPTY(S) and k!=0
        POP(S)
        k <- k-1
    endwhile

現在對棧總共進行n步操作,但三種操作的具體步數未知,PUSH的操作時間爲O(1),POP的操作時間爲O(1),MULTIPOP的操作時間是根據k的具體數值不定的,此時按最壞情況考慮,則操作時間爲O(n);進行n步操作,則最壞情況下的總操作代價爲\large O(n^2)。但因爲每個進棧的元素只能出棧一次,所以POP+MULTIPOP≤PUSH,作爲整體來看,總時間從\large O(n^2)降到了O(n),每一步的平攤代價爲O(1)。

二進制計數器

計數器實現:

INCREMENT(A)
    i <-0   
    while i<length[A] and A[i]=1
        A[i]=0
        i <- i+1
    endwhile
    if i<length[A]
    then A[i] <- 1

 

起始狀態爲0,進行n步Increment操作,當進行一次Increment操作,代價與翻轉的位數有關,例步驟0-1翻轉1位,步驟7-8,翻轉了4位,考慮最壞情況,假設是k位計數器,則1次操作最壞的代價爲\large \Theta(k),總共n步操作,總的代價爲\large \Theta(nk),但這個界偏高。

觀察圖中的規律可以發現,A[0]每一次操作都會翻轉1次,A[1]每2次操作都會翻轉1次,A[3]每4次都會翻轉1次,...,進行n步操作,A[0]翻轉n次,A[1]翻轉\large \left \lfloor n/2^ \right \rfloor次,A[2]翻轉\large \left \lfloor n/4 \right \rfloor次,以此類推,可得總的翻轉次數爲\large \sum_{i=0}^{\left \lfloor logn \right \rfloor}\left \lfloor n/2^i \right \rfloor,這個式子是小於\large n\sum_{i=0}^{\infty }1/2^i=2n,則總的代價是O(n),那麼每一步操作的平攤代價爲O(1)。

二.會計方法

將不同的費用(平攤成本)分配給不同的操作,有些操作的費用會比實際成本高或低。當一個操作的平攤成本超過它的實際成本時,這個差額被分配給數據結構中的特定對象作爲存款。存款可以在以後用於支付那些平攤成本低於實際成本的操作。注意:存儲在數據結構中的總存款不應該變成負數!

棧操作

棧操作中,PUSH操作代價爲1,POP操作代價爲1,MULTIPOP操作代價爲min(k,s)(k是取出的元素個數,s是棧中的元素個數);平攤成本分配,PUSH爲2,POP爲0,MULTIPOP爲0,因爲操作次數POP+MULTIPOP≤PUSH,所以這種平攤是可行的。對於任何n個PUSH、POP和MULTIPOP操作序列,總平攤代價是總實際代價的上限。因爲總成本是O(n),所以總實際成本也是O(n)。

二進制計數器

翻轉1位的代價是1,定義平攤成本,0變爲1代價爲2,其中1個代價支付0變爲1的實際成本,剩餘的1個代價今後用於支付1變爲0,所以每次increment的平攤成本最多爲2,計數器中1的個數一定是大於0的,且平攤成本也一定大於實際成本,即總存款爲正,所以n次increment操作的平攤成本爲O(n),實際成本也是O(n)。

三.勢能方法

將預付的工作表示爲“潛在的能量”或“潛在的”,可以用來支付未來的運作,類似於前面的存款。潛力是與整個數據結構相關聯的,而不是與數據結構中的特定對象相關聯的。

框架結構:1)初始的數據結構或者狀態\large D_0;2)操作i使得\large D_{i-1}\rightarrow D_i;3)第i步操作的成本爲\large c_i;4)定義勢能函數\large \Phi :{D_i}\rightarrow R,\Phi (D_0)=0,\Phi (D_i)\geq 0;5)平攤成本\large \widehat{c_i}=c_i+\Phi (D_i)-\Phi(D_{i-1})

1)\large \Delta \Phi _i=\Phi (D_i)-\Phi (D_{i-1})

如果\large \Delta \Phi _i>0,則\large \widehat{c_i}>c_i,第i步操作存儲勢能供以後使用;

如果\large \Delta \Phi _i<0,則\large \widehat{c_i}<c_i,數據結構釋放能量爲第i步操作支付;

2)平攤成本之和

\large \begin{align*} \sum_{i=1}^{n} \widehat{c_i}&= \sum_{i=1}^{n}(c_i+\Phi (D_i)-\Phi (D_{i-1}))\\ &= \sum_{i=1}^{n}c_i+\Phi (D_n)-\Phi (D_{0})\\ &\geq \sum_{i=1}^{n}c_i \end{align*}(因爲\large \Phi (D_0)=0,\Phi (D_i)\geq 0);

3)會計方法和勢能方法比較

會計方法 勢能方法
定義每個操作的代價 定義總體能量
計算餘額(存款) 計算每一步的代價

 

棧操作

PUSH操作,棧中元素s->s+1,\large \Delta \Phi _i=\Phi (D_i)-\Phi (D_{i-1})=1\large \widehat{c_i}=c_i+\Delta \Phi_i=1+1=2

POP操作,棧中元素s->s-1,\large \Delta \Phi _i=\Phi (D_i)-\Phi (D_{i-1})=-1\large \widehat{c_i}=c_i+\Delta \Phi_i=0

MULTIPOP操作,s->s-min(s,k),\large \Delta \Phi _i=-k\large \widehat{c_i}=c_i+\Delta \Phi_i=k-k=0

總的平攤成本爲O(n)。

二進制計數器

定義第i步增量操作後計數器的勢爲\large b_i,即計數器中1的個數。假設第i步增量重置(1->0)有\large t_i位。操作的實際成本最多是\large t_i+1。(多1是因爲最高位可能發生1->0,最多+1可能不做的因爲可能所有位都是0->1)

1)如果\large b_i=0,第i步increment操作重置(1->0)了k位,則\large b_{i-1}=t_i=k

2)如果\large b_i>0,\large b_i=b_{i-1}-t_i+1;(\large b_i>0排除全1增量的情況,那麼必存在0->1的情況,所以要+1)

不論上述哪種情況,都有\large b_i\leq b_{i-1}-t_i+1,那麼\large \Phi (D_i)-\Phi (D_{i-1})\leq (b_{i-1}-t_i+1)-b_{i-1}\large =1-t_i

平攤成本\large \widehat{c_i}=c_i+\Phi (D_i)-\Phi(D_{i-1})\leq (t_i+1)+(1-t_i)=2

 

四.會計方法分析動態表

一步操作的平攤成本\large \widehat{c_i}=\$3,其中$1支付當前的插入操作,$2存儲起來用於後面擴表($1支付移動當前元素到新表,$1支付移動前面的元素)。

當前表要插入新元素,表發生溢出,申請了一塊新的表(double),需要將舊錶中的元素移動到新表中,再在新表中添加新元素。$2不僅用於自身的移動,剩餘的$1支付前面其中$0的元素的移動;添加新元素,添加花費$1,剩餘$2存儲起來。

當16個數據的表填滿,其中與8個$0的元素,8個$2的元素,添加第17個元素時,發生溢出,申請一塊32塊的表,重複上面的步驟。

說明:第一個元素的平攤成本爲2,因爲其不需要支付前面移動的成本;紅框存款變化表示表發生了擴張。

五.勢能方法分析動態表

定義第i個元素插入後,表的勢能爲\large \Phi (D_i)=2i-2^{\left \lceil logi \right \rceil},假定\large 2^{\left \lceil log0 \right \rceil}=0(i表示表格中的元素個數,\large 2^{\left \lceil logi \right \rceil}表示了表格的規模),\large \Phi (D_0)=0,\Phi (D_i)\geq 0

\large i=2^k \ \ \ \ \ \ \ \ \ \ \Phi (D^i)=2\cdot 2^k-2^k=2^k

         \large 2^k+1 \ \ \ \ \Phi (D^i)=2\cdot (2^k+1)-2^{k+1}=2

         \large 2^k+2 \ \ \ \ \Phi (D^i)=4

         \large 2^k+2^k-1 \ \ \ \ \Phi (D^i)=2^{k+1}-2

         \large 2^{k+1} \ \ \ \ \ \ \ \Phi (D^i)=2^{k+1}

         \large 2^{k+1}+1} \ \ \Phi (D^i)=2

可以發現,i從一次表滿的情況到下一次表滿的情況,\large 2^k\rightarrow 2,2^{k+1}\rightarrow 2,此時都發生了表的擴張,表滿的時候,勢能達到一個極大值。

 平攤成本\large \widehat{c_i}=c_i+\Phi (D_i)-\Phi(D_{i-1})

\large i=2^k+1,圖中可發現此時是表擴張後添加元素,需要將前面舊錶中的元素都移動到新表中,則有\large \widehat{c_i}=i+\large (2i-2^{\left \lceil logi \right \rceil})-(2(i-1)-2^{\left \lceil log(i-1) \right \rceil})=3

\large i\neq 2^k+1,表格未發生擴張,實際成本爲1,則\large \widehat{c_i}=1+\large (2i-2^{\left \lceil logi \right \rceil})-(2(i-1)-2^{\left \lceil log(i-1) \right \rceil})=3

六.可擴張可壓縮的動態表

當表的負載因子(元素個數/表格規模)變得太小(<1/2)時收縮表。一個自然的策略是,當一個項被插入到一個完整的表中時,將表的大小增加一倍;當在滿表中刪除操作會導致表的容量減少一半時,將表的大小減半。這個策略保證了負載因素從未低於 1/2, 但這種操作的平攤成本爲\large \Theta(n^2)

原因:在一次擴展之後,我們沒有執行足夠的刪除來彌補一次收縮。在收縮之後,我們沒有執行足夠的插入來支付擴展。

一個新的策略:當一個項被插入到一個完整的表中時,將表的大小增加一倍;當刪除操作會導致表的滿度小於1/4時,將表的大小減半。動態表中任意n個操作序列的實際時間是O(n)。

令負載因子\large \alpha (T)=num(T)/size(T)(元素數量/表格規模),當\large T=\varnothing\large \alpha (T)=1

定義勢函數\large \Phi (T)=\left\{\begin{matrix} 2num(T)-size(T) &\alpha (T) \geq 1/2\\ size(T)/2-num(T) & \alpha (T) < 1/2 \end{matrix}\right.

插入操作:1)若\large \alpha ^{i-1}\geq 1/2,\widehat{c_i}\leq 3

2)\large \alpha ^{i-1}< 1/2,表格不會擴張:若\large \alpha _i< 1/2,\widehat{c_i}=0;若\large \alpha _i\geq 1/2,\widehat{c_i}<3

刪除操作:1)若\large \alpha ^{i-1}< 1/2,沒發生壓縮,\large \widehat{c_i}=2;若\large \alpha ^{i-1}< 1/2,併發生壓縮,\large \widehat{c_i}=1

2)\large \alpha ^{i-1}\geq 1/2,則不發生壓縮:當\large \alpha _i\geq 1/2,\widehat{c_i}=-1;當\large \alpha _i< 1/2,\widehat{c_i}\leq 2

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章