摊还分析

摊还分析

本章内容:

1.聚合分析

2.核算法

3.势能法

4.动态表

一  聚合分析

1.  在摊还分析中,我们求数据结构的一个操作序列中所执行的所有操作的平均时间,来评价操作的代价,它不涉及概率,可以保证最坏情况下每个操作的平均性能。

2.  摊还代价:对所有n,一个n个操作的序列最坏情况下话费时间为T(n),从而摊还代价(平均代价)为 T(n) / n.

3.  栈操作中加入MULTIPOP(S, k),可以同时删除栈顶的k个元素,总元素少于k则全部删除。

  下面分析一个由n个PUSH, POP, 和MULTIPOP组成的操作序列在一个空栈上的执行情况。假设栈的大小最大为n,那么MULTIPOP的最坏情况代价是O(n)。因此任意一个操作的最坏情况是O(n),从而n个操作的序列的最坏情况代价为O(n ^ 2)。

  上面并不是一个确界,用聚合分析考虑整个序列的n个操作可以得到更好的上界。在一个空栈上执行n个PUSH, POP, MULTIPOP操作,考虑到每个对象PUSH进入后最多只能POP一次,因此执行POP的次数不会多于PUSH的次数,因此最多有n次,即操作序列最多花费O(n)时间,平均代价即为O(n) / n = O(1).

4.  k位二进制计数器递增:

  用一个length为k的位数组A[0..K - 1]作为计数器。当保存值为x时,最低位在A[0],最高位在A[k - 1],有

  初始x为0,为了实现计数器加1,使用如下过程:

  粗略分析发现执行一次最坏情况需要将k个位置由1变为0,所以n此操作最坏情况似乎是O(nk)

  进一步分析,每次INCREMENT时A[0]确实都要翻转,但是A[1]只需要2次执行翻转一次,A[2]则4次执行翻转一次...因此n个操作的序列,A[i]会翻转n / 2 ^ i 次,因此执行序列的翻转操作总数:

  显然摊还代价为O(n) / n = O(1).

二  核算法

1.  对不同的操作赋予不同的费用,将赋予一个操作的费用称为它的摊还代价。当一个操作的摊还代价超出实际代价时,将差额存入数据结构中,称为信用,可以用来支付别的操作摊还代价比实际代价少的差额。

2.  

  左边表示摊还代价总和,右边表示真实代价总和。总的信用等于上面左边减去右边,由于要保证摊还代价为实际代价上界,所以当前的总信用要保持为非负值。

3.  同聚合分析中的栈操作:

  操作的实际代价为:  其中s为调用时栈的元素个数

  我们给这些操作赋予如下的摊还代价:

  可以这样进行考虑,每个元素PUSH进去的时候,需要支付2个单位的代价,其中PUSH操作实际用了一个,剩下的一个代价存起来。这样栈中的每个存在的元素其实自身都含有一个单位可用的代价,在出栈时无论是POP还是MULTIPOP都可以支付自己的出栈代价。由于栈里面的元素个数始终非负,所以可以保证信用始终非负。

  因此,对任意n个操作序列的总摊还代价为实际代价的上界,即O(n).

4.  二进制计算器递增:

  执行INCREMENT操作的运行时间与翻转的位数成正比,所以用翻转的位数作为操作的代价。

  对一次置位操作(将该位由0变为1),设其摊还代价为2。进行置位时,用1来支付置位操作的实际代价,剩下的1存起来作为信用,该以后复位(变为0)的时候用。任何时刻信用值非负,因为计算器中1的个数始终非负。由于每次操作最多只进行一次置位(第6行),所以总摊还代价为O(n),为实际代价的上界。

三  势能法

1.  对一个初始数据结构D0执行n个操作。Di为在数据结构Di-1上执行第 i 个操作得到的结果数据结构。势函数将Di映射到一个实数Φ(Di),表示数据结构Di的势能,类似于前面的信用。有:

  可以理解为摊还代价减去实际代价等于势能的增量。

  n个操作总摊还代价:

  将初始势能简单定义为0,然后要保证对所有 i 有Di的势能大于等于0.

2.  栈操作:

  这里将势函数定义为一个栈中元素的数量,同时保证了永远非负。

  第 i 个操作如果是PUSH,此时栈中有s个元素,则:

  由此得到PUSH的摊还代价:

  第 i 个操作如果是MULTIPOP,势能的减少恰好等于元素个数的减少,也等于实际操作的代价,有:

  由此每个操作摊还代价都是O(1),所以也可以得到总摊还代价O(n)

四  动态表

1.  我们经常无法预先知道将会有多少个对象存储在表中,所以使用动态的表可以节省很多空间。现在就研究这种动态扩张和收缩表的问题,虽然插入和删除操作可能会引起扩张和收缩,但是可以用摊还分析证明它们的摊还代价都是O(1),而且可以保证动态表中的空闲元素相对于总空间的比例永远不超过一个常数分数。

2.  定义一个非空表T的装载因子α(T)定义为表中存储的数据项的数量除以表的规模。对于空表,定义规模为0,装载因子为1.

3.  表扩张:当表的装载因子为1时,尝试插入一个新的元素将没有可用的槽,这时可以分配一个包含更多槽的新表,将原表中的元素复制过去并插入新的元素。一种常用的策略是:为新表分配2倍与旧表的槽,如果只允许插入操作,那么装载因子总是在 1/2 以上,因此浪费的空间少于总的一半。

  下面是伪代码,T.num是表中的数据项数量,T.size是表的规模

  分析对一个空表执行n个插入操作,其中第 i 次操作的代价为ci 。如果不用扩张,那么一次插入值需要O(1)时间。如果i - 1恰好是2的幂,则第 i 个操作会引起扩张。所以

  n个操作总代价:

  所以单个操作摊还代价至多为3.

4.  表扩张与收缩:与扩张类似,很容易想到可以在表的装载因子刚好小于 1/2 的时候进行收缩。但是这种方法不好,原因在于:假设现在表的装载因子恰好就在 1/2 附近,那么多次的插入和删除可能造成表的多次扩张和收缩,从而使每个操作的摊还代价变为O(n)

  可以做改进:当向一个满表插入一个新数据时,仍然进行相同的扩张,但只有当装载因子小于 1/4 时才进行收缩。

  定义势函数:

  观察发现空表和半满的表的势为0,且势永远不可能为负,因此得出的摊还代价是实际代价的上界。

a.  第 i 个操作是插入。若操作前的装载因子大于等于 1/2 ,则与前面分析相同,摊还代价至多为3;

  若操作前的装载因子小于 1/2 ,且第 i 个操作完成后装载因子仍小于 1/2 ,则第 i 个操作的摊还代价为:

  若操作前的装载因子小于 1/2 ,而第 i 个操作完成后装载因子大于等于 1/2 ,则

  因此插入操作摊还代价至多为3

b.  第 i 个操作为删除。若操作前的装载因子小于 1/2 ,且未引起收缩,则

  若操作前的装载因子小于 1/2 ,且引起收缩,则

  当操作前的装载因子大于等于 1/2,可以证明上界也是一个常数。

  由此可得在动态表上执行任意n个操作的实际运行时间为O(n)

总结:摊还分析其实就是将n个操作一起进行分析,对于某些最坏情况,可以用其他一些情况少于实际代价的时间进行摊还,从而算得平均每个操作比较小的时间上界。

摊还分析

时间: 2024-11-05 17:32:07

摊还分析的相关文章

简单理解算法篇--摊还分析

摊还分析是用来评价程序中的一个操作序列的平均代价,有时可能某个操作的代价特别高,但总体上来看也并非那么糟糕,可以形象的理解为把高代价的操作“分摊”到其他操作上去了,要求的就是均匀分摊后的平均代价. 摊还分析有三种常用的技术:聚合分析,核算法,势能法. 首先看个例子,现在有三种操作,push(s),pop(s),mutlipop(s,k),push(s),统称为栈操作. push(s)每次只能压一个数据,所以规定操作的代价为1,pop(s)每次只能弹一个数据,所以也规定操作的代价为1,而mutli

机智零崎不会没梗Ⅲ (摊还分析)

题目描述 零崎总是说自己有一百种梗可玩,然而其实都是假的,是化学成分的,是特技.想要给摊还分析加个梗,实在是不好想,因为这个内容并不是什么算法,而是算法分析. 不过既然还得考,那么没有办法…… “势能法”是摊还分析中一种比较简单常用的方法,而且容易理解.现在零崎有K个硬币,规定每次“翻动”操作只能从最右侧开始翻转硬币,且如果把一个硬币从正面向上翻到背面向上,则需要对其左侧相邻的那个硬币也执行“翻动”操作(翻至最左则结束).定义硬币组的势为硬币中正面向上的硬币的个数.现在要求你求出从某个给定的硬币

[算法导论]#1 摊还分析

目录 引言 聚集分析 记账方法 势能法 总结 引言 一个哈希表多大合适? 数据量为\(n?\),如果哈希表无限大(>=\(n?\)),那么时间复杂度是\(O(1)?\)的,不过很显然,虽然节省了时间,但是浪费了空间. 实际上在我们不知道数据量的情况下,我们无法确定哈希表的大小,这时我们有个很美丽的数据结构->动态表 动态表的工作原理 建立一个表,初始化大小为1. 如果表的容量不够,那么就把大小扩大为原来的两倍,将原来表的内容复制一遍 把原来表的内存释放,再执行插入 容量会以1,2,4,8,16

算法导论17:摊还分析学习笔记

在摊还分析中,通过求数据结构的一系列的操作的平均时间,来评价操作的代价.这样,即使这些操作中的某个单一操作的代价很高,也可以证明平均代价很低.摊还分析不涉及概率,它可以保证最坏情况下每个操作的平均性能. 摊还分析有三种常用的技术: 聚合分析,它确定$n$个操作的总代价的上界为$T(n)$,所以每个操作的平均代价为$\frac{{T(n)}}{n}$.每个操作都有相同的摊还代价. 核算法:分析每个操作的摊还代价,不同于聚合分析,每种操作的摊还代价是不同的,核算法将序列中较早的操作的余额作为“信用”

复杂度分析(下)

复杂度分析(下) 继续上篇,这篇将介绍四个复杂度分析方面的知识点:最好情况时间复杂度.最坏情况时间复杂度.平均情况时间复杂度.均摊时间复杂度. 1.最好.最坏情况时间复杂度 我们以数组查找举例,遍历数组找指定元素,找到则立即返回该元素所在数组的下标位置,没找到则返回-1.代码比较简单我就不写出来了. 我们来分析一下, 最好情况时间复杂度:最好情况当然是数组的第一个元素就是查找的元素,为O(1) 最坏情况时间复杂度:最坏情况就是数组的最后一个元素为查找的元素,需要遍历完整个数组才能找到该元素,或者

最好、最坏、平均、均摊时间复杂度

关注公众号 MageByte,设置星标点「在看」是我们创造好文的动力.后台回复 "加群" 进入技术交流群获更多技术成长. 本文来自 MageByte-青叶编写 上次我们说过 时间复杂度与空间复度,列举了一些分析技巧以及一些常见的复杂度分析比如 O(1).O(logn).O(n).O(nlogn),今天会继续细化时间复杂度. 1. 最好情况时间复杂度(best case time complexity) 2.最坏情况时间复杂度(worst case time complexity) 3.

[算法]复杂度分析

时间复杂度 时间复杂度的分析 只关注循环执行次数最多的一段代码,因为使用大O表示法,其他执行次数较少的复杂度可以忽略 加法法则:总复杂度等于量级最大的那段代码的复杂度 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积 常见的时间复杂度示例 复杂度从低阶到高阶为:(复杂度越高阶,执行效率越低) O(1).O(logn).O(n).O(nlogn).O(n^2) O(1) 该时间复杂度表示代码的执行时间可以认为与输入n无关,是一个固定的值 O(logn)&O(nlogn) o(logn)复杂度

算法分析的正确姿势

[本系列博文会对常见的数据结构及相应算法进行分析与总结,并会在每篇博文中提供几道相关的一线互联网企业面试/笔试题来巩固所学及帮助我们查漏补缺.项目地址:https://github.com/absfree/Algo.由于个人水平有限,叙述中难免存在不清晰准确的地方,希望大家可以指正,谢谢大家:)] 一.前言 在进一步学习数据结构与算法前,我们应该先掌握算法分析的一般方法.算法分析主要包括对算法的时空复杂度进行分析,但有些时候我们更关心算法的实际运行性能如何,此外,算法可视化是一项帮助我们理解算法

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类