[关闭]
@louisyw 2015-11-24T21:03:06.000000Z 字数 3889 阅读 1408

算法
title: 摊还分析
categories: 数据结构与算法


在摊还分析中,我们通过求数据结构中一个操作序列中所执行的所有操作的平均时间,来评价操作的代价。


摊还分析分类

聚合分析

栈操作

栈有两个基本操作
PUSH(S, x):将对象x压入栈S中。
POP(S): 将栈S的栈顶元素弹出,并返回该对象。对空栈执行此操作会报错。
上述两个操作都是O(1)时间,假定花费为1。因此一个n个PUSH和POP操作的写列的总代价为n,而n个操作的实际运行时间为Θ(n)
现增加一个新的操作MULTIPOP(S,k),它删除栈S栈顶的k个对象,如果栈中对象少于k,则将整个栈的内容都弹出。
分析由n个PUSH、POP和MULTIPOP组成的操作序列在一个空栈上的执行情况。虽然一个MULTIPOP操作的代价可能更很高,但是均摊下来,上述操作序列的代价至多是O(n)。我们从MULTIPOP的角度出发,在执行MULTIPOP的操作的时候,栈中肯定右k个元素,也就是说肯定执行了k次PUSH操作,则这k次PUSH操作(代价为k1)和一次MULTIPOP操作(代价为k)均摊下来的操作时间为(k1+k)/k=Θ(1)。所以,在此例中,所有操作的摊还代价为O(1)

特别注意的是,当在栈中继续增加一个MULTIPUSH操作时,此时的均摊代价已不是O(1)了,因为进行n次操作,花费可能最坏达到Θ(nk),这种情况是MULTIPUSH与MULTIPOP交替执行,每次操作的花费都是k,则n次的总花费就是nk

二进制计数器

k位二进制计数器,计数器的初始值为0.用位数组A[0k1]作为计数器,要表示某数则为x=k1i=0A[i]2i.将1加到计数器的值上,使用如下的过程

  1. INCREMENT(A)
  2. i = 0
  3. whil i < A.length and A[I] == 1
  4. A[i] = 0
  5. i = i+1
  6. if i < A.length
  7. A[i] = 1

加1操作中各位置上的变化如下表:

计数器值 A[7] A[6] A[5] A[4] A[3] A[2] A[1] A[0] 每步代价 总代价
0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 1 1
2 0 0 0 0 0 0 1 0 2 3
3 0 0 0 0 0 0 1 1 1 4
4 0 0 0 0 0 1 0 0 3 7
5 0 0 0 0 0 1 0 1 1 8
6 0 0 0 0 0 1 1 0 2 10
7 0 0 0 0 0 1 1 1 1 11
8 0 0 0 0 1 0 0 0 4 15
9 0 0 0 0 1 0 0 1 1 16
10 0 0 0 0 1 0 1 0 2 18
11 0 0 0 0 1 0 1 1 1 19
12 0 0 0 0 1 1 0 0 3 22
13 0 0 0 0 1 1 0 1 1 23
14 0 0 0 0 1 1 1 0 2 25
15 0 0 0 0 1 1 1 1 1 26
16 0 0 0 1 0 0 0 0 5 31

对于一个初值为0,执行n次INCREMENT操作,显然,A[0]位变化n次,A[1]位变化n2,...A[i]会变化n2i,因此总的变换次数为

i=0k1n2i<ni=012i=2n

每个操作的摊还代价为O(n)/n=O(1).


核算法(Accounting method)

在核算法中,对不同的操作赋予不同的费用,赋予某些操作的费用可能大于或等于实际操作的代价。将赋予一个操作的费用称为摊还代价。当一个操作的摊还代价爱超出其市集代价时,我们将差额存入数据结构中的特定对象,存入的差额被成为信用。对于后续操作中摊还代价小于市集代价的情况,信用可以用来支付差额。

算法描述

动态表

  1. 对第i次操作预先收取费用 cˆi=$ 3(i=2,,n), n=1时收取的费用为$2
    • 对每次操作花费$1
    • 结余$2
  2. 当表的容量翻倍时

    • 将以前的元素移到新表时,每移一次以前的元素花费$1
    • 加入新元素时花费$1
      动态表的扩展过程为
      插入第一个元素时,预先收费 $2,花费$1,剩余$1
      插入第二个元素时,表将翻倍,银行中剩余的$1用来支付移动第一个元素的费用 ,对第二个元素,预先收费$3,花费$1,剩余$2
      表中的数字表示剩余的钱,0表示已经插入元素
      可以发现每次当表需要翻倍时,银行中的结余刚好够支付表翻倍时转移old元素的费用
    1
    0 2
    0 0 2 2
    0 0 0 0 2 2 2 2
    0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2

势能法

将银行结余表示为,势能的释放用来支付未来的操作。
+ 数据结构开始于D0
+ 第i次操作,变化为Di1Di,这次操作的花费为ci
+ 定义势能函数Φ(Di)R
Φ(d0)=0Φ(Di)0
摊还代价ci^

ci^=ci+Φ(Di)Φ(Di1)

ΔΦi=Φ(Di)Φ(Di1)

如果 ΔΦi>0,则c^i>ci,第i此操作将储存剩余的量到数据结构中
如果ΔΦi<0,则c^i<ci,则第i次操作将消耗储存的能量

n次操作的总的摊还代价为

i=1nc^i==i=1n(ci+Φ(Di)Φ(Di1))i=1nci+Φ(Dn)Φ(D0)i=1nci

现举例说明:
定义 Φ(D0)=0

Φ(Di)==i=1n(ci^ci)+Φ(D0)2i2logi

Φ(Di)=2i2logi
那么
Φ(Di)={i0

值得注意的是:
Φ(D0)=0 i

Φ(Di)0 i

现在,算出第i次操作的摊还代价为
ci^==ci+Φ(Di)Φ(Di1){i1i-12otherwise+(2i2logi)(2(i1)2logi1)

+ 第一种情况 i-1是2的幂
ci^===i+22logi+2logi1i+22(i1)+(i1)3

+ 第二种情况
ci^===i+22logi+2logi11+23

因此可以得到,在最坏的情形下,插入n次花费Θ(n)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注