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

2023-11-10

平摊分析(摊还分析)

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

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

三种方法

聚集法

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

会计法

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

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

势能法

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

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

  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;而即将翻转的时候是势能函数最大,这样的定义也算合理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 计算机本科生毕业设计题目(三)

    资源下载 计算机本科生毕业设计题目 资源下载 计算机论文题目精选50篇 文章目录 一 安卓 二 java 三 ASP NET C 题目 毕设通用50篇 一 安卓 安卓 001 个人事务管理系统 安卓 002 手机订餐系统 安卓 003 无线
  • 【Redis入门笔记 07】数据库持久化

    目录 持久化是个啥 持久化策略 RDB 持久化策略 AOF 持久化是个啥 我们都知道电脑中的内存一般指的是 DRAM 属于易失性存储器 里面的电容是会漏电的 需要通电来定期刷新 当断电以后内存中的数据会慢慢消失 以速度著称的 redis 就
  • 宋浩概率论与数理统计-第三章-笔记

    概率论与数理统计 第三章 3 1 1 二维随机变量及其分布函数 联合分布 边缘分布 3 1 2 二维离散型的联合分布和边缘分布 3 1 3 二维连续型的联合分布和边缘分布 联合分布 边缘分布 3 2 1 条件分布 3 2 2 离散型的条件分
  • 创建qml自定义视频源(Qt6.3.1+取景器帧)

    前言 笔者之前记录的是Qt5 15的 当前Qt6系列无法使用 笔者本次记录下Qt6中 如何创建qml自定义视频源 一 获取视频帧 这个笔者在之前的文档中记录过 本次算是重复了 1 通过videoSink获取 关键代码如下 Camera id
  • 2、kettle知识点系列之kettle向redis同步数据

    kettle向redis同步数据 网上kettle向redis同步数据的完整案例不是很多 本文将以案例形式对整个过程进行详细讲解 一 案例描述 本文以最简单的案例描述 大家在应用过程中可根据实际情况进行调整 现有学生表和成绩表 如何将表中的
  • 转:基于Spark的电影推荐系统(包含爬虫项目、web网站、后台管理系统以及spark推荐系统)

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net u011254180 article details 80006453 本次项目是基于
  • 算法作业(4):旅行者问题

  • 华为官方翻新产品秒杀活动来袭,官方正品,7折优惠,真香!

    4月24日 华为商城 微博官宣 4月26日12 00和20 00在华为商城APP内将举办两场超级秒杀节活动 其中包括7折优惠的2款华为官方翻新手机 分别是nova 9和nova 9 Pro 华为官方翻新nova 9手机秒杀直降660元 15
  • 前端实现语音播放

    0 Web Speech API Web Speech API 使您能够将语音数据合并到 Web 应用程序中 Web Speech API 有两个部分 SpeechSynthesis 语音合成 文本到语音 TTS 和 SpeechRecog
  • IC 的资源体系

    信息共享空间是集信息资源 各类软硬件设施于一体的一个综合性动态服务模 式 其最大特点是资源共享 因此 要加强电脑终端 打印机等硬件设施的建设 同时强调文献数据库 电子图书 学位论文 各类免费软件等信息资源的建设 提 供知识导航 跨库检索 开
  • SysTick定时器

    SysTick定时器 SysTick定时器也叫SysTick滴答定时器 它是Cortex M3内核的一个外设 它是一个24 位向下递减的定时器 每计数一次所需时间为1 SYSTICK SYSTICK是系统定时器时钟 它可以直接取自系统时钟
  • 【概念梳理】激活函数

    一 引言 常用的激活函数如下 1 Sigmoid函数 2 Tanh函数 3 ReLU函数 4 ELU函数 5 PReLU函数 6 Leaky ReLU函数 7 Maxout函数 8 Mish函数 二 激活函数的定义 多层神经网络中 上层节点
  • CoordinatorLayout的使用-Androidx

    折叠效果实现核心 CoordinatorLayout AppBarLayout CollapsingToolbarLayout 1 build gradle dependencies implementation com google an
  • Elasticsearch7.7 基础教程 1

    Elasticsearch7 7 基础教程 1 以下简称es7 7 es7 7的安装 1 官网下载 https www elastic co cn downloads elasticsearch 2 解压文件 3 在安装文件夹下的bin目录
  • Ajax session一直变,ajax异步session值不唯一 总是改变 解决办法

    public void doFilter ServletRequest servletRequest ServletResponse servletResponse FilterChain filterChain throws IOExce
  • 获取硬件信息的delphi源码(CPUID、操作系统、Mac物理地址、计算机名称、IP地址、用户名)

    转载请保留本文链接地址 http blog csdn net sushengmiyan article details 8545673 作者 sushengmiyan 2013 01 26 备注 功能 硬件信息获取单元 unit Appli
  • 使用nginx做为http-flv服务如何解决跨域问题

    什么是跨域 跨域是指浏览器的同源策略限制 这个策略会阻止一个域的javascript脚本和另外一个域的内容进行交互 如果一个请求url的协议 域名 端口三者之间任意一个与当前页面的url不同即为跨域 如下图所示即为跨域时的报错 使用ngin

随机推荐

  • idea git操作

    图片有的 是idea界面 有的是Android studio界面 当成字典看 不用记 你知道自己想操作仓库时 知道自己曾写过这篇文章就行 目录 引入git别的仓库的其它模块 创建 Git 分支并且 Push 删除分支 删除分支的文件 And
  • eclipse IDE的安装和常用配置教程(详细)

    eclipse IDE的安装和常用配置 第一步 安装配置JDK 打开eclipse需要先安装和配置好JDK 所以需要提前配置JDK 教程链接如下 https blog csdn net weixin 46028577 article det
  • HDFS简单测试

    使用Hadoop的Java客户端API操作分布式文件系统 获取文件系统实现 hdfs master01 9000 FileSystem get URI uri Configuration conf String user fs defaul
  • android 功能模块之通讯模块四

    Android通讯录开发之通讯录联系人搜索功能最新实现 2014年1月13日 之前的有两篇博客介绍了如何解决通讯录搜索功能的问题 那些方法都是从网上搜集 然后经过自己整理试验之后的 但在项目测试人员给我反馈 似乎还是存在一些问题 比如一些简
  • 【Flutter 2-10】Flutter手把手教程UI布局和Widget——流式布局Wrap

    作者 弗拉德 来源 弗拉德 公众号 fulade me Wrap 在Flutter中Wrap是流式布局控件 Row和Column在布局上是很好用 但是有一个缺点 如果当子控件数量过多导致Row或Column装载不下的时候 就会出现UI页面上
  • cdn 引入的资源需要通过 externals 排除打包哦~

    cdn 指的是通过相互连接的网络系统 使用最靠近用户的服务器将音乐 图片等资源以高效率和低成本的方式将内容传递给用户 在 webpack 中 我们可能会将引入的第三方资源会编译成单独的文件 作为静态资源放到服务器上 但有些库它本身就有 cd
  • 结构体大小和类大小的计算

    1 结构体大小的计算 当为空结构体时 其大小为1 选取结构体中类型字节数最大的最为对齐符 注意 是最大的类型字节数 例如 int a 10 并不是以40作为对齐符 每次申请对齐符个字节大小的内存 当内存不够时才继续申请 举例 struct
  • 特征选择&特征提取

    特征 在一些实际问题中 我们得到的样本数据都是多个维度的 即一个样本是用多个特征来表征的 比如在房价预测的问题中 影响房价y的因素有房子面积x1 卧室数量x2等 我们得到的样本数据就是 x1 x2 这样一些样本点 这里的x1和x2又被称为特
  • Maven阿里云镜像配置

    在setttins xml文件中找到标签对 进行修改
  • OpenWRT docker安装homeassistant、node-red、zigbee2mqtt

    1 安装 Docker 和 Docker Compose opkg update opkg install docker compose 2 创建 Home Assistant 的配置文件目录和数据目录 mkdir p opt hassio
  • 晶振PCB Layout

    晶振PCB Layout 前提摘要 个人说明 限于时间紧迫以及作者水平有限 本文错误 疏漏之处恐不在少数 恳请读者批评指正 意见请留言或者发送邮件至 noahpanzzz gmail com 参考 B站唐老师讲电赛 跟着大厂学画PCB 2
  • 静态代码检测工具:PC-Lint(for c/c++)

    PC Lint是C C 软件代码静态分析工具 你可以把它看作是一种更加严格的编译器 它不仅可以检查出一般的语法错误 还可以检查出那些虽然符合语法要求但不易发现的潜在错误 C语言的灵活性带来了代码效率的提升 但相应带来了代码编写的随意性 另外
  • 20130828可注册域名列表

    aabkx com aapun com abmrw com abnks com acphq com acsgq com actcL com aderz com adjni com adojj com adyjy com afabd com
  • mysql查询以逗号分隔数据

  • 静态代码扫描的原理

    静态代码扫描存在的价值 研发过程 发现BUG越晚 修复的成本越大 缺陷引入的大部分是在编码阶段 但发现的更多是在单元测试 集成测试 功能测试阶段 统计证明 在整个软件开发生命周期中 30 至 70 的代码逻辑设计和编码缺陷是可以通过静态代码
  • 阿里 ARouter的github地址

    不知道为什么搜不到 于是直接去github搜了下 下面是地址 https github com alibaba ARouter
  • 【编程语言】java--jsp--javabean中的scope用法小解

    javabean中的scope用法小解 1 scope取值page JSP分配给每个bean是互不相同的 虽然bean的功能是一样 但是占据不同的内存单元 bean的有效范围是当前页面 2 scope取值session JSP分配给每个be
  • ✖ 2 problems (0 errors, 2 warnings) 0 errors and 2 warnings potentially fixable with the `--fix`

    1 说有两个警告 需要删除一个点 和加一个回车 但我找不到位置 所以直接使用 2 报警告的原因 我创建项目的时候选择了eslint 代码规范 3 解决办法 执行npm run lint fix自动修复警告 npm run lint fix
  • Python Get()函数用法介绍

    一 简介 Python是一种高级编程语言 它具有简单 易学 高效等特点 而Python get 函数是其中一个重要的函数 该函数用于返回指定键的值 如果键不存在 则返回默认值None 下面将从各个方面对Python get 函数做详细的阐述
  • 平摊分析的三种方法(聚集、会计和势能)+举例(栈操作、二进制加法器、动态表)

    平摊分析 摊还分析 我们有时候会有一个算法 或者只是单纯的一系列操作 当我们需要将这一些操作计算一个平均代价 但是又不涉及概率的问题 我们就可以使用平摊分析 就比如一个月的账单 可能每一天都是正常的一日三餐 但是有一个周末出去玩花的钱可能会