「闲话随笔」势能分析法

Posted Keven-He

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「闲话随笔」势能分析法相关的知识,希望对你有一定的参考价值。

「闲话随笔」势能分析法

点击查看目录

这闲话已经被催了两天了,累死我了。

感谢 joke3579 帮我找到了 Tarjan 的论文虽然没看懂只截了一下里面的图。

语文考了 82,需要单独给语文老师发作业,很闹心。

今日推歌:盲龙默虎 feat.洛天依 vs 言和

iKz 老师的《一年一度武斗大赛》系列是不是快要更了?

简介

一种挺神奇的分析时间复杂度的方法。

一个算法/数据结构单次操作的复杂度难以计算时可以用势能分析法。

分析

设第 \\(i\\) 次操作的时间复杂度为 \\(a_i\\),显然总复杂度为 \\(\\sum_i=1^na_i\\)

构造一个势能函数 \\(\\phi(i)\\) 表示第 \\(i\\) 次操作后的势能,设 \\(\\Delta_\\phi(i)\\) 表示单次的势能变化,即 \\(\\Delta_\\phi(i)=\\phi(i)-\\phi(i-1)\\)

设摊还代价 \\(b_i=a_i+\\Delta_\\phi(i)\\),则:

\\[\\sum_i=1^na_i=(\\sum_i=1^na_i+\\Delta_\\phi(i))-\\sum_i=1^n\\Delta_\\phi(i)=\\sum_i=1^nb_i+\\phi(0)-\\phi(n) \\]

不知道我在说什么,对不对?看起来啥用没有,对不对?

不对就怪了

实际上我们虽然无法算出具体的 \\(a_i\\),但是可以用未知数表示出来,这个时候构造一个优秀的势能函数,就可以巧妙地将 \\(b_i\\) 化为一个数或是求出其上限。

看看例题就都明白了。

例题

二进制计数器

题意:

一个二进制下的计数器,每次累加 \\(1\\),做 \\(n\\) 次累加,求操作的次数。

定义一次操作为一位上发生变化,因此每次累加 \\(1\\) 可能会带有多次操作。

设每次累加会有 \\(x\\)\\(1\\) 变为 \\(0\\),那么 \\(a_i=x+1\\)(还会有一个 \\(0\\) 变为 \\(1\\))。

那么构造势能函数 \\(\\phi(i)\\) 表示累加 \\(i\\) 次后数字里有多少个一。

显然 \\(\\Delta_\\phi(i)=1-x\\)

然后就发生了一件神奇的事:\\(b_i=(x-1)+(1-x)=2\\)

然后化简原式得到复杂度 \\(2n-\\phi(n)\\le 2n\\)

单调栈

不会单调栈就来学这个是不是有点过猛了。

显然单调栈不用这么麻烦地分析,但确实可以这么用。

设每次操作会有 \\(x\\) 个元素弹出,\\(1\\) 个元素加入,那么 \\(a_i=x+1\\)

那么构造势能函数 \\(\\phi(i)\\) 表示当前栈内元素个数。

显然 \\(\\Delta_\\phi(i)=1-x\\)

然后就发生了一件神奇的事:\\(b_i=(x-1)+(1-x)=2\\)

然后化简原式得到复杂度 \\(2n-\\phi(n)\\le 2n\\)

然后还发生了一件神奇的事:这个过程和上个过程居然没啥区别。

Splay

默认读者已经会 splay 了,不会的话去网上搜 或者等我平衡树学习笔记写完了再看

定义 \\(x\\) 为一棵 splay 上的一个节点,\\(x\'\\)\\(x\\) 旋一次后的位置,\\(\\left|x\\right|\\) 为以 \\(x\\) 为根的子树的大小,\\(\\phi_j(x)=\\log_2\\left|x\\right|\\) 为一次 splay 操作中第 \\(j\\) 次操作后节点 \\(x\\) 的势能,\\(\\Phi_j\\) 为一次 splay 操作中第 \\(j\\) 次操作后整棵树的势能。

然后我们开始分析三种旋转情况的 \\(a_i\\):

  1. 旋上根(\\(\\textzig\\)):

    可以发现转一次只会有 \\(x\\)\\(y\\) 的势能发生变化。

    显然 \\(\\phi_j(x)=\\phi_j-1(y)\\)

    \\[\\beginaligned b_\\textzig&=a_j+\\Delta_\\Phi_j\\\\ &=1+\\phi_j(x)+\\phi_j(y)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ &=1+\\phi_j(y)-\\phi_j-1(x)\\\\ \\endaligned \\]

  2. 三点共线(\\(\\textzig-zig\\)):

    可以发现转一次只会有 \\(x,y\\)\\(z\\) 的势能发生变化。

    显然 \\(\\phi_j(x)=\\phi_j-1(z)\\)

    \\[\\beginaligned b_\\textzig-zig&=a_j+\\Delta_\\Phi_j\\\\ &=2+\\phi_j(x)+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)-\\phi_j-1(z)\\\\ &=2+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ \\endaligned \\]

    注意到这个 \\(2\\) 长得很难看,尝试把它去掉。

    不难发现 \\(\\left|x\\right|+\\left|z\'\\right|+1=\\left|x\'\\right|\\),因此 \\(\\left|x\'\\right|^2>4\\cdot\\left|x\\right|\\cdot\\left|z\'\\right|\\),那么:

    \\[\\phi_j-1(x)+\\phi_j(z)-2\\phi_j(x)=\\log_2\\frac\\left|x\\right|\\cdot\\left|z\'\\right|\\left|x\'\\right|^2\\le\\log_2\\frac14=-2 \\]

    然后代入一下原式:

    \\[\\beginaligned b_\\textzig-zig&=2+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ &=2\\phi_j(x)-\\phi_j-1(x)-\\phi_j(z)+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ &=2\\phi_j(x)-2\\phi_j-1(x)+\\phi_j(y)-\\phi_j-1(y)\\\\ &\\le3(\\phi_j(x)-\\phi_j-1(x)) \\endaligned \\]

    这样就求出了它的上限。

  3. 三点不共线(\\(\\textzig-zag\\)):

    比较像上面的式子。

    \\[\\beginaligned b_\\textzig-zig&=a_j+\\Delta_\\Phi_j\\\\ &=2+\\phi_j(x)+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)-\\phi_j-1(z)\\\\ &=2+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ \\endaligned \\]

    然后由于 \\(\\left|y\'\\right|+\\left|z\'\\right|+1=\\left|x\'\\right|\\)

    \\[\\phi_j(y)+\\phi_j(z)-2\\phi_j(x)=\\log_2\\frac\\left|y\'\\right|\\cdot\\left|z\'\\right|\\left|x\'\\right|^2\\le\\log_2\\frac14=-2 \\]

    然后代入原式:

    \\[\\beginaligned b_\\textzig-zig&=2+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ &=2\\phi_j(x)-\\phi_j(y)-\\phi_j(z)+\\phi_j(y)+\\phi_j(z)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ &=2\\phi_j(x)-\\phi_j-1(x)-\\phi_j-1(y)\\\\ &\\le2(\\phi_j(x)-\\phi_j-1(x)) \\endaligned \\]

现在我们把三种旋转的摊还代价算出来了,问题变成了如何算出一次 splay 操作的摊还代价。

显然一次 splay 操作会进行若干次 \\(\\textzig-zig\\)\\(\\textzig-zag\\) 和至多一次 \\(\\textzig\\),那么:

\\[b_i\\le\\sum_i=1^k3(\\phi_j(x)-\\phi_j-1(x))=3(\\phi_k(x)-\\phi_0(x))=O(\\log_2\\frac\\left|x\'\\right|\\left|x\\right|)\\le O(\\log_2n) \\]

然后由于 \\(0\\le\\Phi_i\\le n\\log_2n\\)\\(-n\\log_2n\\le\\Phi_0-\\Phi_n\\le n\\log_2n\\)

所以总复杂度为:

\\[\\sum_i=1^ma_i=\\sum_i=1^mb_i+\\Phi_0-\\Phi_n\\le O(m\\log_2n)+O(n\\log_2n)=O((m+n)\\log_2n) \\]

\\[\\Huge\\mathfrakThe\\ End \\]

以上是关于「闲话随笔」势能分析法的主要内容,如果未能解决你的问题,请参考以下文章

算法导论摊还分析—聚合分析核算法势能法

bzoj3211 花神游历各国 线段树,势能分析

复杂度分析---平摊分析(Amortized Analysis)

分块&势能分析!!!(orz Flash!)

ICPC网络赛第二场 L.Euler Function 欧拉函数性质+势能线段树

ICPC网络赛第二场 L.Euler Function 欧拉函数性质+势能线段树