算法设计搬运(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 = , 所以对于这样的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+\sum_{i=0}^{n}2^i = O(3·2^n),amortized cost per operation = \frac{3·2^n}{2^n+1}= 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, with \log n 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 = , 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·\sum_{i=1}^{\log n} i = 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}·2^i = 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) 时间内完成涉及到 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 , amortized cost per operation = 。
为保证 的amortized cost per operation,只能用 Zig-Zig (Zag-Zag) rotation rule,此时在一棵 n 个 nodes 的 Splay Tree 上进行 m 次 operation 的复杂度为 。

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 就可以。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271