Algs4-1.4.32均摊分析

1.4.32均摊分析。请证明,对一个基于大小可变的数组实现的空栈的M次操作访问数组的次数和M成正比。
答:设N为2的幂

1)连续N次的push操作均摊后每次push操作访问数组的次数

设数组长度为L,一次延长需要的数组访问次数为:初始化2L长度的数组需要将数组元素置初值,需要2L次写操作。将原数组中L个元素读出,然后写入到新数组各需L次读、L次写操作。那么数组一次延长需要4L次数组元素的访问,也就是上一次数组长度的4倍。

数组开始长度为1,之后每次延长都是上一次长度的2倍,数组长度最后一次延长是从N/2至N。连续N次push延长数组需要访问数组元素次数为:

  图片

N次push操作需要对数组元素进行N次写访问,那么最终的数组访问次数=4N-4+N=5N-4,连续N次push操作均摊后每次push操作对数组访问次数=(5N-4)/N。

2)最坏的连续push操作均摊后每次push操作访问数组的次数

设栈长度达到N/2+1时数组长度从N/2延长至N。连续的push操作使数组从长度N延长至2N是最坏的连续push情况。栈长度在N/2+2至N区间进行的N/2-1次push操作不会促使数组延长,此区间每次push对应一次数组元素的写访问。栈长度从N变为N+1时需要一次push操作和一次数组延长,一次push操作对应一次数组写操作,一次数组延长对应4N次数组访问。那么栈长度从N/2+1变为N+1需要进行的数组访问次数为N/2+4N,push操作次数为N/2,均摊后每次push操作对应的数组访问次数为(N/2+4N)/(N/2)=9

3)连续的N次pop操作均摊后每次pop操作访问数组的次数,不考虑pop之前的push

设数组长度为L,一次收缩需要的数组访问次数为:初始化L/2长度的数组需要将数组元素置初值,需要L/2次写操作。将原数组中L/4个元素读出,然后写入到新的数组各需L/4次读、L/4次写,总的次数为L。那么数组一次收缩需要L次数组访问,也就是收缩后长度的2倍。

数组开始长度为N,之后每次收缩后新的长度都是原有长度的1/2,每一次收缩需要访问的数组次数为收缩后长度的2,因为在栈长度/数组长度=1/4时才收缩,所以最后一次收缩是在数组长度为4时。连续Npop收缩数组需要访问数组元素次数为:
图片

N次pop操作需要对数组元素进行N次读访问,那么最终的数组访问次数=2N-4+N=3N-4,连续N次pop操作均摊后每次pop操作对数组访问次数=(3N-4)/N

4)最坏的连续pop操作均摊后每次pop操作访问数组的次数,不考虑pop之前的push

设数组完成最近一次的收缩后数组长度为4N,栈长度为2N,数组长度收缩至2N需要访问的次数为:连续进行N次pop,每次pop需要一次数组元素读访问,经过N次pop后栈长度个数减至N,触发数组收缩至2N的操作,数组收缩至2N时需要初始化新的数组,为数组元素置空操作需要2N次写访问,将原数组中的N个元素读出需要N次读访问,再将N个元素写入新数组需要N次写访问,那么总的访问次数为: N+2N+N+N=5N,pop操作次数为N,均摊成本=数组访问次数/操作次数=5N/N=5

 

5)连续N次push操作后连续N次pop操作均摊访问数组的次数

依据上述1)得出连续N次push需要5N-4次访问,依据上述3)连续N次pop需要3N-4次访问,共需要((5N-4)+(3N-4))=8N-8,操作次数N+N=2N,均摊成本=(8N-8)/2N

 

6)最坏情况下连续的push与pop均摊访问数组的成本

设数组完成最近一次延长后数组的长度是2N,栈长度是N+1。连续进行N次push后数组延长至4N,栈长度为2N+1,push操作对数组访问的次数为4N次的数组初始化,2N次对旧数组元素读,2N次对新数组写,N次push对应N次写,一共9N次访问。数组长度从4N,栈长度从2N+1使数组收缩一次需要N+1次pop,需要对数组访问的次数为2N次数组初始化,N次对旧数组元素读,N次对新数组写,N+1次pop对应N+1次读,一共4N+1次访问。N次push与N+1次pop的对数组均摊访问次数=(9N+4N+1)/(2N+1)=(13N+1)/(2N+1)

 

根据以上6种情况的分析,混合操作均摊后对数组访问的次数都是常数,那么M次混合操作在任何情况下对数组的访问次数成正比。

 附1:基于可变数组的栈代码
import java.util.Iterator;
public class ResizingArrayStack<Item> implements Iterable<Item>
{
    private Item[] a=(Item[]) new Object[1];
    private int N=0;
    public boolean isEmpty(){return N==0;}
    public int size(){return N;}
    private void resize(int max)
    {
        Item[] temp=(Item[]) new Object[max];
        for(int i=0;i<N;i++)
            temp[i]=a[i];
        a=temp;
    }//end resize
    public void push(Item item)
    {
        if(N==a.length) resize(2*a.length);
            a[N++]=item;
    }//end push
    public Item pop()
    {
        Item item=a[--N];
        a[N]=null;
        if(N>0 && N==a.length/4) resize(a.length/2);
        return item;
    }//end pop
    public Iterator<Item> iterator()
    {return new ReverseArrayIterator();}
   
    private class ReverseArrayIterator implements Iterator<Item>
    {
        private int i=N;
        public boolean hasNext(){return i>0;}
        public Item next(){return a[--i];}
        public void remove(){}
     }
}
附2:求和公式
图片

posted @ 2018-10-26 09:22  修电脑的龙生  阅读(403)  评论(0编辑  收藏  举报