平摊分析(Amortize Analysis)

对于一个操作的序列来讲,平摊分析(Amortize Analysis)得出的是在特定问题中这个序列下每个操作的平摊开销。

  一个操作序列中,可能存在一、两个开销比较大的操作,在一般地分析下,如果割裂了各个操作的相关性或忽视问题的具体条件,那么操作序列的开销分析结果就可能会不够紧确,导致对于操作序列的性能做出不准确的判断。用平摊分析就可以得出更好的、更有实践指导意义的结果。因为这个操作序列中各个操作可能会是相互制约的,所以开销很大的那一、两个操作,在操作序列总开销中的贡献也会被削弱和限制。所以最终会发现,对于序列来讲,每个操作平摊的开销是比较小的。

  我有这样的理解:"对于一个操作序列来讲,平摊分析得出的是这个序列下每个操作的平摊开销"这句话中,操作这个词出现了两次,意义并不相同。

  比如,我们拿《算法分析》中17.1节中举的第一个关于栈操作的例子来解释。

  操作序列,是指由pop、push、multipop三个动作任意组成一组动作组合,并对一个栈进行这组动作。这里"操作序列"中的"操作",是特指的pop动作、push动作、multipop动作等。

  而"每个操作的平摊开销"中的"操作",并不是特指某一种动作,而是指对于一个n个动作的序列中的每一个动作,这时不关心动作本身具体是什么。

  分清这种区别初听起来有些咬文嚼字,但实际上我觉得,这样理解的意义在于,搞清楚以下的"精神":平摊分析的输入是问题的具体条件以及若干个有相互关联的具体动作的序列,输出是,在该问题的条件下,这个动作序列的每一个动作(不管具体它是什么)的平摊性能开销并不等于最大开销动作的最坏时间(不再需要考虑具体动作,只需要知道每个动作可能是平摊小开销的)。

  平摊分析不是平均运行时间性能分析。平摊分析并不需要建立在概率分析的基础上,也允许操作序列中各操作出现最差运行时间。但平摊分析的结果是要告诉我们,一个操作序列的最坏性能并不是像初看起来那样糟(割裂各操作关系和具体条件,用开销最大的操作的最坏时间复杂度去分析整个操作序列的最坏性能),而是总体来讲有一个比较好的最坏开销(因为有了各操作相互的制约以及初始条件的限制后,开销最大的操作贡献有限)。

  平摊分析或许称不上是一种算法设计方法。但平摊分析为我们提供了另一种观察性能的角度。使我们在某些问题上能得出更具有实践意义的结果。

 

  下面我们来看一个关于动态表的具体例子:

  我们经常会遇到这样的情况:无法预先知道有多少对象需要存储。因此无法准确地事先分配好足够的内存并保证内存是大致足够的。事先分配的内存要么可能不足,要么太多以致浪费。

  对于这种情况,我们可以采用动态表的策略来应对。就是一开始分配一定量的内存A,如果在向这个内存内继续增加对象时发现空间不足的话,就重新分配一块比原来大的内存B(就定B比A要大两倍吧),把原内存A中所有的对象都复制到内存B中,然后把新加入的对象增加到B的空余位置中,最后把内存A释放掉。同样,如果从内存中称除某个对象时,发现此空间内所存的对象太少,出现空间浪费时,就分配一块新的更小的内存(先假设新内存是原内存的二分之一),把原内存中的对象复制过来,并释放原内存给其它的程序使用(C++标准库中的vector等,就用了这样的策略)。

  我们先来看插入操作的伪代码:

TABLE-INSERT (T , x)

if size[T ] = 0 then

2     allocate table[T] with 1 slot

3     size[T] ← 1

if num[T] = size[T] then

5     allocate new-table with 2 · size[T] slots

6     insert all items in table[T] into new-table

7     free table[T]

8     table[T] → new-table

9     size[T] → 2 · size[T]

10 insert x into table[T]

11 num[T] → num[T] + 1

  在这个操作中,如果插入时,原内存块还有足够的空间,那就只要把新插入的x放到未使用的空间上(空间的使用是连续的,动作就是伪代码中的第10行),这样,需要的时间仅仅是常数时间O(1)。如果插入对象时,原内存块已经没有空间再容纳新成员了,这时就需要分配新的空间,把原内存的数据复制过去,释放原内存,然后再进行插入动作(如伪代码中的5-9行)。这种情况下,需要的时间候,就是原内存的长度相关(因为要依次拷贝原内存中的每一个对象),这是一个O(n)时间的操作。因此,TABLE-INSERT操作的最坏时间复杂度是性线的O(n)。

  好,例子的情况到这里已经描述完了。现在我们来看,如果对一个动态表进行一个由n个TABLE-INSERT操作组合起来的动作序列,那么这个序列的操作性能是怎么样的呢?

乍一想,我们可能会认为,每个TABLE-INSERT的最坏性能是O(n),n个TABLE-INSERT操作如果恰好每个操作都出现了最坏性能,那么整个操作序列的最差时间复杂度就是O(n2)。虽然这个结果是这个操作序列的一个最差时间复杂度上界,但实际上,这个上界是不够紧确的。

  我们假设一开始这个表是空的,然后用TABLE-INSERT向这个表中依次插入n个对象。仔细一想,就会发现,这个操作序列中的n个TABLE-INSERT是不可能连续n次出现最坏性能的,甚至,出现最坏性能的机会是比较小的。假设ci是第i次插入时的时间,那么很容易就可以分析出来,ci只i等于2的整数次幂时,才会出现比较大的值(值为i),除此以外,ci都是常数1。我们把ci进行求和,就可以得出总的时间

计算式 

  所以,这个操作序列的最坏时间复杂度其实是O(n)。

  原因就在于,这个TABLE-INSERT虽然偶尔会出现线性时间复杂度的操作,但这种情况,在这个问题中出现的机会很少(只有i等于2的整数次幂时),而其它情况下,TABLE-INSERT的操作都是常数时间,这些情况足以将TABLE-INSERT偶尔出现的"恶劣"行为的影响平摊掉。使得对于整个序列来说,最坏时间复杂度仍然保持在比较好的水平下,每个操作的平摊性能也显得很优良(每个操作都O(1)的)。(这个问题更严谨的分析应该看《算法导论》书中17.4部分)

  同样的,TABLE-DELETE操作与TABLE-INSERT操作的分析是极为类似的。这里就不再单独讨论。

  但有意思的是,如果是由TABLE-DELETE与TABLE-INSERT两种操作来组成一个操作序列的话,结果会是怎么样的呢?我们先假设,a一个动态表的装载因子,它是用内存块中对象个数除以内存块可存储的对象总个数得到的。那么,如果动态表每次扩充都变成原来大小的两倍的话,每个TABLE-INSERT之后,装载因子都是0.5;同样,每次缩减时成缩减成原来的2/1的话,而每次TABLE-DELETE之前,表的装载因子也为0.5。

与原来单纯的INSERT序列或DELETE序列不一样的是,在上面这段话中描述的情况下,最差    时间复杂度将会是O(n2)。为什么呢??因为具体问题的条件变化了,这两个操作之间的制约也减弱了,比如,可能是以下这么一种操作序列:

  先进行插入,一直到插入动作进行时表进行了扩张,之后的操作则是:

  D、D、I、I、D、D、I、I、D、D、I、I......

  这时,分析可知,这个表将在这些动作间不断地扩张和缩减。这个序列中之后的操作每一个都是O(n)时间。因此,总的时间复杂度是O(n2)。之所以会这样(不再是O(n)了),是因为,这个操作序列和条件下,已经不能保证有足够的O(1)的操作来平摊掉O(n)操作的开销, O(n)操作的出现机会也不再受到控制。

  有一个办法可以改进,就是动态表在TABLE-DELETE在装载因子是0.25(或其它可能值)而不是0.5时才进行缩减。这样,又能恢复类似之前的分析和结果了(最差时间复杂度为O(n))。

 

  从这个例子也可以看出:

  这就是平摊分析观察问题的角度,它考查的是一个操作序列,并且把操作之间的相互影响,以及问题的条件制约考虑进来得出性能结论。这比起割裂各操作的相互限制以及问题具体条件来分析更具指导意义。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、本书的内容 目前,市面上有关计算机算法的书很多,有些叙述严谨但不全面,另外一些则是容量很大但不够严谨。本书将叙述的严谨性以及内容的深度和广度有机地结合了起来。第1版推出后,即在世界范围内受到了广泛的欢迎,被各高等院校用作多种课程的教材和业界的标准参考资料。它深入浅出地介绍了大量的算法及相关的数据结构,以及用于解决一些复杂计算问题的高级策略(如动态规划、贪心算法、平摊分析等),重点在于算法的分析和设计。对于每一个专题,作者都试图提供目前最新的研究成果及样例解答,并通过清晰的图示来说明算法的执行过程。. 本书是原书的第2版,在第1版的基础之上增加了一些新的内容,涉及算法的作用、概率分析和随机化算法、线性规划,以及对第1版中详尽的、几乎涉及到每一小节的修订。这些修订看似细微,实际上非常重要。书中引入了“循环不变式”,并贯穿始终地用来证明算法的正确性。在不改动数学和分析重点的前提下,作者将第1版中的许多数学基础知识从第一部分移到了附录中。 二、本书的特点 本书在进行算法分析的过程中,保持了很好的数学严谨性。书中的分析和设计可以被具有各种水平的读者所理解。相对来说,每一章都可以作为一个相对独立的单元来教授或学习。书中的算法以英语加伪代码的形式给出,只要有一点程序设计经验的人都能读懂,并可以用任何计算机语言(如C/C++和Java等)方便地实现。在书中,作者将算法的讨论集中在一些比较现代的例子上,它们来自分子生物学(如人类基因项目)、商业和工程等领域。每一小节通常以对相关历史素材的讨论结束,讨论了在每一算法领域的原创研究。 本书的特点可以概括为以下几个方面: 1.概念清晰,广度、深度兼顾。 本书收集了现代计算机常用的数据结构和算法,并作了系统而深入的介绍。对涉及的概念和背景知识都作了清晰的阐述,有关的定理给出了完整的证明。 2.“五个一”的描述方法。 本书以相当的深度介绍了许多常用的数据结构和有效的算法。编写上采用了“五个一”,即一章介绍一个算法、一种设计技术、一个应用领域和一个相关话题。.. 3.图文并茂,可读性强。 书中的算法均以通俗易懂的语言进行说明,并采用了大量插图来说明算法是如何工作的,易于理解。 4.算法的“伪代码”形式简明实用。 书中的算法均以非常简明的“伪代码”形式来设计,可以很容易地把它转化为计算机程序,直接应用。 注重算法设计的效率,对所有的算法进行了仔细、精确的运行时间分析,有利于进一步改进算法。 三、本书的用法 本书对内容进行了精心的设计和安排,尽可能考虑到所有水平的读者。即使是初学计算机算法的人,也可以在本书中找到所需的材料。 每一章都是独立的,读者只需将注意力集中到最感兴趣的章节阅读。 1.适合作为教材或教学参考书。 本书兼顾通用性与系统性,覆盖了许多方面的内容。本书不但阐述通俗、严谨,而且提供了大量练习和思考题。针对每一节的内容,都给出了数量和难度不等的练习题。练习题用于考察对基本内容的掌握程度,思考题有一定的难度,需进行精心的研究,有时还通过思考题介绍一些新的知识。 前言回到顶部↑本书提供了对当代计算机算法研究的一个全面、综合性的介绍。书中给出了多个算法,并对它们进行了较为深入的分析,使得这些算法的设计和分析易于被各个层次的读者所理解。力求在不牺牲分析的深度和数学严密性的前提下,给出深入浅出的说明。. 书中每一章都给出了一个算法、一种算法设计技术、一个应用领域或一个相关的主题。算法是用英语和一种“伪代码”来描述的,任何有一点程序设计经验的人都能看得懂。书中给出了230多幅图,说明各个算法的工作过程。我们强调将算法的效率作为一种设计标准,对书中的所有算法,都给出了关于其运行时间的详细分析。 本书主要供本科生和研究生的算法或数据结构课程使用。因为书中讨论了算法设计中的工程问题及其数学性质,因此,本书也可以供专业技术人员自学之用。 本书是第2版。在这个版本里,我们对全书进行了更新。所做的改动从新增了若干章,到个别语句的改写。 致使用本书的教师 本书的设计目标是全面、适用于多种用途。它可用于若干课程,从本科生的数据结构课程到研究生的算法课程。由于书中给出的内容比较多,只讲一学期一般讲不完,因此,教师们应该将本书看成是一种“缓存区”或“瑞典式自助餐”,从中挑选出能最好地支持自己希望教授的课程的内容。 教师们会发现,要围绕自己所需的各个章节来组织课程是比较容易的。书中的各章都是相对独立的,因此,你不必担心意想不到的或不必要的各章之间的依赖关系。每一章都是以节为单位,内容由易到难。如果将本书用于本科生的课程,可以选用每一章的前面几节内容;在研究生课程中,则可以完整地讲授每一章。 全书包含920多个练习题和140多个思考题。每一节结束时给出练习题,每一章结束时给出一些

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值