在平摊分析中,执行一系列数据结构操作所需要的时间是通过执行的所有操作求平均而得出的。平摊分析可以用来证明在一系列操作中,通过对所有操作求平均之后,即使其中单一的操作具有较大的代价,平均代价还是很小的。
平摊分析不牵涉到概率, 平摊分析保证在最坏情况下,每个操作具有的平均性能。
三中最常用的技术:
1.聚集分析(aggregate analysis)
证明对所有的n, 由n个操作所构成的序列的总时间在最坏情况下为T(n)
例子:
栈操作
PUSH(S, x):将x压入S
POP(S):弹出栈顶
MULTIPOP(S, k):弹出栈顶k个对象
由n个PUSH, POP和MULTIPOP操作序列,其作用于一个初始为空的栈。
分析:每个操作的最坏情况时间是O(n). 因此n个操作的序列的代价是O (n2)
虽然这一分析正确,但这个界不够紧确,利用聚集分析,可以获得更好的上界。
一个对象在每次被压入栈后,至多被弹出一次。所以,调用POP(包括MULTIPOP)的次数至多等于PUSH的次数,即至多为n.。对任意的n,包含n个PUSH, POP, MULTIPOP操作的序列的总时间为O(n). 每个操作的平均代价为O(n) / n = O(1).。 聚集分析中,将每个操作的平摊代价指派为平均代价。所以三个栈操作的平摊代价都是O(1)。
2.会计方法
在平摊分析的记帐方法中,对不同的操作赋予不同的费用,某些操作的费用比它们的实际代价或多或少。
我们对一个操作的收费的数量称为平摊代价。当一个操作的平摊代价超过了它的实际代价时,两者的差值就被当作存款(credit),并赋予数据结构中的一些特定对象,可以用来补偿那些平摊代价低于其实际代价的操作。这种方法与聚集分析不同的是,对后者,所有操作都具有相同的平摊代价。
数据结构中存储的总存款等于总的平摊代价和总的实际代价之差。注意:总存款不能是负的。
Σ平摊代价>Σ实际代价
例子:栈操作
实际代价:
PUSH 1
POP 1
MULTIPOP min(k, s)
现在对它们赋予以下的平摊代价:
PUSH: 2
POP: 0
MULTIPOP 0
假设用1元钱来表示代价的单位。开始时栈时空的。
栈数据结构与餐厅中一堆叠放的盘子类似。当把一个盘子压入栈时,用1元钱来支付该动作的实际代价,还有1元的存款,将其放在盘子的顶上。盘子上面的钱时用来预付将盘子从栈中弹出所需代价的。当执行一个POP操作时,对该操作不收取任何费用,只需用盘中存放的存款来支付其实际代价即可。同理,也无需对MULTIPOP收取任何费用。
因为栈上的每个盘子上面都有1元钱,而且栈中总有非负个数的盘子。这就保证了存款的总量总是非负的。这样,对任意的包含n次PUSH, POP, MULTIPOP操作的序列,总的平摊代价就是总的实际代价的一个上界。总的平摊代价为O(n), 故总的实际代价也为O(n).
设计是有套路的,进入时候有存款,出去时候会花掉。2用来支付push和pop的代价(反正只会pop一次)。
例子:二进制计数器
Σ = n+n/2+n/4+…
0-1翻转的平摊代价为2
1-0翻转的平摊代价为0
思路和上一个例子相同。
对于每一个操作,找到左起的第一个0,将他翻转成1--支付代价2
将这个0之前的所有1翻转成0--支付代价0
所以复杂度上界2n
问题:如果里面不为空,平摊分析还可以用吗?