什么是平摊分析?
在分析一个对某序列的操作的成本时,我们会发现有些场合成本会比较低,而有些场合成本又会很高,平摊分析就是研究其每一趟操作的平均成本,而不关注于某一次的特定操作。
平摊分析有以下几种分析方法:
- 聚合分析
- 会计分析
- 势能分析
聚合分析实例
让我们通过动态表(Dynamic Table)的例子来学习聚合分析。
动态表是一个哈希表(hash table)的实现方案, 其主要特征是动态的根据需要来增加表的空间, 适用于当我们初始时无法预知该表的长度。
具体的操作步骤如下:
- 初始状态表仅有一个元素。
- 当插入元素时, 如表空间未满则直接插入, 如表空间已满时执行以下操作
- 分配一个新的表,其空间是原表的2倍
- 把原表中的元素复制到新表中
- 释放旧表空间
具体来看下图实例,
- 插入第1个节点时,分配1个单元
- 插入第2个节点时,发生溢出,分配新空间为原来的2倍,即2个单元,同时把原来1个单元的数据复制过去
- 插入第3个节点时,又发生溢出,分配新空间为4个单元,同时把原来2个单元的数据复制过去
- 插入第4节点时,没有发生溢出,正常插入
- 插入第5五个节点时,再次发生溢出,分配新空间为8个单元,同时把原来4个单元的数据复制过去
- 插入第6,7,8节点时,没有发生溢出,正常插入
以此类推
实例:
采用聚合法来分析其平摊成本。
我们假设
C
i
C_i
Ci是第i次插入元素的成本(Cost),则可以用以下表达式来表示第i次插入的成本:
C
i
=
{
i
if
i
−
1
=
2
n
1
if 其他
C_i= { \begin{cases} i &\text{if } i-1 = 2 ^n\\ 1 &\text{if } 其他 \end{cases} }
Ci={i1if i−1=2nif 其他
当i-1是2的指数时,根据动态表的定义,我们可以知道此时表已满,需要执行分配新表空间,复制数据和释放旧表的操作。其他时候,则仅需要直接在表位增加新数据即可, 我们将每次添加数据的成记作1。
在分配新表空间,复制数据和释放旧表的操作中,分配空间, 释放旧表的耗时非常低,因此此处忽略其成本。而主要的成本就是将数据从旧表移到新表,这也可以看成将数据依次添加到新表的操作,每一个的成本为1, 则总共的成本就是 i x 1 = i。
我们通过做表来看一下
C
i
C_i
Ci的变化情况
| i | 1 | 2|3|4|5|6|7|8|9|10|
| --|–|–|–|–|–|–|–|–|–|–|–|
| size i |1 | 2|4 | 4| 8|8 |8|8|16|16|
|
C
i
C_i
Ci |1 | 2|3 | 1| 5|1 |1|1|9|1|
|
C
i
C_i
Ci 插入数据 |1 | 1|1 | 1| 1|1 |1|1|1|1|
|
C
i
C_i
Ci 复制数据 |0 |1|2 | 0| 4|0 |0|0|8|0|
在计算平摊成本时,我们可以得到执行n次插入操作后的总成本是:
C
o
s
t
(
n
)
=
Σ
i
=
1
n
C
i
Cost(n) = \varSigma^n_{i=1} C_i
Cost(n)=Σi=1nCi
由上面表格我们可以清楚的看到我们将
C
i
C_i
Ci 分成两部分来求和就非常容易了,第一部分恒等于1, 第二部分则仅在i -1 是 2的指数次时才有值且等于i-1。
=
n
+
Σ
j
=
0
└
l
g
(
n
−
1
)
┘
2
j
= n+\varSigma^{└lg( n-1)┘}_{j=0}2^j
=n+Σj=0└lg(n−1)┘2j
第一部分n个1求和为n,第二部分是一个几何级数求和。
注释:
└
l
g
(
n
−
1
)
┘
└ lg(n-1)┘
└lg(n−1)┘表示对lg(n-1)向下取整
<
=
3
n
=
θ
(
n
)
<= 3n = \theta(n)
<=3n=θ(n)
由此,我们可以知道平摊的插入成为:
θ
(
n
)
/
n
=
θ
(
1
)
\theta(n)/n = \theta(1)
θ(n)/n=θ(1)
会计分析实例(Accounting Analysis)
会计分析法的步骤
- 对于第 i 次插入操作,首先会要求支付一个虚构的平摊成本 C i C_i Ci
- 实际的插入操作会消耗会从支付的虚构的平摊成本里扣除(此处假设每次插入数据需花费¥1)
- 未使用的部分则存入“银行”账户以待以后使用。
- “银行”账户的余额不得小于0, 即:
Σ i = 1 n C i < = Σ i = 1 n C i ^ \varSigma^n_{i=1} C_i <= \varSigma^n_{i=1} \hat{C_i} Σi=1nCi<=Σi=1nCi^
我们依旧使用动态表的例子,使用会计分析来他的平摊成本。
每次插入时我们要求支付¥3元,其中¥1元用来插入数据,¥2元存入“银行”账户用于以后表格存满后,将该元素移到新表中。
当表格满了的时候,依旧仅要求支付¥3元,移动旧表数据到新表时提取“银行”账户里存储的余额补足不够部分。
从第1个元素插入到第10个元素插入,需支付的平摊成本,实际成本和“银行”账户余额的变化如下表所示。
其中,需要注意的是银行余额不可小于0。
| i | 1 | 2|3|4|5|6|7|8|9|10|
| --|–|–|–|–|–|–|–|–|–|–|–|
| size i |1 | 2|4 | 4| 8|8 |8|8|16|16|
|
C
i
^
\hat{C_i}
Ci^ |2 | 3|3 |3| 3|3 |3|3|3|3|
|
C
i
C_i
Ci |1 | 2|3 | 1| 5|1 |1|1|8|1|
| 银行账户 |1 |2|2 |4| 2|4 |6|8|2|4|
势能分析实例(Potential Analysis)
将会计分析中的“银行”存款看做是一种势能, 步骤如下:
- 先预设数据结构 D 0 D_0 D0
- 第 i 次操作将数据结构 D i − 1 D_{i-1} Di−1变成 D i D_i Di
- 第 i 次操作的成记作 C i C_i Ci
- 定义势能函数
ϕ
:
{
D
i
}
→
R
\phi:\lbrace{D_i}\rbrace\rightarrow R
ϕ:{Di}→R
ϕ : ( D 0 ) = 0 , ϕ : ( D i ) > = 0 ∀ i \phi:(D_0) = 0, \phi:(D_i)>=0 \forall{i} ϕ:(D0)=0,ϕ:(Di)>=0∀i - 则平摊成本
C
i
^
\hat{C_i}
Ci^ 对于
ϕ
\phi
ϕ而言就等于第 i 次操作的实际成加上操作前后的势能差
C i ^ = C i + ϕ ( D i ) − ϕ ( D i − 1 ) \hat{C_i} = C_i + \phi(D_i)-\phi(D_{i-1}) Ci^=Ci+ϕ(Di)−ϕ(Di−1)
我们依旧以动态表为例,使用势能分析来分析它的平摊成本。
定义势能函数:
ϕ
=
2
i
−
2
┌
l
g
i
┐
\phi=2i-2^{┌lgi ┐}
ϕ=2i−2┌lgi┐ 并假设
2
l
g
0
=
0
2^{lg0}=0
2lg0=0
并且
ϕ
(
D
0
)
=
0
,
ϕ
(
D
i
)
>
0
,
∀
i
\phi(D_0)=0, \phi(D_i)>0, \forall i
ϕ(D0)=0,ϕ(Di)>0,∀i
根据公式得平摊成本为
C
i
^
=
C
i
+
ϕ
(
D
i
)
−
ϕ
(
D
i
−
1
)
\hat{C_i}=C_i+\phi(D_i)-\phi(D_{i-1})
Ci^=Ci+ϕ(Di)−ϕ(Di−1)
=
{
i
当
i
−
1
=
2
n
1
其他
}
+
(
2
i
−
2
┌
l
g
i
┐
)
−
(
2
(
i
−
1
)
−
2
┌
l
g
(
i
−
1
)
┐
)
= { \begin{cases} i &\text{当} i-1 = 2 ^n\\ 1 &\text{其他} \end{cases} } \Bigg\rbrace+(2i-2^{┌lgi ┐})-(2(i-1)-2^{┌lg(i-1) ┐})
={i1当i−1=2n其他}+(2i−2┌lgi┐)−(2(i−1)−2┌lg(i−1)┐)
=
{
i
当
i
−
1
=
2
n
1
其他
}
+
2
−
2
┌
l
g
i
┐
+
2
┌
l
g
(
i
−
1
)
┐
= { \begin{cases} i &\text{当} i-1 = 2 ^n\\ 1 &\text{其他} \end{cases} } \Bigg\rbrace+2-2^{┌lgi ┐}+2^{┌lg(i-1) ┐}
={i1当i−1=2n其他}+2−2┌lgi┐+2┌lg(i−1)┐
接下来,我们分两种情况来讨论
- 当 i-1是2的指数次时,
2
┌
l
g
(
i
−
1
)
┐
=
i
−
1
2^{┌lg(i-1) ┐}=i-1
2┌lg(i−1)┐=i−1, 而
2
┌
l
g
i
┐
2^{┌lgi ┐}
2┌lgi┐由于对
┌
l
g
i
┐
┌lgi ┐
┌lgi┐向上取整的缘故,则会等于
2
(
i
−
1
)
2(i-1)
2(i−1),故而可以得到以下结果。
C i ^ = i + 2 − 2 ┌ l g i ┐ + 2 ┌ l g ( i − 1 ) ┐ \hat{C_i}=i+2-2^{┌lgi ┐}+2^{┌lg(i-1) ┐} Ci^=i+2−2┌lgi┐+2┌lg(i−1)┐
= i + 2 − 2 ( i − 1 ) + i − 1 = 3 =i+2-2(i-1)+i-1=3 =i+2−2(i−1)+i−1=3 - 当 i-1不是2的指数次时, 则
2
┌
l
g
i
┐
=
2
┌
l
g
(
i
−
1
)
┐
2^{┌lgi ┐}=2^{┌lg(i-1) ┐}
2┌lgi┐=2┌lg(i−1)┐, 因此可以得到一下结果。
C i ^ = 1 + 2 − 2 ┌ l g i ┐ + 2 ┌ l g ( i − 1 ) ┐ \hat{C_i}=1+2-2^{┌lgi ┐}+2^{┌lg(i-1) ┐} Ci^=1+2−2┌lgi┐+2┌lg(i−1)┐
= 1 + 2 = 3 =1+2=3 =1+2=3
综上所得,平摊成本是3。
小结
平摊成本分析的优势在于可以给出一个清晰的量化的数值来评估一种数据结构的性能。
有三种方法可以实现平摊成本分析,他们各有各自的优势,在不同的场合应选择最为合适或精确的算法来进行分析。
对于后两种分析方法,选择不同的势能函数或者不同的预存成本可能会导致不同的分析结果,因此需谨慎使用。