算法实验报告1

[在此处输入文章标题]

 

 

算法分析与设计实验报告

 

实验一渗透问题(Percolation

  1. 1.   实验目的

使用合并-查找(union-find)数据结构,编写程序通过蒙特卡罗模拟(Monte Carlo simulation) 来估计渗透阈值。

  1. 2.   实验环境

Java8  Eclipse  algs4.jar包

  1. 3.   实验内容

我们使用 N×N 网格点来模型化一个渗透系统。 每个格点或是 open 格点或是 blocked格点,一个 full site 是一个 open 格点,它可以通过一系列的邻近(左、右、上、下)open格点连通到顶行的一个 open 格点。如果在底行中存在一个 full site 格点,则称系统是渗透的。 (对于绝缘/金属材料的例子,open 格点对应于金属材料,渗透系统有一条从顶行到底行的金属路径,且 full sites 格点导电。对于多孔物质示例,open 格点对应于空格,水可能流过,从而渗透系统使水充满 open 格点,自顶向下流动。)

 

科学问题:如果将格点以概率p 独立地设置为 open 格点(因此以概率 1-p 被设置为 blocked 格点),系统渗透的概率是多少? 当 p = 0 时,系统不会渗出; 当 p=1 时,系统渗透。下图显示了 20×20 随机网格(左)和 100×100 随机网格(右)的格点空置概率 p 与渗滤概率。

当 N 足够大时,存在阈值 p*,使得当 p <p*,随机 N× N 网格几乎不会渗透,并且当 p> p*时,随机 N× N 网格几乎总是渗透。 尚未得出用于确定渗滤阈值 p*的数学解。你的任务是编写一个计算机程序来估计 p*。

 

 

 

图1 实验内容示例

 

图2 可能性P的变化规律

 

 

 

  1. 4.   实验程序设计

Percolation数据类型。模型化一个Percolation系统,创建含有以下API的数据类型Percolation。

public class Percolation {

public Percolation(int N) // create N-by-N grid, with all sites blocked

public void open(int i, int j) // open site (row i, column j) if it is not already

public boolean isOpen(int i, int j) // is site (row i, column j) open?

public boolean isFull(int i, int j) // is site (row i, column j) full?

public boolean percolates() // does the system percolate?

public static void main(String[] args) // test client, optional

}

为了应用union-find的两种算法,我们这里初始化两个节点,一个标号为0,表示最上面的一个节点,另外一个标号为N*N+1表示最底下的一个节点,一开始的时候,我们将第一行的所有节点与0号相连,而最后一行的所有元素和N*N+1号节点相连,为了判断是否渗漏,只要判断0号节点与N*N+1号节点是否相连就行,这样简化了判断第一行是否和最后一行相连这个问题。

而一旦出现“open”的元素,我们判断他四个周围的节点,一旦有一个周围节点是open 的,我们就将此节点与他的周围节点连通。

而为了使用蒙特卡洛模拟,我们使用以下的算法分析方法:

1)初始化所有格点为 blocked。

2)重复以下操作直到系统渗出:

3)在所有 blocked 的格点之间随机均匀选择一个格点 (row i, column j)。

4)设置这个格点(row i, column j)为 open 格点open 格点的比例提供了系统渗透时渗透阈值的一个估计。

5)通过重复该计算实验 T 次并对结果求平均值,我们获得了更准确的渗滤阈值估计。 令 xt是第 t 次计算实验中 open 格点所占比例。样本均值μ提供渗滤阈值的一个估计值;样本标准差σ测量阈值的灵敏性,计算以下值

 

还有95%的置信区间

 

下面进入我们的核心程序:

1) Quickfind算法的实现

//并查集代码1

public class QuickFindUF {

    private int[] id;

    private int count;

    public QuickFindUF(int N)

    {

        count = N;

        id = new int[N];

        for(int i=0;i<N;i++)

        {

           id[i] = i;

        }

    }

    public int count()

    {

        return count;

    }

    public int find(int p)

    {

        return id[p];

    }

    public boolean connected(int p,int q)

    {

        return find(p) == find(q);

    }

    public void union(int p,int q)

    {

        int pid = find(p);

        int qid = find(q);

       

        if(pid == qid)

           return;

        for(int i =0;i<id.length;i++)

        {

           if(id[i] == pid)

               id[i] = qid;

        }

        count--;

    }

}

2) WeightedQuickUnion算法的实现

//并查集代码2

public class WeightedQuickUnionUF {

    private int[] id;

    private int[] sz;

    private int count;

    public WeightedQuickUnionUF(int N)

    {

        count = N;

        id = new int[N];

        for(int i=0;i<N;i++)

        {

           id[i] = i;

        }

        sz = new int[N];

        for(int i=0;i<N;i++)

        {

           sz[i] = 1;

        }

    }

    public int count()

    {

        return count;

    }

    public int find(int p)

    {

        while(p!=id[p])

           p = id[p];

        return p;

    }

    public boolean connected(int p,int q)//判断连通性

    {

        return find(p) == find(q);

    }

    public void union(int p,int q)//合并!

    {

        int pid = find(p);

        int qid = find(q);

       

        if(pid == qid)

           return;

        if(sz[pid]<sz[qid])

        {

           id[pid] = qid;

           sz[qid]+= sz[pid];

        }

        else

        {

           id[qid] = pid;

           sz[pid] += sz[qid];

        }

        count--;

    }

}

 

3) Percolation类的实现,这里我给出了一份代码,表示的quick-find算法实现的代码,而weightedquickunion算法的代码以注释的形式,把与quick-find的代码不同的地方写出来,因为两份具体实现的代码非常相似,仅仅是调用了不同的并查集算法,也就是一个使用的是uf1实例,一个使用的是uf2实例

public class Percolation {

    private int N;

    private QuickFindUF uf1;

    //private WeightedQuickUnionUF uf2;

    private int grid[][];

 

    public Percolation(int N)//0为最上方的节点,N*N+1自然是最下方的节点

    {

        this.N = N;

        grid = new int[1+N][1+N];

        uf1 = new QuickFindUF(N*N+2);

        //uf2 = new WeightedQuickUnionUF(N*N+2);

        for(int i=1;i<=N;i++)

        {

           for(int j=1;j<=N;j++)

           {

               grid[i][j] = 0;//一开始的时候都是block的状态

           }

        }

        for(int i=1;i<=N;i++)//初始化,使得最上方与最下方的节点分别和第一行,最后一行连起来

        {

           uf1.union(0, i);//第一行

           //uf2.union(0, i);//第一行

           uf1.union(N*N+1, N*N-i);//最后一行

           //uf2.union(N*N+1, N*N-i);//最后一行

        }

    }

    public void open(int row,int col) {

        grid[row][col] = 1;//置grid为1

        int recentkey = (row-1)*N+col;

        int key;//下面判断是否超出边界,以及算key,key代表Unionfind算法中的id值

        if(row-1>=1&&row-1<=N&&col>=1&&col<=N)

        {

           key = (row-1-1)*N+col;

           if(grid[row-1][col]==1)

           {

               uf1.union(recentkey, key);//union操作,将两个grid连通起来

               //uf2.union(recentkey, key);

           }

        }

        if(row+1>=1&&row+1<=N&&col>=1&&col<=N)

        {

           key = (row+1-1)*N+col;

           if(grid[row+1][col]==1)

           {

               uf1.union(recentkey, key);

               //uf2.union(recentkey, key);

           }

        }

        if(row>=1&&row<=N&&col+1>=1&&col+1<=N)

        {

           key = (row-1)*N+col+1;

           if(grid[row][col+1]==1)

           {

               uf1.union(recentkey, key);

               //uf2.union(recentkey, key);

           }

        }

        if(row>=1&&row<=N&&col-1>=1&&col-1<=N)

        {

           key = (row-1)*N+col-1;

           if(grid[row][col-1]==1)

           {

               uf1.union(recentkey, key);

               //uf2.union(recentkey, key);

           }

        }

    }

    public boolean isOpen(int row,int col)

    {

        return grid[row][col] == 1;

    }

    public boolean isFull(int row,int col)//是否与最上方的0号节点连通

    {

        return uf1.connected(0, (row-1)*N+col)&&isOpen(row,col);

    }

    public boolean percolates()//仅判断第一和最后即可

    {

        return uf1.connected(0, N*N+1);

    }

}

 

  1. 5.   实验具体代码以及运行结果截图

全部的代码如下

public class Percolation {

    private int N;

    private QuickFindUF uf1;

    //private WeightedQuickUnionUF uf2;

    private int grid[][];

 

    public Percolation(int N)//0为最上方的节点,N*N+1自然是最下方的节点

    {

        this.N = N;

        grid = new int[1+N][1+N];

        uf1 = new QuickFindUF(N*N+2);

        //uf2 = new WeightedQuickUnionUF(N*N+2);

        for(int i=1;i<=N;i++)

        {

           for(int j=1;j<=N;j++)

           {

               grid[i][j] = 0;//一开始的时候都是block的状态

           }

        }

        for(int i=1;i<=N;i++)//初始化,使得最上方与最下方的节点分别和第一行,最后一行连起来

        {

           uf1.union(0, i);//第一行

           //uf2.union(0, i);//第一行

           uf1.union(N*N+1, N*N-i);//最后一行

           //uf2.union(N*N+1, N*N-i);//最后一行

        }

    }

    public void open(int row,int col) {

        grid[row][col] = 1;//置grid为1

        int recentkey = (row-1)*N+col;

        int key;//下面判断是否超出边界,以及算key,key代表Unionfind算法中的id值

        if(row-1>=1&&row-1<=N&&col>=1&&col<=N)

        {

           key = (row-1-1)*N+col;

           if(grid[row-1][col]==1)

           {

               uf1.union(recentkey, key);//union操作,将两个grid连通起来

               //uf2.union(recentkey, key);

           }

        }

        if(row+1>=1&&row+1<=N&&col>=1&&col<=N)

        {

           key = (row+1-1)*N+col;

           if(grid[row+1][col]==1)

           {

               uf1.union(recentkey, key);

               //uf2.union(recentkey, key);

           }

        }

        if(row>=1&&row<=N&&col+1>=1&&col+1<=N)

        {

           key = (row-1)*N+col+1;

           if(grid[row][col+1]==1)

           {

               uf1.union(recentkey, key);

               //uf2.union(recentkey, key);

           }

        }

        if(row>=1&&row<=N&&col-1>=1&&col-1<=N)

        {

           key = (row-1)*N+col-1;

           if(grid[row][col-1]==1)

           {

               uf1.union(recentkey, key);

               //uf2.union(recentkey, key);

           }

        }

    }

    public boolean isOpen(int row,int col)

    {

        return grid[row][col] == 1;

    }

    public boolean isFull(int row,int col)//是否与最上方的0号节点连通

    {

        return uf1.connected(0, (row-1)*N+col)&&isOpen(row,col);

    }

    public boolean percolates()//仅判断第一和最后即可

    {

        return uf1.connected(0, N*N+1);

    }

}

//并查集代码1

public class QuickFindUF {

    private int[] id;

    private int count;

    public QuickFindUF(int N)

    {

        count = N;

        id = new int[N];

        for(int i=0;i<N;i++)

        {

           id[i] = i;

        }

    }

    public int count()

    {

        return count;

    }

    public int find(int p)

    {

        return id[p];

    }

    public boolean connected(int p,int q)

    {

        return find(p) == find(q);

    }

    public void union(int p,int q)

    {

        int pid = find(p);

        int qid = find(q);

       

        if(pid == qid)

           return;

        for(int i =0;i<id.length;i++)

        {

           if(id[i] == pid)

               id[i] = qid;

        }

        count--;

    }

}

//并查集代码2

public class WeightedQuickUnionUF {

    private int[] id;

    private int[] sz;

    private int count;

    public WeightedQuickUnionUF(int N)

    {

        count = N;

        id = new int[N];

        for(int i=0;i<N;i++)

        {

           id[i] = i;

        }

        sz = new int[N];

        for(int i=0;i<N;i++)

        {

           sz[i] = 1;

        }

    }

    public int count()

    {

        return count;

    }

    public int find(int p)

    {

        while(p!=id[p])

           p = id[p];

        return p;

    }

    public boolean connected(int p,int q)

    {

        return find(p) == find(q);

    }

    public void union(int p,int q)

    {

        int pid = find(p);

        int qid = find(q);

       

        if(pid == qid)

           return;

        if(sz[pid]<sz[qid])

        {

           id[pid] = qid;

           sz[qid]+= sz[pid];

        }

        else

        {

           id[qid] = pid;

           sz[pid] += sz[qid];

        }

        count--;

    }

}

import edu.princeton.cs.algs4.StdRandom;

 

//统计类,使用加权并查集,这里仅仅需要改变其中的并查集类就行了,跟quickfind的统计类大致形似

public class PercolationStatsWeighted {

    private int T_number;

    private int matrixLength;

    private double[] threshold;

    private double mean;                //平均值

    private double stddev;              //标准偏差

    private double confidenceLow;   // 最低置信度

    private double confidenceHigh;    // 高置信度

    public PercolationStatsWeighted(int N, int T)

    {// perform T independent computational experiments on an N-by-N grid

        matrixLength = N;

        T_number = T;

        mean = 0;

        threshold = new double[T];

        for(int i=0;i<T;i++)

        {

          

           PercolationWeighted percolation = new  PercolationWeighted(N);

           int count = 0;

           do {

               int row = StdRandom.uniform(N)+1;

               int col = StdRandom.uniform(N)+1;

               if(percolation.isOpen(row, col))

               {

                   continue;

               }

               else

               {

                   ++count;

                   percolation.open(row, col);

               }

           }while(!percolation.percolates());

           threshold[i] = (double)1.0*count/(N*N);

        }

    }

     public double mean() // sample mean of percolation threshold

     {

         for(int i=0;i<threshold.length;i++)

         {

            mean+=threshold[i];

         }

         return mean=mean/threshold.length;

     }

     public double stddev()

     {// sample standard deviation of percolation threshold

         double result = 0;

         for(int i=0;i<threshold.length;i++)

         {

            result+=(threshold[i]-mean)*(threshold[i]-mean);

         }

         result/=(T_number-1);

         return stddev=Math.sqrt(result);//赋值并且返回结果

     }

     public double confidenceLo()

     {// returns lower bound of the 95% confidence interval

         return confidenceLow=(mean-stddev*1.96/Math.sqrt(T_number));//赋值并返回结果

     }

     public double confidenceHi()

     {// returns upper bound of the 95% confidence interval

         return confidenceHigh=(mean+stddev*1.96/Math.sqrt(T_number));//赋值并返回结果

     }

}

 

 

package percolation;

import edu.princeton.cs.algs4.StdRandom;

 

public class PercolationStats {

   

    private int T_number;

    private int matrixLength;

    private double[] threshold;

    private double mean;                //平均值

    private double stddev;              //标准偏差

    private double confidenceLow;   // 最低置信度

    private double confidenceHigh;    // 高置信度

    public PercolationStats(int N, int T)

    {// perform T independent computational experiments on an N-by-N grid

        matrixLength = N;

        T_number = T;

        mean = 0;

        threshold = new double[T];

        for(int i=0;i<T;i++)

        {

          

           Percolation percolation = new  Percolation(N);

           int count = 0;

           do {

               int row = StdRandom.uniform(N)+1;

               int col = StdRandom.uniform(N)+1;

               if(percolation.isOpen(row, col))

               {

                   continue;

               }

               else

               {

                   ++count;

                   percolation.open(row, col);

               }

           }while(!percolation.percolates());

           threshold[i] = (double)1.0*count/(N*N);

        }

    }

     public double mean() // sample mean of percolation threshold

     {

         for(int i=0;i<threshold.length;i++)

         {

            mean+=threshold[i];

         }

         return mean=mean/threshold.length;

     }

     public double stddev()

     {// sample standard deviation of percolation threshold

         double result = 0;

         for(int i=0;i<threshold.length;i++)

         {

            result+=(threshold[i]-mean)*(threshold[i]-mean);

         }

         result/=(T_number-1);

         return stddev=Math.sqrt(result);//赋值并且返回结果

     }

     public double confidenceLo()

     {// returns lower bound of the 95% confidence interval

         return confidenceLow=(mean-stddev*1.96/Math.sqrt(T_number));//赋值并返回结果

     }

     public double confidenceHi()

     {// returns upper bound of the 95% confidence interval

         return confidenceHigh=(mean+stddev*1.96/Math.sqrt(T_number));//赋值并返回结果

     }

    public static void main(String[] args) {

        // TODO 自动生成的方法存根

        for(int x=10;x<=2000;x*=2)

        {

            

             long starttime = System.currentTimeMillis();

             PercolationStats quickfind = new PercolationStats(x,30);

             long endtime =     System.currentTimeMillis();

             long realtime = endtime - starttime;

             System.out.println("使用quickfind算法实现,所使用的n为 "+x+",t是30");

             System.out.println("mean()        "+quickfind.mean());

             System.out.println("stddev()    "+quickfind.stddev());

             System.out.println("confidenceLo()    "+quickfind.confidenceLo());

             System.out.println("confidenceHi()    "+quickfind.confidenceHi());

             System.out.println("运行时间"+realtime+"毫秒");

             starttime = System.currentTimeMillis();

             PercolationStatsWeighted Weightedqucikunion = new PercolationStatsWeighted(x,30);

             endtime =  System.currentTimeMillis();

             realtime = endtime - starttime;

             System.out.println("使用WeightedquickUnion算法实现,所使用的n为 "+x+",t是30");

             System.out.println("mean()        "+Weightedqucikunion.mean());

             System.out.println("stddev()    "+Weightedqucikunion.stddev());

             System.out.println("confidenceLo()    "+Weightedqucikunion.confidenceLo());

             System.out.println("confidenceHi()    "+Weightedqucikunion.confidenceHi());

             System.out.println("运行时间"+realtime+"毫秒");

             System.out.println("---------------------分割线-----------------------------");

            

        }

    }

 

}

实验运行结果截图如下

 

 

 

  1. 6.   实验结果分析

N=10的时候,我们进行如下分析

算法时间(ms)

n

2n

4n

8n

16n

32n

64n

Quickfind

15

7

46

259

4379

62345

1203317

Weightedquickunion

5

3

4

5

46

241

1350

 

我们都知道Quickfind应该是N2的算法,下面,我们对两边取lg对数,然后进行线性回归

 

此时lgT = lg(3.9192)+2.912lgN + lg30

其中30 = t

所以得到 T = 3.9192*t*N^(2.9192),此时,我们发现,在这种条件下

总时间T与N^(2.9192)成了正比。

而对Weigtedquickunion,我们直接进行线性回归

在matlab中,输入

 

得到 T = -136.7414+2.0561*N,所以WeightedQuickUnion此时近似为O(2N)的算法,而如果算上t,那么

T = (-136.7414+2.0561*N)/30*t

posted @ 2021-01-22 23:20  coolwx  阅读(615)  评论(2编辑  收藏  举报