摊还分析
1何为摊还分析?
摊还分析主要求解数据结构维护序列执行的所有操作的平均时间,来评价操作的代价,从而保证最坏情况下每个操作的平均性能。
2聚合分析
2.1何为聚合分析?
若长度为n的操作序列最坏情况下所花费时间为T(n)。
聚合分析状态下,摊还代价C=T(n)/n。
2.2栈的时间复杂度分析
此时栈有3个操作:
1.PUSH(S,x)表示将对象x压入栈中。
2.POP(S,x)表示从栈顶弹出一个元素(保证不会弹空)。
3.MULTIPOP(S,k)表示弹出栈顶的k个元素(保证不会弹空)。
试证明单次操作的摊还代价。
一次POP的最坏时间是O(n)的,那么n个操作的时间是否是O(n^2)的呢?
其实不然。
证明:事实上,每一个元素只入栈一次,出栈一次,而栈中最多存在n个元素,所以n次操作最坏只需要O(n)的时间。而每个操作的摊还代价C=O(n)/n=O(1)。
2.3单调队列的时间复杂度分析
单调队列也有两个操作:
1.PUSH(que,x)表示在队尾插入。
2.POP(que,k)表示在队首弹出k个元素(保证不会弹空)。
试证明单次操作的摊还代价。
证明:每个元素依然只入队一次,出队一次,最多n个元素,最坏为O(n),每个操作的摊还代价C=O(n)/n=O(1)
3.核算法
3.1何为核算法?
我们进行摊还分析时,对每一个不同的操作赋予不同的费用,将赋予一个操作的费用称为它的摊还代价。
每一次摊还代价超出实际代价时,就可以将多出的部分“储存”起来,称之为“信用”,它在以后的操作中可以“抵账”。
但核算法应确保总摊还代价大于总真实代价的上界。如果用ci表示第i个操作的真实代价,ĉi表示第i个操作的摊还代价,要求
也就是
在保证信用非负的情况下,总摊还代价是总真实代价的上界。
3.2栈的核算法分析
还是刚才栈的例子。
它的每个操作的实际代价为:
PUSH 1
POP 1
MULTIPOP k
我们赋予每个操作这样的摊还代价:
PUSH 2
POP 0
MULTIPOP 0
可证明对于任意的操作序列,总摊还代价大于总实际代价。
就相当于每一次PUSH存入2元,1元当做PUSH的代价,还有1元当做将来弹出这个元素的代价。这样可以保证信用永远非负,之前PUSH多付出的摊还代价预支了以后POP需要的代价。所以在POP是可以当作没有任何代价了。总摊还代价还是O(n)的。
3势能法
3.1何为势能法?
势能法没有将预支代价表示为特定操作的信用,而是表示为“势能”,用势能的释放预支代价。势能与整个数据结构对象相关联,而非像核算法一样和某个特定的操作代价相关联。
我们将一个初始数据结构D0执行n个操作。对于每一个操作i,令ci为第i个操作的实际代价,令Di为数据结构Di-1上执行第i个操作所得到的结果数据结构。势函数φ把每个数据结构表示为一个势能大小。
摊还代价:ĉi=ci+φ(Di)-φ(Di-1)
每个操作的摊还代价为实际代价+势能变化。
总摊还代价:
(3.1.1)
需要定义φ使得φ(Dn)>=φ(D0),那么总摊还代价会是总实际代价的上界,因此我们要求φ(Di)>=φ(D0),一般把φ(D0)定义为0,只需要保证φ(Di)>=0即可。
3.2栈的势能法分析
栈的操作同上。
我们设φ(Di)表示i次操作后栈中的元素数量s。
D0初始为空栈。φ(D0)初始为0.
1.对于PUSH(S,x),φ(Di)-φ(Di-1)=(s+1)-s=1。由公式(3.1.1)可得,ĉi=ci+φ(Di)-φ(Di-1)=1+1=2
2.对于MULTIPOP(S,k),φ(Di)-φ(Di-1)=(s-k)-s=-k。由公式(3.1.1)可得ĉi=ci+φ(Di)-φ(Di-1)=k-k=0
同理,POP的摊还代价也为0.
综上所述,每个操作的摊还代价均为O(1),因此总摊还代价为O(n),此时必有φ(Di)>=φ(D0)。
4.动态表扩张
你需要完成对一个序列的插入,快速查询操作。
简单来说就是实现C++中的vector,为其寻找一个空间为O(n)的方法。
对于这个问题,我们的方法是:每次空间存满,扩展两倍空间,并复制原来空间的内容进入新的空间。
(1).若使用聚合分析
ci=
1.当i-1=2^k,ci=i
2.当i-1!=2^k,ci=1
总代价为:
(2).若通过核算法。
令每一次操作的ĉi=3。总摊还时间为3n。
(3).若通过势能法。
定义φ(Di)=2*T.num-T.size。
1.非扩张
ĉi=ci+φ(Di)-φ(Di-1)
=1+(2*numi-sizei)-(2*numi-1-sizei-1)
=1+(2*numi-sizei)-(2(numi-1)-sizei)
=3
2.扩张
ĉi=ci+φ(Di)-φ(Di-1)
=numi+(2*numi - sizei)-(2*numi-1 - sizei-1)
=numi+(2*numi-2*(numi -1))-(2*(numi -1))-(numi -1)
=numi+2(numi - 1)
=3
总摊还代价为O(n)。
5.思考
如何求堆的摊还代价?
如何求AVL的摊还代价?
如何求splay的摊还代价?
6.参考文献
算法导论