聚合分析 (aggregate analysis)
一个 n 个操作的序列最坏情况下花费的总时间为 T(n) T ( n ) , 则在最坏情况下, 每个操作的摊还代价为 T(n)n T ( n ) n
如栈中的 push, pop 操作都是
O(1)
O
(
1
)
, 增加一个新操作 multipop
,
def multipop(stk,k):
while not stk.empty() and k>0:
stk.pop()
k-=1
multipop 的时间复杂度为 min(stk.size,k), 最坏情况为 O(n) O ( n ) , 则 n 个包含 push pop multipop 的操作列的最坏情况是 O(n2) O ( n 2 ) , 并不是这样, 注意到, 必须栈中有元素, 再 pop, 所以 push 操作与 pop 操作 (包含 multipop 中的 pop), 个数相当, 所以 实际上应为 O(n) O ( n ) , 每个操作的摊还代价 为 O(1) O ( 1 )
核算法 (accounting method)
对不同操作赋予不同费用 cost (称为摊还代价 c′i c i ′ ), 可能多于或者少于其实际代价 ci c i
当
c′i>ci
c
i
′
>
c
i
, 将
c′i−ci
c
i
′
−
c
i
( credit
) 存入数据结构中的特定对象.. 对于后续
c′i<ci
c
i
′
<
c
i
时, 可以使用这些 credit 来 支付差额.. 有要求
如栈
op | c′i c i ′ | ci c i |
---|---|---|
push | 2 | 1 |
pop | 0 | 1 |
multipop | 0 | min(s,k) |
由核算法, 摊还代价满足要求, 所以 n 个操作总代价 O(n) O ( n ) , 每个操作摊还代价为 O(1) O ( 1 )
势能法 (potential method)
势能释放用来支付未来操作的代价, 势能是整个数据结构的, 不是特定对象的 (核算法是).
数据结构 D0 D 0 为初始状态, 依次 执行 n 个操作 opi o p i 进行势能转换 Di=opi(Di−1),i=1,2,…,n D i = o p i ( D i − 1 ) , i = 1 , 2 , … , n , 各操作代价为 ci c i
势函数 Φ:Di→R Φ : D i → R , Φ(Di) Φ ( D i ) 即为 Di D i 的势
则第 i 个操作的摊还代价
则
如果定义一个势函数
Φ,st Φ(Di)⩾Φ(D0)
Φ
,
s
t
Φ
(
D
i
)
⩾
Φ
(
D
0
)
, 则总摊还代价给出了实际代价的一个上界
可以简单地以
D0为参考状态,then Φ(D0)=0
D
0
为参考状态
,
t
h
e
n
Φ
(
D
0
)
=
0
例如栈操作,
设空栈为
D0
D
0
, 势函数定义为栈的元素数
对于 push,
Φ(Di)−Φ(D0)=1
Φ
(
D
i
)
−
Φ
(
D
0
)
=
1
则
c′=c+Φ(Di)−Φ(D0)=c+1=2
c
′
=
c
+
Φ
(
D
i
)
−
Φ
(
D
0
)
=
c
+
1
=
2
对于 multipop,
Φ(Di)−Φ(D0)=−min(k,s)
Φ
(
D
i
)
−
Φ
(
D
0
)
=
−
m
i
n
(
k
,
s
)
则
c′=c−min(k,s)=0
c
′
=
c
−
m
i
n
(
k
,
s
)
=
0
同理 pop 的摊还代价也是 0, 则总摊还代价的上界 (最坏情况) 为 O(n) O ( n )