算法设计搬运(2)——Amortized Analysis

前言

本篇是关于复杂度分析,amortized analysis —— 570课程 lecture 2的内容。因为我并没有在算法设计这本书上找到相关内容,所以基本全是课程知识的搬运和消化。

2. Amortized Analysis

Amortized Analysis指的是对于一个一串操作复杂度,即 a sequence of operations. 它存在的意义是为了能在某些场景能得到一个更为准确的复杂度。因为如果要执行一串操作,其中可能有的op复杂度很高,而有时op复杂度可能很低。这样如果我们如果用最传统的 worst-case runtime complexity的话,给出的就可能是一个非常非常悲观的估计。我们用 Amortized Analysis 就是为了给出一个在一段时间内,一些操作的(平均)复杂度估计。与之对比的是,average case 复杂度。Average case复杂度是依据多次独立的 random input,然后求平均;而 amortized analysis 的复杂度则建立在一个sequence上。

2.1 Unbounded Array

首先是引入了 Unbounded Array,也就是不定长的数组。这个学过数据结构的话应该明白,我们申请的数组是计算机内存中一块连续的存储空间。对于 Insert 操作来说,如果有空间,那么复杂度O(1);而如果空间满了,则需要先 copy 现存数组,申请一块更大的内存,而 copy 是线性 O(n) 的,所以此时 Insert 也是 O(n). 所以 worst-case Insert takes O(n).

Unbounded Array 其实就是这么个东西 通过 Insert 来实现。那么这 op 的 amortized cost 是什么呢?
回答这个问题之前先要解决另一个问题,就是 Insert 满时,申请更大内存的机制。我们再次假设是 increase by k。(线性增加,举例 k = 2) 在这个场景下,我们插入一系列数。(如下图)
Sequence Insertion
可知,所有插入,若其是奇数次,则O(1),偶数次线性,因为满了要copy。假设插入 n 次,则其 amortized cost = n 2 + ∑ i = 1 n / 2 2 i = O ( n 2 ) \frac{n}{2}+\sum_{i=1}^{n/2}2i = O(n^2) 2n+i=1n/22i=O(n2), 所以对于这样的insertion,其 amortized cost per operation 是 O(n)。

这里有个推论,就是如果是线性增长 (increase by),那么 Unbounded Array 的insert 就是 O(n)。而如果是按比例增长申请新内存 (multiply by),那么便是O(k),k 为一常数。另外,利用 amortized cost 分析时,O(k) 不写成 O(1),这一点需要稍微注意一下。
如果我们这里 multiply by 2,那么 假设插入 2n+1 次,只有 n+1 次copy,amortized cost = 2 n − n + ∑ i = 0 n 2 i = O ( 3 ⋅ 2 n ) 2^n-n+\sum_{i=0}^{n}2^i = O(3·2^n) 2nn+i=0n2i=O(32n),amortized cost per operation = 3 ⋅ 2 n 2 n + 1 = O ( 3 ) \frac{3·2^n}{2^n+1}= O(3) 2n+132n=O(3)

以上的这种分析方法称为 Aggregate Method.

另一种分析方法称为 Accounting Method,它的核心是给每个赋予一定数量的 token,我们每次要去做一个op,就获得它对应的 token,而完成这个操作也需要消耗一定的已有 token,而 op 最终的的 amortized cost 就是能使一系列 op 顺利完成所需的最少 token。

同样是上面那个例子 (multiply by 2),加入我们赋予 insert 1个 token。那么插入第一个数,获得一个token,但是一次赋值要消耗掉一个token。当插入第二个数时,获得一个token,但需要先申请更大内存,先copy 第一个数,消耗一个token,此时 token 余额为 0,无法进行第二个数的赋值。所以一个 token的 insert 是行不通的。如下图。
Each insertion 1 token
相应的,增加 insertion 的 token,直至无论怎样插入都可以始终保证可行。此处最少需要 3 token。2 token 和 3 token 的情况如下图。
Each insertion 2 token
Each insertion 3 token
Fail的情况是因为余额不足,银行是不会贷款的…对于该例,可见用此法同样得到 O(3) 的 amortized cost per operation.

2.2 Binary Counter

Binary Counter 是不同于 Unbounded Array 的另一种模型。

Given a binary number n n n, with log ⁡ n \log n logn bits, stored as an array, where each entry A[i] stores the i-th bit, the cost of incrementing a binary number is the number of bits flipped. We use the standard way of incrementing the counter, which is to toggle the lowest order bit.

就是给定一个用 log n 个bits 表示的数,我们想去increment,通过最低位+1的方式增加,即二进制加法。代价是每一次 +1 变化的位数,显然每一次 increment 的cost不同。那么这种 increment 的 amortized cost是什么呢?(此处给定每一位 flip 的cost相同,由 cost is the number of bits flipped 得)
假设 n = 8,3 bits,使用 Aggregate Method:
Binary Counter
最低位每次都变,倒数第二位每两 increment 变一次,倒数第三位每四次变一次,以此类推,直至最高位。Total Work = $ n\sum_{i=1}^{-1+\log n} \frac{1}{2^i} = O(n)$, amortized cost per operation = O(1).
这类模型的变化可能在于 flip 每一位的 cost 不同,但分析方法相同。

2.3 Amortized Dictionary

这里的 amortized dictionary 跟 Binomial Heap 很像。是 linked list 和 sorted array 的嵌套。其中每一个节点是一个长度唯一的 sorted array,array k (binary) 的 size 是 2k。比如说存储 11 个数,那么会把这11个数拆分成 11 = 1 + 2 + 8,也就是对应 Binomial Tree 的 B0, B1, B3
amortized dictionary

对于这个 dictionary,他要提供插入和搜索 op。对于 insert 而言,我们要维护它的结构和 ordering。当插入新数据时,可能会要合并已有array,这时要 merge 花费线性时间。
比如此时插入新元素 1,那么就出现了两个 B0,size 1,1,2,8    ⟹    \implies 2,2,8    ⟹    \implies 4,8.其中 merge 有序array O(n),分别 2+4。

如果我们考虑 worst-case,那么必然每次插入都会涉及到 merge,那么如果我们有 n 个数,最多需要 log n 这么多节点。那么 worst-case,就要从 1,开始合并,1-2-4-…- log n,复杂度 = 2 ⋅ ∑ i = 1 log ⁡ n i = O ( n ) 2·\sum_{i=1}^{\log n} i = O(n) 2i=1logni=O(n),一次插入。n 次就 O(n2)了… 但显然不可能每次插入都会遇到这种情况,所以我们看它的amortized cost 来得到 sequence insertion 的比较实际(更为准确)的复杂度。

假设我们连续插入 n 个数。那么这其实就是对应的 binary counter,只不过每个 merge 的cost,对应 flip 每一位的cost不再都是 O(1),而是有一个weight,取决于它是第几位。二进制的最低位 filp cost 1,n 次,倒数第二位cost 2,n/2 次, 倒数第三位 4,n/4 次… 这样 total cost = $ n·1+\frac{n}{2}·2+\frac{n}{4}·4+…+2·2^{-1+\log n} = n·\sum_{i=0}^{-1+\log n}2{-i}·2i = O(n\log n)$

2.4 Amortized Tree

这张的最后一部分时关于 Amortized Tree 的,二叉搜索树的变形——考虑到 binary search tree 不保证 balance,最差就变成了类似线性查找。这里的 amortized tree 是一种变形——每次搜索完都重构这搜索树,使我们让上一次搜索的数成为新树的根节点,称为 splay tree。这种做法背后的思想是我们一段时间的搜索很大概率上都是针对相同 local area的,类似 cache或者换进来一个block以节省成本。
其中比较重要的操作就是 Splay(N),把 searched N move to root. 而我们要保证该操作在 O ( log ⁡ n ) O(\log n) O(logn) 时间内完成涉及到 6 种旋转操作——基本就是本科数据结构所学。

  • 1 Zig(Zag)——右旋(左旋)
    当 N 的 parent 为 root 时,进行 右旋,zig。如下图。 N 是 P 右孩子是左旋。
    右旋

  • Zig-Zag (Zag-Zig)——先右旋(左旋),再左旋(右旋),如下图。
    Zig-Zag

  • Zig-Zig (Zag-Zag)——右旋(左旋),再右旋(左旋),如下图。
    Zig-Zig

注意,有一种看似可行的 Zig-Zig,但并不能保证O(log n)的rotation,而是 linear,如下图。
wrong Zig-Zig

为了说明上面那种优先 rotate N 的 Zig-Zig rule 是错误的,我们假设下面这种情况。初始我们由一个已经 sorted array,1,2,3,…,n。一路左叉,到底。如图

an example
这样,我们如果 Splay(1),Splay(2),Splay(3),…,Splay(n) 来 search,那么Splay(1) 从最下面 rotate n次,Splay(2) 需要从最下面 Zig n-1次 … 直到最后面Splay(n) Zig 1次。这样total cost$ = n + n-1 + n-2 … + 1 = O(n^2)$ , amortized cost per operation = O ( n ) O(n) O(n)
为保证 O ( log ⁡ n ) O(\log n) O(logn)的amortized cost per operation,只能用 Zig-Zig (Zag-Zag) rotation rule,此时在一棵 n 个 nodes 的 Splay Tree 上进行 m 次 operation 的复杂度为 O ( m log ⁡ n ) O(m\log n) O(mlogn)

proof.(证明超纲…)
Algorithm Design 这本书上并没有找到 Amortized Analysis 相关的内容,所有内容均来自 Victor 自己编的教材。570 的 lecture 只覆盖了 2.1 和 2.2 的内容

Discussion Problem: Using two stacks to implement a FIFO queue, stack has POP and PUSH operations, cost of each is O(1). The FIFO should have ENQUEUE and DEQUEUE operation,分别求 amortized cost?

Solution: 首先对于sequence operation,我们只能让它一连串 ENQUEUE,如果要 DEQUEUE 就必须全部出队列。实现的话对所有 enqueue 操作先全部都push 到栈 A 里面,然后再 pop,接 push 压到另一个栈 B 里,完成入队列。dequeue就全部从栈 B 逐个pop。那么此处使用 accounting method 分析,考虑分给 ENQUEUE and DEQUEUE 的最少可行 token。ENQUEUE 若 token 1,那么全部 push 到 A 之后就没有余额 pop完成 ENQUEUE。若ENQUEUE 赋予 2 token,那么 n 次push A,pop A 之后没有余额去 push B,也不成立。若ENQUEUE 赋予 3 token,那么可以保证每一个 item 都能入队列,3个token 中1个push A,1个pop A,1个push B。而DEQUEUE只需要直接 pop B,O(1),顺次出栈,不需要余额,1个token 就可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值