【算法】平摊分析
2022-11-23 11:49:57
0.引子
执行一系列数据结构上操作的时间复杂度,可以通过对每个操作的时间复杂度进行分析,取平均,进行估计。
常用的方法有三种:聚焦方法、会计方法、势能方法。
1.聚焦方法
该方法大意为:考察每个操作的最坏时间复杂度,然后作平均得到整体的一个复杂度上界。
这个方法比较简单,易于操作,但是它估计出来的结果有的时候非常不精确,放缩的范围太大。
实例:栈操作
考察:初始栈为空,经过一系列操作的代价。操作有三种:
- Push:压栈。
- Pop:弹栈。
- MultiPop:多重弹栈。
由于一个对象在每次压栈后,至多被弹出一次,MultiPop弹栈的次数折算成Pop的次数,必然和压栈次数相等。如果压栈n次,那么MultiPop折算成Pop,也相当于Pop了n次,设最终栈空,则整体代价为2n=O(n),平摊下来,每个操作都是O(1)的复杂度。
2.会计方法
一系列操作的实际代价可能彼此不同。那么我们可以为每种操作分配不同的平摊代价,其有两种情况:
- 如果平摊代价大于、等于实际代价,则平摊代价一部分用于抵消实际代价,一部分用于当作余额,支付其他操作的代价。
- 如果平摊代价小于实际代价,则平摊代价全部用作抵消自身实际代价,还要从其他操作的余额中索取一部分,补充抵消自身的代价。
我们选取的平摊代价之和,必须大于实际代价之和,这样我们才能获取一个整体的代价上界,来估计实际代价。
实例:栈操作
已知三种状态的实际代价为:
- Cost(Push)=1
- Cost(Pop)=1
- Cost(MultiPop)=min(n,k),k为栈中元素个数,n为MultiPop折算成Pop的弹栈次数。
平摊代价为:
- Cost(Push)=2,一个1用来支付Push的开销,剩下一个1用来预支Pop的开销。
- Cost(Pop)=0。
- Cost(MultiPop)=0。
那么最坏总代价(最终栈空)为2n,n为压栈次数。总平摊代价也就为O(n),每个操作的平摊代价就是O(1)。
3.势能方法
我们把进行一个操作的前后看作两个状态的转变,设每个状态$D_i$都有一个势能(潜力)$\phi(D_i)$,那么代价就是:
$$
AmortizedCost(Procedure)=ActualCost(Procedure)+\Delta\phi
$$
势能怎么得到?认为规定即可,但是它要满足几个条件:
- 恒正。
- $\phi(D_i)\ge\phi(D_0)$,保证总平摊代价是总实际代价的上界。
实例:栈操作
规定$\phi(D_i)$为$D_i$状态下,栈中元素的个数,那么对于每个操作来说,它们的平摊代价为:
- Cost(Push)=1+n-(n-1)=2
- Cost(Pop)=1+(n-1)-n=0
- Cost(MultiPop)=0
n个栈操作的最坏总平摊代价为2n,其他结论同上。