摊还分析用来评价某个数据结构的一系列操作的平均代价,有时可能某个操作的代价特别高,但总体上来看也并非那么糟糕,可以形象的理解为把高代价的操作“分摊”到其他操作上去了,要求的就是均摊后的平均代价。

摊还分析有三种常用的技术:聚合分析,核算法,势能法。

1.聚合分析

利用聚合分析,我们可以证明对于任意的

,一个包含
个操作的序列花费的总时间为
。因此,在最坏情况下,每个操作的平均代价,或称为摊还代价为
,即每个操作的时间复杂度为

下面讲两个简单的例子来说明。

  • 栈操作

考虑一个空栈

,有三种操作:
PUSH(S,x):将对象x压入栈S中。
POP(S):将栈S的栈顶对象弹出,并返回该对象。对空栈调用POP会产生一个错误。
MULTIPOP(S,k):循环调用POP(S),弹出栈顶的k个元素(k<n,n为栈的最大容量)。

其中,第三种操作的伪代码如下:

MULTIPOP(S,k)while not STACK-EMPTY(S) and k>0POP(S)k=k-1

那么,现在需要分析:执行n次栈操作最坏情况下的时间复杂度是多少?

分析:

单独看三个操作,前两个都是

的,第三个是
的。这样直观的看,最坏情况下,执行
次操作的代价是
,但这实际上是一个松的上界。因为要想达到这个上界,就要尽量多执行第三个操作,但是栈为空以后,第三个操作就什么都不干了。

我们需要对这三个操作整体分析。显然,对于一个非空的栈,可以执行的

操作的个数(包括后两个操作的所有POP)与执行了
操作的个数相当,即最多

因此,

次操作最坏情况下执行的时间复杂度为
,平均每次操作的代价是

在聚合分析中,我们将每个操作的摊还代价设定为平均代价,故三种操作的摊还代价都是

  • 二进制计数器递增

考虑一个

位二进制计数器,其初值为0,用一个数组
来表示,当计数器中保存的值是
时,
的最低位保存在
最高为保存在
,即

只有一个操作

,即计数器
INCREMENT(A)i=0while i<k and A[i]==1A[i]=0i=i+1if i<kA[i]=1

那么,现在需要分析:执行n次该操作最坏情况下的时间复杂度是多少?

分析:

和上一个例子类似,单独考虑每一个操作,如果原来k个1,操作一次会把k个位置全更改一遍,代价是

,执行n次就是
,这是一个非常松的上界,不可能每次都遇到这种极端情况。

考虑连续的n次操作,我们可以得知,每次调用

都会翻转,每两次调用
翻转一次,每四次调用
翻转一次,每
次调用
翻转一次。

因此执行n次操作的总代价

故n次操作代价是

,每次操作代价是

2.核算法

用核算法进行摊还分析时,我们对不同操作赋予不同费用,赋予某些操作的费用可能多于或少于其实际代价。我们将赋予一个操作的费用称为它的摊还代价。当一个操作的摊还代价超出其实际代价时,我们将正差额存入数据结构中的特定对象,存入的正差额称为信用。对于后续操作中摊还分析小于实际代价的情况,信用可以用来支付负差额。因此,我们可以将一个操作的摊还代价分解为其实际代价和信用(存入的或用掉的)。不同的操作可能有不同的摊还代价。这种方法不同于聚合分析中所有操作都赋予相同摊还代价的方式。

如果我们希望通过分析摊还代价来证明每个操作的平均代价的最坏情况很小,就应确保操作序列的总摊还代价给出了序列真实代价的上界。而且与聚合分析一样,这种关系必须对所有操作序列都成立。将第 i 个操作的摊还代价表示为

,真实代价表示为
,则要求
,数据结构中存储的信用恰好等于总摊还代价与总实际代价的差值,要求这一差值一直保持非负。比如在某个步骤,允许信用为负值(前面操作缴费不足,承诺在随后补齐账户欠费),那么当时的总摊还代价就小于总实际代价,对于到那个时刻为止的操作序列,总摊还代价就不再是总实际代价的上界了。因此,必须保证数据结构中的总信用永远为非负值。

还是回到上面的两个例子。

  • 栈操作

先考虑每个操作的实际代价(s是栈中元素个数):

然后我们赋予三个操作摊还代价:

假定使用¥1来代表1个单位的代价,那么每次PUSH时,除了支付¥1外,我们额外支付¥1作为可能发生的将来POP的预付费(信用),在POP时直接从信用里取,那么,任意时刻,每个栈里的元素都存在对应的预付费(信用)¥1,一定满足当前信用为非负值,即当前摊还代价是实际代价的一个上界。

因此,每次操作的摊还代价是

,n次操作的摊还代价是
  • 二进制计数器递增

我们设定置位(0变1)的摊还代价为2,复位(1变0)的摊还代价为0。

与上面的例子的分析方法相同,每次操作至多有1个位发生置位,这时除了支付¥1的实际代价之外,再预付将来可能发生的复位的代价¥1,那么将来复位时就可以直接从信用中拿。

因此,每次操作的摊还代价是

,n次操作的摊还代价是

3.势能分析

势能法摊还分析并不将预付代价表示为数据结构中特定对象的信用,而是表示成“势能”,将势能释放即可用来支付未来操作的代价。我们将势能与整个数据结构而不是特定的对象相关联。

势能法工作方式如下。我们将对一个初始数据结构

执行 n 个操作。对每个 i = 1,2,......,n,令
为第i个操作的实际代价,令
为在数据结构
上执行第 i 个操作得到的结果数据结构。势能函
将每个数据结构 D
映射到一个实数
,此值即为关联到数据结构
的势能,第 i 个操作的摊还代价
用势函数
定义为:
。因此,第每个操作的摊还代价等于操作的实际代价加上此操作引起的势能变化。

累加即可得n步操作的总摊还代价:

如果能定义一个势函数

,使得对于任意的n都有:
,则总摊还代价
给出了总实际代价
的一个上界。

因此问题的关键在于定义一个合适的势函数

  • 栈操作

我们定义势函数为当前栈中的元素个数,那么

,且对于任意i,有
,那么任意时刻摊还代价都是实际代价的一个上界。

下面分析每个操作的摊还代价。

假设第i个操作是PUSH,且当时栈中包含s个对象,那么

,PUSH操作的摊还代价是

假设第i个操作是MULTIPOP(S,k),将

个对象弹出栈,对象的实际代价为
,势差为
,那么该操作的弹簧代价为

类似地,普通的POP操作的弹簧代价也为0。

因此,每次操作的摊还代价是

,n次操作的摊还代价是
  • 二进制计数器递增

我们定义第i次操作后,势函数为当前计数器中1的个数

假设第i次操作把

个位复位(1变回0),则实际代价至多位
,如果
,则所有k位都复位了,因此
,若干
,则
,无论哪种情况,都满足
,势差为

因此,摊还代价为

因此,每次操作的摊还代价是

,n次操作的摊还代价是

另外,即使计数器不是从0开始也可以使用势能法分析。

初始时有

个1,n次操作以后有
个1,把摊还代价的定义式移项,可得
,由于
,因此只要
,总实际代价就是
,即至少执行
次即可。

因此,不管计数器初值是什么,n次操作的总实际代价都是

分支限界法时间复杂度_数据结构时间复杂度的摊还分析(均摊法)之一:基础...相关推荐

  1. 摊还分析,核算法与势能法

    为什么我们需要摊还分析 上篇文章我们提到了算法的时间复杂度分析,给定输入规模,我们分析出算法的耗时,但是这样够了吗? 有时输入规模不是一个静态的值,可能输入是一系列操作,比如在这棵树里先插入结点,再做 ...

  2. 算法导论笔记:17摊还分析

    在摊还分析中,通过求数据结构的一系列的操作的平均时间,来评价操作的代价.这样,即使这些操作中的某个单一操作的代价很高,也可以证明平均代价很低.摊还分析不涉及概率,它可以保证最坏情况下每个操作的平均性能 ...

  3. a*算法的时间复杂度_数据结构与算法系列——时间、空间复杂度

    数据结构和算法本质就是帮我们用最快的时间和最少的空间来执行我们的代码.所以,执行效率是衡量一个算法的非常重要的指标.那如何来计算你的算法代码的执行效率呢?这就需要时间.空间复杂度来分析了. 有人可能会 ...

  4. 一层循环时间复杂度_数据结构与算法系列——时间、空间复杂度

    数据结构和算法本质就是帮我们用最快的时间和最少的空间来执行我们的代码.所以,执行效率是衡量一个算法的非常重要的指标.那如何来计算你的算法代码的执行效率呢?这就需要时间.空间复杂度来分析了. 有人可能会 ...

  5. a*算法的时间复杂度_数据结构(1)——算法和时间复杂度

    Data Structure 1 算法和时间复杂度 01.什么是数据结构? 程序设计 = 数据结构 + 算法 数据结构是关系,是数据元素相互之间存在的一种或多种特定关系的集合. 数据结构和算法凌驾于任 ...

  6. 一层循环时间复杂度_数据结构:二叉排序树的前/中/后序遍历(递归与循环两种版本)...

    树的设计初衷与操作时间复杂度 树这种数据结构的出现主要是对链表数据结构的优化,链表数据结构是线性结构,操作一般需要O(N)的时间复杂度,树是链表的变形,即链表的每个节点包含一个节点,而树的节点可以包含 ...

  7. b+树时间复杂度_数据结构:线性表,栈,队列,数组,字符串,树和二叉树,哈希表...

    作者:张人大 代码效率优化 复杂度 -- 一个关于输入数据量n的函数 时间复杂度 -- 昂贵 与代码的结构设计有着紧密关系 一个顺序结构的代码,时间复杂度是O(1), 即任务与算例个数 n 无关 空间 ...

  8. 一层循环时间复杂度_渐进时间复杂度分析

    公众号:原与译 直接看题: 给定一个自然数 n,然后求出前 n 个自然数的和 sum.( n > 0 ) 如: n = 3,则 sum = 1 + 2 + 3 = 6 n = 5,则 sum = ...

  9. python中链表和数组_数据结构笔记(一):数组、链表|python基础教程|python入门|python教程...

    https://www.xin3721.com/eschool/pythonxin3721/ (一)数组 数组(Array)是一种线性表数据结构.它用一组连续的内存空间,来存储一组具有相同类型的数据. ...

最新文章

  1. 随机网络构建_构建随机报价机
  2. 某程序员总结大厂程序员性格:阿里出来的是人精!百度出来的脾气好!美图出来的一根筋!头条出来的心高气傲!京东出来的满嘴是兄弟!...
  3. python分析excel数据-总结:像Excel一样使用python进行数据分析
  4. ept技术_EPT技术在压载水处理中的运用
  5. 什么是电源正激和反激? 正激和反激有什么区别特点?如何快速区分
  6. 信息学奥赛一本通C++语言——1105:数组逆序重存放
  7. MySQL安装与操作总结
  8. [codeVS1204] 寻找子串位置
  9. 黑客攻防技术宝典web实战篇:利用信息泄露习题
  10. HDU 1084:What Is Your Grade?
  11. Openstack api 学习文档 restclient使用文档
  12. android布局详解
  13. 主动降噪技术matlab,主动降噪技术(ANC)的前生今世--原理仿真
  14. URL里面携带了#是什么意思
  15. HCE技术在城市一卡通中的应用探讨
  16. 小米手机刷机工具MiFlash
  17. 区分英文句号和数字中的正则表达式
  18. 2021-1-1今日新闻简报 每天精选12条最新时事热点新闻摘要和1条微语
  19. Assets file ‘E:\*\*\*\obj\project.assets.json‘ not found.Run a NuGet package restore to generate
  20. DTD与shema学习

热门文章

  1. redis的5种数据结构和基本操作
  2. python爬虫Day1(requests基本使用)
  3. 一些常用的WebServices
  4. [翻译] LASIImageView - 显示进度指示并异步下载图片
  5. 初步设计了一下视频工具合集的界面
  6. (CSS3)CSS3- 最常用的属性(一)
  7. Window入侵排查
  8. Fast上传图片成功,FastDFSweb页面显示失败
  9. postgres报错:FATAL: Peer authentication failed for user “zabbix“ Previous connection kept
  10. 身份证号码(最后一位)计算。(使用 Excel 公式,计算 身份证最后一位)