算法導論之攤還分析學習筆記


基本原理

攤還分析(amortized analysis)中, 通過求數據結構的一個操作序列中的所有操作的平均執行時間, 來評價操作的代價. 攤還分析不同於平均情況分析, 它並不涉及概率, 可以保證最壞情形每個操作平均性能.
常用的三種技術: 聚合分析(aggregate analysis), 記賬法(accounting method)和勢能法(potential method).
聚合分析用來確定一個序列(內有 n 個操作)的總代價的上界 T(n). 因而每個操作的平均代價為 T(n)/n. 聚合分析方法將平均代價作為每個操作的攤還代價, 即所有操作(可能包含多個類型的操作)具有相同的攤還代價.
記賬法勢能法都可用來分析每個操作的攤還代價. 當存在不止一種類型的操作時, 其攤還代價可能不同.

記賬法將序列中某些較早的操作的余額(overcharge)作為預付信用(prepaid credit)儲存起來, 與數據結構中的特定對象相關聯, 在操作序列中的隨后部分, 儲存的信用即可用來為那些繳費少於實際代價的操作支付差額. 賦予一個操作的費用稱為這個操作的攤還代價. 用記賬法(accounting method)進行攤還分析時, 不同的操作可能有不同的攤還代價, 某些操作的攤還代價可能多於或少於其實際代價. 當一個操作的攤還代價超過其實際代價時, 將其差額存入數據結構中的特定對象, 存入的差額稱為信用(credit).

勢能法也是通過較早操作的余額來補償稍后操作的差額. 與記賬法不同, 勢能法將余額作為一個整體(即數據結構的勢能)存儲, 而不是將信用與數據結構中單個對象關聯並分開存儲. 勢函數將每個數據結構映射到一個實數, 此值即為關聯到該數據結構的. 注意, 是非負的, 但第 i 個操作的勢差可能是負的, 從而導致攤還代價為負.

示例: 動態表(Dynamic Tables)

應用攤還分析方法分析動態擴張收縮表(動態表, dynamic table)的問題.

學習目標
證明, 雖然動態表的插入(TABLE-INSERT)和刪除(TABLE-DELETE)操作可能引起表的擴張或收縮, 有較高的實際代價,但它們的攤還代價都是O(1);
而且, 如何保證動態表中的空閑空間相對於總空間的比例永不超過某個常數分數.

(slot)定義為保存一個數據項的空間;
非空表 T (non-empty table)的的裝載因子 α(T) 定義為表中存儲的數據項的數量除以表的規模(槽的數量); 對於空表(沒有數據項), 其規模為 0, 將其裝載因子(load factor)設定為 1.

TABLE-INSERT 操作將一個數據項插入表中, 占用一個槽; TABLE-DELETE 操作從表中刪除一個數據項;
表擴張(table expansion)是由於對於某些應用程序, 無法預知會有多少個對象存儲在表中. 我們會先分配一定的內存空間, 當發現不夠用時, 必須為其重新分配更大的空間, 並將所有對象從原表復制到新的空間. 釋放原有的空間; 簡單說, 擴張表(expand table)通過分配一個包含更多槽的新表來實現.
表收縮(table contraction)是從表中刪除了很多對象后, 為提高內存空間的利用率, 值得為其重新分配更少的空間. 具體來說, 當表中的數據項下降得太少時, 首先分配一個新的更少的表空間, 然后將數據項從舊表復制到新表中, 之后釋放舊表占用的內存空間, 將其歸還內存管理系統.
理想情況下, 希望保持兩個性質:

  • 動態表的裝載因子有一個正的常數的下界;
  • 一個表操作的攤還代價有一個常數下界. 

當對滿表(full table)進行插入數據項操作時, 需要將表的規模(table size)加倍, 以存放新的數據項, 同時又避免了頻繁重新分配內存.
常用的啟發式策略(heuristic)是: 為新表分配兩倍於舊表的槽(內存空間). 如果只允許插入操作, 那么裝載因子總會保持在1/2以上, 因此, 浪費的空間永遠不會超過總空間的一半.

TABLE-INSERT 操作的偽代碼, 如下:

其中, T 是一個對象, 對應表. 屬性 T.table 保存指向表的存儲空間的指針; T.num 保存表中的數據項數量; T.size 保存表的規模(槽數, the total number of slots in a table). 初始時, 令表為空: T.num = T.size = 0.

只有表擴張(TABLE-INSERTION)這一種操作時, 在執行 n 個 TABLE-INSERTION 操作的過程中, 僅當 i-1 恰為 2 的冪時, 第 i 個操作才會引起1次表擴張, 下面使用上述的3種方法來分析:
采用聚合分析法, 第 i 個操作的代價為

所以, n 個 TABLE-INSERTION 操作的總代價為

采用記賬法分析, 一次 TABLE-INSERTION 操作的攤還代價為 3. 直觀上, 可以理解為處理每個數據項需付出3次基本插入操作的代價: 將當前數據項插入到當前表中; 表擴張時, 移動本數據項的代價; 表擴張時, 移動另一個已經移動過的數據項的代價.

采用勢能法分析 n 個TABLE-INSERTION 操作序列. 定義勢函數Φ,在表擴張后,其值為0; 表滿時, 其值為表的規模, 以用來支付下次擴張表的代價. 勢函數可以定義為

其中, T 是一個對應表的對象; T.num 為表中數據項數量; T.size 為表的規模(槽數). 當進行一次表擴張后, 有 T.num = T.size/2, 此時有 Φ(T) = 0; 而在表擴張之前, 有 T.num = T.size, 此時 Φ(T) = T.num. 勢函數初值為0(T.num = T.size = 0), 且表都是至少是半滿的, 即 T.num >= T.size/2, 於是 Φ(T) 總是非負的. 因此 n 個TABLE-INSERTION 操作的攤還代價給出實際代價上界, 即

其中, D0為初始數據結構; ci為每個操作的實際代價; 為每個操作的攤還代價

每個操作的攤還代價等於其實際代價與此操作引起的勢能變化之和.

分析第 i 個 TABLE-INSERT 操作的攤還代價. 令 num表示第 i 個操作后表中數據項的數量, size表示第i個操作后,表的總規模, Φ表示第 i 個操作后的勢.
初始時, num0 = size0 = Φ0 = 0,然后分兩種情況進行討論:
1) 第 i 個TABLE-INSERT操作沒有觸發表擴張. 此時有 sizei = sizei-1 及 numi-1 = numi - 1 < sizei-1. 第 i 個操作的攤還代價為

2) 第 i 個TABLE-INSERT操作觸發表擴張. 此時有 sizei = 2 * sizei-1 及 sizei-1 = numi-1 = numi - 1. 第 i 個操作的攤還代價為

下圖中, 繪制出了 numi, sizei 和 Φi 隨 i 變化的情況. 注意, 勢(potential)是如何累積來支付表擴張的代價的. 每次插入數據項(攤還代價為3), 去掉插入本數據項所需的代價(實際代價為1), 將未消耗的代價(即 2)存入勢(potential)中, 進行累積. 

表擴張和收縮采用的常用策略是允許表的裝載因子低於1/2. 具體地, 當向一個滿表, 插入一個新數據項時, 將表規模加倍; 但只有當裝載因子小於1/4而不是1/2時, 將表的規模減半.

下面采用勢能法分析n個TABLE-INSERT和TABLE-DELETE操作組成的序列的代價.

首先定義一個勢函數Φ, 滿足在表擴張或表收縮之后, 其值為 0, 而當裝載因子增長為 1 或下降為 1/4 時, 勢函數值累積到足夠支付表擴張或表收縮操作的代價值. 勢函數的具體定義如下:

可以在下圖中觀察到勢函數的這些特性.

令 ci 表示第 i 個操作的實際代價,  ^c表示用勢函數 Φ 定義的攤還代價, numi 表示表中第 i 個操作存儲的數據項的數量, sizei 表示第 i 個操作后,表的規模, αi 表示第 i 個操作后的裝載因子, Φi 表示第i個操作后的勢. 初始時, num0 = size0 = 0, α0=1, Φ0 = 0.

下面分別分析第 i 個操作是 TABLE-INSERT或TABLE-DELETE的情況:
a) 當第 i 個操作為TABLE-INSERT時,總有 numi = numi-1 + 1
1) 若 αi-1 >= 1/2, 則與上面"采用勢能法分析 n 個TABLE-INSERTION 操作序列"的情況相同. 無論表是否擴張, 操作的攤還代價至多都是 3;
2) 若 αi-1 < 1/2, 則第 i 個操作不能令表擴張, 即表規模不變( sizei = sizei-1), 因為只有當 αi-1 = 1時, 第i個TABLE-INSERT操作才會引起表擴張.
2.1) 若 αi < 1/2, 則第 i 個操作的攤還代價為


2.2) 若 αi >= 1/2, 則第 i 個操作的攤還代價為

注: numi-1 = numi - 1 = sizei * αi-1 < sizei/2 <= sizei * αi

b) 當第i個操作為TABLE-DELETE時,總有 numi = numi-1 - 1
1) 若 αi-1 < 1/2, 則必須考慮刪除操作是否引起表收縮.
1.1) 若第i個操作未引起表收縮,此時αi >= 1/4 ,則 sizei = sizei-1, 此時TABLE-DELETE操作的攤還代價為


1.2) 若第i個操作觸發了表收縮(裝載因子αi-1 = numi-1/sizei-1 = 1/4),則有 sizei = sizei-1/2 = 2*numi-1= 2*(numi+1),且操作的實際代價為 ci = numi+1,因為刪除1個數據項,之后又移動了 num個數據項, 此時 TABLE-DELETE 操作的攤還代價為


2) 若αi-1 >= 1/2, 則第i個操作不能引起表收縮, 即表規模不變(sizei = sizei-1)
2.1) 若 αi >= 1/2,則第i個操作的攤還代價為


2.2) 若 αi < 1/2,此時裝載因子αi-1 = numi-1/sizei-1 = (numi+1)/sizei= 1/2, 則第 i 個操作的攤還代價為


綜上可知,每個操作的攤還代價的上界是一個常數,所以在一個動態表上執行任意 n 個操作的運行時間是 O(n).

練習及解答

17.4-3 假定我們改變表收縮的方式, 不是當裝載因子小於1/4時將表規模減半, 而是當裝載因子小於1/3時將表規模變為原來的2/3. 使用勢函數
Φ(T) =| 2 * T.num - T.size |
證明使用此策略, TABLE-DELETE 操作的攤還代價的上界是一個常數.

解答

分析第 i 個 TABLE-DELETE 操作, 此時有 numi = numi-1-1.
若 αi-1 < 1/2, 則必須考慮刪除操作是否引起表收縮. 如果沒有引起收縮 , 則 sizei = sizei-1, 且操作的攤還代價為


若 αi-1 < 1/2 且 第 i 個操作觸發了收縮操作, 即裝載因子小於1/3(numi/sizei < 1/3), 則 sizei = 2 * sizei-1/3, 此時操作的實際代價為 ci = numi+1, 因為我們刪除了 1 個數據項, 又移動了 num個數據項. 因此操作的攤還代價為


若 αi-1 >= 1/2 則操作的攤還代價為

所以, 每個TABLE-DELETE操作的攤還代價的上界是一個常數. 

參考資料

[1] T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein. Introduction to Algorithms (3. ed.). MIT Press, 2009.
[2] (美)科爾曼(Cormen, T.H.)等著,殷建平等譯. 算法導論(原書第3版). 北京: 機械工業出版社, 2013.1
[3] 算法導論習題解答 17-4-3. https://blog.csdn.net/weixin_42369181/article/details/105469860


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM