平摊分析(摊还分析)

我们有时候会有一个算法,或者只是单纯的一系列操作,当我们需要将这一些操作计算一个平均代价,但是又不涉及概率的问题,我们就可以使用平摊分析。

就比如一个月的账单,可能每一天都是正常的一日三餐,但是有一个周末出去玩花的钱可能会很多,
如果你想计算一下这一个月平均每一天消费的上界,最简单的方式就是找最大消费的那天(出去玩),但是很明显这样是不合理的,但我们又没有概率支撑,不知道有多大的概率出去玩。这时就需要用到平摊分析了。

三种方法

聚集法

看名字就知道了,这个方法也是很简单粗暴的,就是将所有的操作加起来,然后除以操作数,得到的结果就是平摊代价。

会计法

会计每天对着什么,是账本,所以我们的想法是找一个账本将额外支付的代价记录下来
额外的代价?
我们实际上是清楚每一个操作的代价的,但是我们在这里还需要自己定义一个平摊代价(也就是最后我们要找的),
有的操作平摊代价比实际代价要大,这时我们就需要将多出来的部分记录在账本上;
有的却比实际低,这时我们就可以通过划掉账本的一部分记录来抵消,只要我们保证账本非负,那么我们最开始的平摊代价就一定是实际代价的一个上界

这里的会计法看起来很像对一个已知的平摊代价进行证明,其实不完全是这样的,这种方法的首先其实是对平摊代价进行猜测,然后会计法只是一种验证。

势能法

没错,和物理中的势能是差不多的东西。这种方法和会计法其实还是很像的,都是利用存储的方式。这里我们将存储的余额叫做势能

对于势能,我们给出两个关键点

  1. 每一个实际代价 ci 都将数据结构从Di-1改变为Di,所以我们的平摊代价就可以定义为:
    ci’ = ci + φ(Di) - φ(Di-1) 【φ代表势能函数】
  2. 如果平摊代价要比实际代价大,那么我们就说势能增加,否则势能降低。

所以我们将所有操作的平摊代价相加,会发现后面的势能函数基本上消没了。
也就是平摊代价的和 = 实际代价和+φ(Dn)-φ(D0),我们假设φ(D0)为0,这和物理的零势能面相似。
如果我们能证明φ(Dn)为正,那么我们就可以说明我们的平摊代价一定是实际代价的一个上界。

势能和会计的不同点
还记得高中的物理中,能量分析部分有一个公式,左边是面向过程,将每一部分加在一起;右边是面向结果,只需要判断整体的能量变化(具体记不清楚不敢乱叫)。
有一说一,其实会计和势能就是这样的,会计要求的是每时每刻账本为正,势能只需要保证最后势能为正就行。
因此,我们一般常用的还是势能法,相对简单。

势能法看着也像是用来对猜测进行验证的,但其实它也可以用来计算平摊代价,接下来我们看几个例子。

栈操作

我们已知的栈操作只有压栈和出栈,每一个的代价都是1,没什么好说的。
这次我们加一个操作:MULTIPOP(S,k),将栈顶的k个对象去掉,如果没有k个就清空整个栈。
我们可以分析出,该操作的实际代价为min{s,k},其中s为栈顶元素个数。

先用聚集法
我们将n步操作加在一起,但是我们都不知道每一种操作的个数啊。
别急,我们知道栈内元素的个数一定是小于等于n的,因为我们每一次只能压入一个元素,而我们取出元素的个数也一定小于n,所以整体下来最多也就压入n个、拿出n个(而且一定达不到),那么平均下来每一步的平摊代价就是2。

会计法
这里我们先对操作估计一个平摊代价,其中压栈代价为2,出栈代价为0。
感觉很奇怪,解释一下就好了。
首先,我们的账本就是整个,没错这就是账本。
当我们每一次压入一个元素,账本上就多了一项,平摊代价2支付了压栈的代价1并将剩下的部分存储在栈内;
当我们弹出一个元素,刚好账本上少了一个元素,拿来支付代价刚刚好。
因为栈内的元素个数非负,所以我们保证了估计的平摊代价的合理性。

势能法
势能法一定要先给出势能函数,这里的势能函数为栈内元素个数。
每一个实际代价为1,但是压栈时势能函数加一,按照我们上面的公式,平摊代价为2;
同理,出栈的平摊代价为0。
(这个例子就是势能法计算平摊代价,你说是我猜完了证明也行吧)

二进制计数器

看着特别高端,其实就是初始一串零,每一次进行加一。
000->001->010->……

先给出实现的伪代码:

INCREMENT(A)
i=0
while  i<length[A] and A[i]=1 A[i]=0;i=i+1;
If  i<length[A]A[i]=1

也就是说,每一次加一都是从最后一位开始,碰到1就翻转成0,碰到0或者溢出才结束。结束时需要判断一下,如果有需要就将0翻转成1,这和常识都相符。

这里面的操作是翻转0和翻转1,但我们要算平摊代价的操作是每一步的平摊代价

但我们每一步的代价应该怎么算?
我们默认每一次的翻转,不论是0到1还是1到0都一样,代价都是1。
第一步是1,第二步则是2,第三步是1,第四步是3……

乍一看,真的找不到规律,那么应该怎么算?
这里我们看的不是每一步的代价,而是每一位的代价

通过观察,我们可以看到对于第一位,是每一次都进行翻转,而第二位则是每两次进行一次翻转,依次进行,我们将整体进行求和,然后除以n,结果是小于2的,这里就不算了。
所以,我们说,此处的平摊代价为2。

会计法
这次我们的账本变成了数组,或者是字符串。
我们假设0->1的代价为2,一个支付实际代价,另一个存储在帐本上;
1->0的代价是0,账本上少了一个1,刚好用来支付代价。
和上面一样,1的个数非负,所以我们的平摊代价估计的很合理。

然后我们对整体来说,总代价和一定是小于2n的,所以我们给出的平摊代价为2。
(实际上我们只需要知道是O(1),所以也不需要特别精确)

势能法
还是先给出势能函数,这里的势能为计数器中1的个数
计数器中1的个数也是非负的。

假设我们在低i次操作将a个位置从1翻转成0,最多将一位翻转为1。(可能在最后一位溢出了么)
所以我们的势能差小于等于1-a,而我们的实际代价是小于等于a+1的,求和后平摊代价小于2。

(在这道题中,我们如果将初始的计数器不置零,那么对会计法来说就会有一点烦,因为要保证每一步判断账本是否为空;但是势能法只需要看最后的情况,所以简单了很多。)

动态表的扩张和收缩

这里我们主要讲的是扩张操作。

什么是动态表

想象成一个数组,假设其大小为size_T,其中的元素个数为num_T,可以用来存储东西,先不考虑移除。
当这个数组满了的时候,我们将这个数组扩大一倍,使其能接着存储。
这里就有一个装载因子的概念了,为num_T/size_T,代表的是这个表的存储元素比例,按照我们上述的操作,能保证装填因子大于等于0.5
接下来我们就要看操作了。
如果是正常的操作,就是存入一个元素,代价为1;
但如果很不幸碰到了元素个数满了,还需要将整个表进行扩张操作,这时的代价为1+a(a为扩张代价)
我们可以推断出的是,只有在a那一步,我们才需要扩展a,而a为2i,其中i为自然数。(包括0)

实际代价:
在2i步(i为自然数),代价为2i+1;剩下的部分,代价为1。

聚集分析:
首先将每一步都取一个1出来,然后将剩下的等比数列求和,

我们得到的平摊代价为3。

会计法
这次不能取元素个数为账本了,因为账本只增不减,明显不合理。

我们假设平摊代价为3,每一次的压入我们将1作为必要代价支付,同时将2的代价存储在表内没有对应代价为位置,当所有位置都有存款了,那么就需要将表进行扩张,同时所有的存款清空。
(第一步会特殊一点)


说着可能有一点复杂,但是实际上还是很简单的。

很明显,1元素个数非负,我们的代价是合理的。

势能法
我们的势能函数需要满足以下两条性质:

  1. 刚扩充完,φ(T)=0
  2. 表满时,φ(T)=size(T)

这两个条件不是必须,但是这样的条件能使我们的函数更加合理

想一下么,我们建立势能函数的目的不就是为了保证我们在进行大代价操作时的代价能被之前的势能(或者说是存款)抵消,上述的条件刚好卡着我们的目的。

所以我们的给出的势能函数为2*num_T - size_T

我们还是假设平摊代价为3。

对于不扩张的操作,势能增加2,实际代价为1,按照公式平摊代价为3;
对于需要扩张的操作,size_T扩大为两倍,num_T加一(加一千万别忘了),
所以势能差为2(num_T+1) - 2size_T - [2num_T - size_T] = 2-size_T,而我们的实际代价为1+size_T,求和仍为3,得证。(或者说我们不给出之前的假设,这时就是通过这些步骤算出平摊代价为3)

收缩操作

一直在讲扩张的问题,这里补充一下收缩,收缩是因为删除元素引起的,我们为了保证表不至于太空旷,将表的装载因子定义为[1/4,1]。
也就是说当我们装满了的时候,也就是装载因子为1,我们就需要进行扩张;
如果因为删除过多导致装载因子为1/4,就需要将表收缩为之前的一半。
这两个操作很明显都能保证装载因子为1/2。

因为我们的大代价操作有扩张和收缩两个,而这两个操作结束都能保证装载因子为1/2,而此时的势能函数最优应当为0,所以我们定义的势能函数是这样的:

类似于动态表的问题

之前见过一个和这个很像的问题,叫做翻转栈的数据结构,是bill提出的,该数据结构支持一种操作Flip_push(),先正常压栈,如果发现栈内元素个数为2的幂次,那么就将栈进行翻转。

举例:(1)⇒(2,1)⇒(2,1,3)⇒(4,3,1,2)【右边为栈顶】

仔细分析一下,每一次都有压栈的操作,代价为1,每2i步,我们还有一个2i的代价,其实就是和动态表相同。

其中,会计法我们采取和动态表相同的记账方式,每一步平摊代价为3,先支付1的代价入栈,然后将剩下两个存储在没有存款的元素上,当每一个元素都有存款就进行翻转,同时将存款清空;

势能法的势能函数为2(num_T-size_T),但这次的定义产生了变化。
我们num_T的定义还是之前的定义,但是size_T为上一次翻转的元素的个数
当我们刚好需要翻转时,size_T和num_T相同,势能函数为0;而即将翻转的时候是势能函数最大,这样的定义也算合理。

平摊分析的三种方法(聚集、会计和势能)+举例(栈操作、二进制加法器、动态表)相关推荐

  1. pandas相关性分析的三种方法

    使用pandas中的皮尔逊,肯德尔和斯皮尔曼做特征相关性分析 三种系数的具体试用范围可参考: https://blog.csdn.net/qrdsy_lrf/article/details/79227 ...

  2. 剑指 Offer 07. 重建二叉树【千字分析,三种方法】

    立志用最少的代码做最高效的表达 输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字. 例如,给出 前序遍历 preorder = [3,9,2 ...

  3. [转]asp.net导出数据到Excel的三种方法

    原文出处:asp.net导出数据到Excel的几种方法(1/3) .asp.net导出数据到Excel的几种方法(2/3).asp.net导出数据到Excel的几种方法(3/3) asp.net导出到 ...

  4. mybatis批量更新数据三种方法效率对比

    探讨批量更新数据三种写法的效率问题. 实现方式有三种, 1> 用for循环通过循环传过来的参数集合,循环出N条sql,需要在db链接url后面带一个参数  &allowMultiQuer ...

  5. ArcEngine获取字段唯一值的三种方法

    在做GIS数据处理时,我们经常需要获取某个字段的唯一值.我在这里总结了三种方法,下面分别进行说明. 方法一:读取表记录 这种方法就是逐条读取记录,然后选用合适的数据结构进行查重,它的好处就在于:不必去 ...

  6. 计算机垂直同步在哪里更改,Win7系统开启与关闭垂直同步功能三种方法【图文教程】...

    win7系统下玩游戏开启垂直同步功能可以获得更好的游戏效果,但是有些游戏例外,必须关闭垂直同步功能才有完美体验效果.那么最新win7 64位系统如何开启与关闭垂直同步功能,关闭垂直重同步一般需要在显卡 ...

  7. php遍历数组哪个效率高,PHP遍历数组的三种方法及效率对比分析

    PHP遍历数组的三种方法及效率对比分析 发布于 2015-03-04 21:55:27 | 129 次阅读 | 评论: 0 | 来源: 网友投递 PHP开源脚本语言PHP(外文名: Hypertext ...

  8. java equals 判断空_Java 判断字符串是否为空的三种方法与性能分析

    [java中判断字符串是否为数字的三种方法  1>用JAVA自带的函数 public static boolean isNumeric(String str){   for (int i = s ...

  9. 为什么系统调用会消耗较多资源?系统调用的三种方法:软件中断(分析过程)、SYSCALL指令、vDSO(虚拟动态链接对象linux-vdso.so.1)

    Table of Contents 软件中断 汇编指令 vDSO 总结 参考文章 系统调用是计算机程序在执行的过程中向操作系统内核申请服务的方法,这可能包含硬件相关的服务.新进程的创建和执行以及进程调 ...

最新文章

  1. MPLS学习一些问题(一)
  2. jQuery快速入门专题
  3. 玩点创意编程,发现另一个世界
  4. 通过反射运行配置文件内容
  5. ABAP SET UPDATE TASK LOCAL的测试
  6. hades武器第四形态解锁_凯多的第四个技能预告——冰冻!
  7. javaWeb服务详解(含源代码,测试通过,注释) ——applicationContext-Service.xml
  8. 当我们的代码遇到问题的时候....;要想不遇到问题,写代码的时候要.....
  9. c语言格式对齐填充_C ++中类的大小 课堂上的填充和对齐| 派生类的大小
  10. 量子计算机怎么算有用,如何在量子计算机上实现经典计算
  11. win7 64位系统配置服务器,Tomcat服务器win764位配置方法
  12. mysql 部署最佳实践_MySQL安装脚本最佳实践
  13. grub4dos挂载iso linux,GRUB4DOS加载ISO启动光盘完美解决方案
  14. 使用BenchMarkSQL测试openGauss
  15. 第32期:索引设计(索引设计详细规范)
  16. 选择java大数据开发方向学习,应该怎么规划学习路线
  17. 新发现-网盘珍惜资源网址(值得收藏)
  18. 使用深度学习进行三维脑肿瘤分割
  19. 把照片变成字母符号软件下载 抖音上特别说的照片变成TXT文档软件
  20. ubuntu18完全安装Openpose指南

热门文章

  1. 极光id 唯一性问题
  2. java for 下标_Java如何在 Word 中设置上、下标
  3. NGUI之实现连连看小游戏
  4. JavaScript for、for..in、for..of、forEach的区别
  5. 新手用户如何选购UCLOUD优刻云主机的配置?
  6. 安装SSL证书遇到困难怎么办?
  7. 秒杀99%的海量数据处理面试题
  8. python写圣诞祝福语_有哪些高逼格的猪年春节祝福语?
  9. 常见的 CSRF、XSS、sql注入、DDOS流量攻击
  10. js中数组拼接成字符串