一、合计方法
合计方法顾名思义,就是把前n个方程所需要的费用加起来,然后再把这个加起来的数÷n即可。
例1:建立一个动态的数组,初始长度为1。每次都插入一个数,而当长度为k的数组插入k+1个数时,会开辟一个新的2k的数组。插入数字的消费记作1,而将数字从旧数组移动到新数组也记作1。所以插入每个数的消费分别为:
第1个数:1
第2个数:2 数组开辟到2
第3个数:3 数组开辟到4
第4个数:1
第5个数:5 数组开辟到8
第6、7、8个数:1
……
求插入每个数字的平均算法复杂度。
很容易能看出,插入每个数所需的话费分别为:
由此,插入前n个数的合计消费为:
因此最终答案为O(n)/n=O(1)
可以看出:合计方法只要知道每次行动究竟耗费多少,那么就能找出它的总和来。剩下的就是用不等式去掉取整部分等问题了。
二、记账方法
记账方法的核心就是给部分步骤每一步安排多余的费用,在其余的步骤中支付这些多余的费用。至于费用的具体安排需要自己寻找,安排费用的最终目标是将不均匀的操作分到均匀的操作里面。
还是以上面的例题为例,不均匀的操作为扩大数组后各个数字的复制,均匀的操作为每个新数字的插入。
我们令每个新数字的插入消费为3(这个数字需要自己想,可以借助之前的合计方法)。自己插入先消耗一个。然后在插入他之后第一次扩张数组时,一个用于自己插入,另一个用于前半段的自己插入。这样就能把不均匀的分配到均匀之中。
为了加深理解,再来一个例题
例2:有一个2进制计数器,某位从1变成0或从0变成1的花费记作1,那么求每次加1时算法消耗的平均复杂度。
在这里,只要计算几次就会发现:每加一次必定会有一个位变1,而变0的位则不固定。所以令1消费2,1变回0抵消掉1的消费,那么复杂度就是2n,也就是O(n)。
综上所述,记账方法的过程是把均匀的操作的消费增加一些,之后不均匀的操作用均匀的操作中和掉,从而得到平均分摊的效果。
三、势能方法
势能方法是在正常的计算中添加一个势能函数Φ。如果把记账方法用每一步提前存储一些钱来描述的话,势能方法的Φ就是一个总的大存钱罐。当平均消费较低时Φ就多存一些,平均消费较高时就从Φ取,以达到平均的效果。即消费低时Φ增加,消费高时Φ减小。
以例1为例,在这里很明显不是时 ,消费较少需要存,根据合计方法的经验,先存个2,然后在时,把存的基本上全取出来,那就剩0,以这个为基本思想,那么最初定义公式为
那么分开讨论,当时
加了势能变化的消费为
当时
加了势能变化的消费为
可以看出两种情况下加了势能变化的消费都是常数,所以是O(1)
那么对于势能函数(numbers指插入的数字数,size指动态数组大小),也是相同的处理情况,最后算出来结果也是O(1),这里不在赘述。