2023-01-12 22:15阅读: 298评论: 7推荐: 10

「闲话随笔」势能分析法

「闲话随笔」势能分析法

点击查看目录

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

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

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

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

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

简介

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

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

分析

设第 i 次操作的时间复杂度为 ai,显然总复杂度为 i=1nai

构造一个势能函数 ϕ(i) 表示第 i 次操作后的势能,设 Δϕ(i) 表示单次的势能变化,即 Δϕ(i)=ϕ(i)ϕ(i1)

设摊还代价 bi=ai+Δϕ(i),则:

i=1nai=(i=1nai+Δϕ(i))i=1nΔϕ(i)=i=1nbi+ϕ(0)ϕ(n)

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

不对就怪了

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

看看例题就都明白了。

例题

二进制计数器

题意:

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

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

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

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

显然 Δϕ(i)=1x

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

然后化简原式得到复杂度 2nϕ(n)2n

单调栈

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

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

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

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

显然 Δϕ(i)=1x

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

然后化简原式得到复杂度 2nϕ(n)2n

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

Splay

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

定义 x 为一棵 splay 上的一个节点,xx 旋一次后的位置,|x| 为以 x 为根的子树的大小,ϕj(x)=log2|x| 为一次 splay 操作中第 j 次操作后节点 x 的势能,Φj 为一次 splay 操作中第 j 次操作后整棵树的势能。

然后我们开始分析三种旋转情况的 ai:

  1. 旋上根(zig):

    屏幕截图_20230112_192746

    可以发现转一次只会有 xy 的势能发生变化。

    显然 ϕj(x)=ϕj1(y)

    bzig=aj+ΔΦj=1+ϕj(x)+ϕj(y)ϕj1(x)ϕj1(y)=1+ϕj(y)ϕj1(x)ϕj(x)ϕj1(x)

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

    屏幕截图_20230112_192904

    可以发现转一次只会有 x,yz 的势能发生变化。

    显然 ϕj(x)=ϕj1(z)

    bzig-zig=aj+ΔΦj=2+ϕj(x)+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)ϕj1(z)=2+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)

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

    不难发现 |x|+|z|+1=|x|,因此 |x|2>4|x||z|,那么:

    ϕj1(x)+ϕj(z)2ϕj(x)=log2|x||z||x|2log214=2

    然后代入一下原式:

    bzig-zig=2+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)=2ϕj(x)ϕj1(x)ϕj(z)+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)=2ϕj(x)2ϕj1(x)+ϕj(y)ϕj1(y)3(ϕj(x)ϕj1(x))

    这样就求出了它的上限。

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

    屏幕截图_20230112_192911

    比较像上面的式子。

    bzig-zag=aj+ΔΦj=2+ϕj(x)+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)ϕj1(z)=2+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)

    然后由于 |y|+|z|+1=|x|

    ϕj(y)+ϕj(z)2ϕj(x)=log2|y||z||x|2log214=2

    然后代入原式:

    bzig-zag=2+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)=2ϕj(x)ϕj(y)ϕj(z)+ϕj(y)+ϕj(z)ϕj1(x)ϕj1(y)=2ϕj(x)ϕj1(x)ϕj1(y)2(ϕj(x)ϕj1(x))

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

显然一次 splay 操作会进行若干次 zig-zigzig-zag 和至多一次 zig,且三者上限都不超过 3(ϕj(x)ϕj1(x)),那么:

bij=1k3(ϕj(x)ϕj1(x))=3(ϕk(x)ϕ0(x))=O(log2|x||x|)O(log2n)

然后由于 0Φinlog2nnlog2nΦ0Φnnlog2n

所以总复杂度为:

i=1mai=i=1mbi+Φ0ΦnO(mlog2n)+O(nlog2n)=O((m+n)log2n)

The End

本文作者:K8He

本文链接:https://www.cnblogs.com/K8He/p/chat_20230112.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   K8He  阅读(298)  评论(7编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起