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

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

  • List item
  1. 我们使用 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×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 与渗滤概率。

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

    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
    }
    约定行 i 列 j 下标在 1 和 N 之间,其中(1, 1)为左上格点位置:如果 open(), isOpen(), or isFull()不在这个规定的范围,则抛出IndexOutOfBoundsException 例外。如果 N ≤ 0,构造函数应该抛出 IllegalArgumentException 例外。构造函数应该与 N2成正比。所有方法应该为常量时间加上常量次调用合并-查找方法 union(), find(), connected(), and count()。
    通过重复该计算实验 T 次并对结果求平均值,我们获得了更准确的渗滤阈值估计。 令 xt
    是第 t 次计算实验中 open 格点所占比例。样本均值μ提供渗滤阈值的一个估计值;样本标准
    差σ测量阈值的灵敏性。
    我们创建数据类型 PercolationStats 来执行一系列计算实验,包含以下 API。
    public class PercolationStats {
    public PercolationStats(int N, int T) // perform T independent computational experiments
    on an N-by-N grid
    public double mean() // sample mean of percolation threshold
    public double stddev() // sample standard deviation of percolation threshold
    public double confidenceLo() // returns lower bound of the 95% confidence interval
    public double confidenceHi() // returns upper bound of the 95% confidence interval
    public static void main(String[] args) // test client, described below
    }
    在 N ≤ 0 或 T ≤ 0 时,构造函数应该抛出 java.lang.IllegalArgumentException 例外。
    此外,还包括一个 main( )方法,它取两个命令行参数 N 和 T,在 N×N 网格上进行 T 次独
    立的计算实验(上面讨论),并打印出均值μ、标准差σ和 95% 渗透阈值的置信区间。 使
    用标准库中的标准随机数生成随机数; 使用标准统计库来计算样本均值和标准差。
    Example values after creating PercolationStats(200, 100)
    mean() = 0.5929934999999997
    stddev() = 0.00876990421552567
    confidenceLow() = 0.5912745987737567
    confidenceHigh() = 0.5947124012262428
    Example values after creating PercolationStats(200, 100)
    mean() = 0.592877
    stddev() = 0.009990523717073799
    confidenceLow() = 0.5909188573514536
    confidenceHigh() = 0.5948351426485464
    Example values after creating PercolationStats(2, 100000)
    mean() = 0.6669475
    stddev() = 0.11775205263262094
    confidenceLow() = 0.666217665216461
    confidenceHigh() = 0.6676773347835391
    运行时间和内存占用分析。
  • 5 -
    使用 quick-find 算法(QuickFindUF.java from algs4.jar)实现 Percolation 数据类型。进行实验
    表明当 N 加倍时对运行时间的影响;使用近似表示法,给出在计算机上的总时间,它是输
    入 N 和 T 的函数表达式。
    使用 weighted quick-union 算法(WeightedQuickUnionUF.java from algs4.jar)实现 Percolation
    数据类型。进行实验表明当 N 加倍时对运行时间的影响;使用近似表示法,给出在计算机
    上的总时间,它是输入 N 和 T 的函数表达式。
    解析:
    通过所学的连通性判断方法和在NN的网格上随机的打开渗透点直到顶和底联通,分别使用quick_find,union_find和union_find加权来进行连通性判断,并分别分析所用时间和结果的正确性。
    本质其实就是对于一个N
    N的全闭合的格子使用随机数的函数分别随机出行数和列数,每次开一个格点,用quickfind和unionfind和unionfind加权的方法把这个格点连接到已有的联通分量里。直到最顶行和最低行连通。
    实现结果如下:
    当n为40时:
    在这里插入图片描述
    当N为80时:
    在这里插入图片描述
    当n为160时:
    在这里插入图片描述
    结果分析
    Quickfind在n越来越大后他的执行效率时最慢的时间复杂度分析:
    Quickfind每次开一个点都要将整个数组遍历一遍,把和该点联通的所有点的值改为这个点的下标所以算法的复杂度为n平方的。这里的n为二维矩阵的边长,所以时间的的增量为每次矩阵增量的4次方,这里我的例子里每次的增量为2,所以理论每次的时间应翻16倍,但这个算法每次所开的点比例为0.59,所以时间翻倍为0.5916约为9.44大概为10,通过上面的时间比,大概也趋向于10。
    Union_find和Union_find加权的算法的时间复杂度都为nlogn,经过平方后2
    n平方*logn增量为2时比例变化,无固定比值,但都满足所验证的结论。
package 渗透;
public class Percolation //第一个类定义三种判断连通性的方法
{          
	public int N;
	public int count_open=0;
	public int [][]box;
	public int []id;
	public int top,bottom;
	private int []sz;
	private boolean connected=true;
	private boolean unconnected=false;
	public Percolation(int N)
	{
		box=new int [N][N];
		id=new int [N*N+2];
		sz=new int [N*N+2];
		top=N*N;
		bottom=N*N+1;
		id[top]=top;
		id[bottom]=bottom;
		int k=0;
		for(int i=0;i<N;i++)
		{
			for(int j=0;j<N;j++,k++)//初始N*N的矩,让其都为0,同时其对应的id【】其顺位的的值,sz【】全初始为1
			{
				box[i][j]=0;
				id[k]=k;
				sz[k]=1;
			}
		}
		for(int i=0;i<N;i++)
		{
			id[i]=id[top];//顶行的值都为n平方
		}
		for(int i=N*(N-1);i<N*N;i++)
		{
			id[i]=id[bottom];//底行的值都为n平方+1
		}
		
	}
	public void open (int i,int j)
	{
		box[i][j]=1;
		count_open++;
	}
	public boolean isopen(int i,int j)
	{
		return box[i][j]==1;
	}
	public boolean isfull(int i,int j)
	{
		return box[i][j]==0;
	}
	public int find(int p)
	{
		while(id[p]!=p)
		{
			p=id[p];
		}
		return id[p];
	}
	public boolean connected (int i,int j)
	{
		return find(i)==find(j);
	}
	public boolean quickconnected(int i,int j)
	{
		return id[i]==id[j];
	}
	public boolean percolates()//with no canshu
	{
		if(connected(top,bottom))//可以理解为顶行的根节点为top,底行为bottom,只要这两行中有任意两个点联通,则top和bottpom联通
		{
			return connected;
		}
		else
		{
			return unconnected;
		}
		
	}
	public boolean quickpercolates()//with no canshu
	{
		if(quickconnected(top,bottom))//可以理解为顶行的根节点为top,底行为bottom,只要这两行中有任意两个点联通,则top和bottpom联通
		{
			return connected;
		}
		else
		{
			return unconnected;
		}
		
	}
	public void union(int p,int q)
	{
		int i=find(p);
		int j=find(q);
		if(i==j)
		{
			return ;
		}
		if(sz[i]<sz[j])
		{
			id[i]=j;
			sz[j]=sz[i]+sz[j];
		}
		else
		{
			id[j]=i;
			sz[i]=sz[i]+sz[j];
		}
	}
	public void quickfindunion(int p,int q)
	{
		         int i=id[p];
				 int j=id[q];
		if(i==j)
		{
			return;
		}
		    int z;
			for(z=0;z<id.length;z++)
			{
				if(id[z]==i)
				{
					id[z]=j;
				}
					
			}
	}
	public void quickunion(int p,int q)
	{
		int i=find(p);
		int j=find(q);
		if(i==j)
		{
			return ;
		}
		id[i]=j;
		}
	}
类二:
package 渗透;
import java.lang.Math;
public class Percolationstats //union——find加权
{
	  int N,T;
	  double[]sample=new double[100000];
	  double[]sample1=new double[100000];
	  double mean,stddev,lo,hi;
	  public Percolationstats(int N,int T)//
	  {
		  this.N=N;
		  this.T=T;
		  int total=N*N;
		  for(int times=0;times<T;times++)
		  {
			  Percolation per =new Percolation(N);
			  while(!per.percolates())//打开格点直到渗透
			  {
				  int i=(int )(Math.random()*N);
				  int j=(int )(Math.random()*N);
				  if(per.isfull(i, j))
				  {
					  per.open(i, j);//打开格点,把以前不开的打开
				  
				  int p=0,q=0;
				  p=i*N+j;
				  if(i-1>=0)
				  {
					  q=p-N;
					  if(per.box[i-1][j]==1)
					  {
						  per.union(p, q);
					  }
				  }
				   if(j+1<N)
				  {
					  q=p+1;
					  if(per.box[i][j+1]==1)
					   {
						  per.union(p, q);
					   }
				   }
				   if(i+1<N)
				   {
					   q=p+N;
					   if(per.box[i+1][j]==1)
					   {
						   per.union(p, q);
					   }
				   }
				   if(j-1>=0)
				   {
					   q=p-1;
					   if(per.box[i][j-1]==1)
					   {
						   per.union(p, q);
					   }
				   }
			  }
			  }
			  sample[times]=1.0*per.count_open/total;//计算open格点与总格点的比值
		  }
	   
  }
	 
	  public double mean(){//计算平均阈值
		  double sum=0;
		  for(int i=0;i<T;i++)
		  {
			  sum=sum+sample[i];
		  }
		  mean=sum/T;
		  return mean;
	  }
	  public double stddev() //几次的方差
	  {
		  double sig,sum=0;
		  for(int i=0;i<T;i++)
		  {
			  sum=sum+(sample[i]-mean)*(sample[i]-mean);
		  }
		  sig=sum/(T-1);
		  stddev=Math.sqrt(sig);
		  return stddev;
	  }
	  public double confidenceLo(){
		  lo=mean-1.96*stddev/Math.sqrt(T);//置信区间最小值
		  return lo;
	  }
	  public double confidenceHi()//置信区间最大值
	  {
		  hi=mean+1.96*stddev/Math.sqrt(T);
		  return hi;
	  }
}
package 渗透;
import java.lang.Math;
public class quick_find {//quick-find
	
		  int N,T;
		  double[]sample=new double[100000];
		  double[]sample1=new double[100000];
		  double mean,stddev,lo,hi;
		  public  quick_find(int N,int T)//
		  {
			  this.N=N;
			  this.T=T;
			  int total=N*N;
			  
			  for(int times=0;times<T;times++)
			  {
				  int p=0;
				  Percolation  per =new Percolation (N);
				  while(!per.quickpercolates())//打开格点直到渗透
				  {
					  int i=(int )(Math.random()*N);
					  int j=(int )(Math.random()*N);
					  if(per.isfull(i, j))
					  {
					   per.open(i, j);//打开格点,把以前不开的打开
					  int q=0;
					  p=i*N+j;
					  if(i-1>=0)
					  {
						  q=p-N;
						  if(per.box[i-1][j]==1)
						  {
							  per.quickfindunion(p, q);
						  }
					  }
					   if(j+1<=N-1)
					  {
						  q=p+1;
						  if(per.box[i][j+1]==1)
						   {
							  per.quickfindunion(p, q);
						   }
					   }
					   if(i+1<=N-1)
					   {
						   q=p+N;
						   if(per.box[i+1][j]==1)
						   {
							   per.quickfindunion(p, q);
						   }
					   }
					   if(j-1>=0)
					   {
						   q=p-1;
						   if(per.box[i][j-1]==1)
						   {
							   per.quickfindunion(p, q);
						   }
					   }
					  }
				  }
				  
				  sample[times]=1.0*per.count_open/total;//计算open格点与总格点的比值
			  }
		   
	 }
		  public double mean(){//计算平均阈值
			  double sum=0;
			  for(int i=0;i<T;i++)
			  {
				  sum=sum+sample[i];
			  }
			  mean=sum/T;
			  return mean;
		  }
		  public double stddev() //几次的方差
		  {
			  double sig,sum=0;
			  for(int i=0;i<T;i++)
			  {
				  sum=sum+(sample[i]-mean)*(sample[i]-mean);
			  }
			  sig=sum/(T-1);
			  stddev=Math.sqrt(sig);
			  return stddev;
		  }
		  public double confidenceLo(){
			  lo=mean-1.96*stddev/Math.sqrt(T);//置信区间最小值
			  return lo;
		  }
		  public double confidenceHi()//置信区间最大值
		  {
			  hi=mean*1.96*stddev/Math.sqrt(T);
			  return hi;
		  }
}
package 渗透;
import java.lang.Math;
public class quickfindPercolationstats {//union-find
	  int N,T;
	  double[]sample=new double[100000];
	  double[]sample1=new double[100000];
	  double mean,stddev,lo,hi;
	  public  quickfindPercolationstats(int N,int T)//
	  {
		  this.N=N;
		  this.T=T;
		  int total=N*N;
		  
		  for(int times=0;times<T;times++)
		  {
			  Percolation per =new Percolation(N);
			  while(!per.percolates())//打开格点直到渗透
			  {
				  int i=(int )(Math.random()*N);
				  int j=(int )(Math.random()*N);
				  if(per.isfull(i, j))
				  {
					  per.open(i, j);//打开格点,把以前不开的打开
				 
				  int p=0,q=0;
				  p=i*N+j;
				  if(i-1>=0)
				  {
					  q=p-N;
					  if(per.box[i-1][j]==1)
					  {
						  per.quickunion(p, q);
					  }
				  }
				   if(j+1<N)
				  {
					  q=p+1;
					  if(per.box[i][j+1]==1)
					   {
						  per.quickunion(p, q);
					   }
				   }
				   if(i+1<N)
				   {
					   q=p+N;
					   if(per.box[i+1][j]==1)
					   {
						   per.quickunion(p, q);
					   }
				   }
				   if(j-1>=0)
				   {
					   q=p-1;
					   if(per.box[i][j-1]==1)
					   {
						   per.quickunion(p, q);
					   }
				   }
			  }
			  }
			  sample[times]=1.0*per.count_open/total;//计算open格点与总格点的比值
		  }
	   
 }
	  public double mean(){//计算平均阈值
		  double sum=0;
		  for(int i=0;i<T;i++)
		  {
			  sum=sum+sample[i];
		  }
		  mean=sum/T;
		  return mean;
	  }
	  public double stddev() //几次的方差
	  {
		  double sig,sum=0;
		  for(int i=0;i<T;i++)
		  {
			  sum=sum+(sample[i]-mean)*(sample[i]-mean);
		  }
		  sig=sum/(T-1);
		  stddev=Math.sqrt(sig);
		  return stddev;
	  }
	  public double confidenceLo(){
		  lo=mean-1.96*stddev/Math.sqrt(T);//置信区间最小值
		  return lo;
	  }
	  public double confidenceHi()//置信区间最大值
	  {
		  hi=mean+1.96*stddev/Math.sqrt(T);
		  return hi;
	  }
}
package 渗透;
import java.util.*;
public class text {
   public static void main (String [] args)
   {
	   System.out .println("请输入N和T");
	   int N,T;
	   Scanner input=new Scanner(System.in);
	   N=input.nextInt();
	   T=input.nextInt();
	   int i,j;
	   for(i=0;i<5;i++)
	   {
		   long start,end,start1,end1,start2,end2;
		   start=System.currentTimeMillis();
		   quickfindPercolationstats p=new  quickfindPercolationstats(N,T);
		   end=System.currentTimeMillis();
		   start1=System.currentTimeMillis();
		   Percolationstats q=new  Percolationstats(N,T);
		   end1=System.currentTimeMillis();
		   start2=System.currentTimeMillis();
		   quick_find e=new  quick_find(N,T);
		   end2=System.currentTimeMillis();
		   System.out .println("Result:");
		   System.out .println("mean          ="+q.mean());
		   System.out .println("stddev        ="+q.stddev());
		   System.out .println("confidenceLow ="+q.confidenceLo());
		   System.out .println("confidenceHigh ="+q.confidenceHi());
		   System.out .println("N  ="+N+"   T  ="+T);
		   System.out .println("quickfind time ="+(end2-start2)+"ms\n");
		   System.out .println("union running time ="+(end-start)+"ms\n");
		   System.out .println("union running -quantime ="+(end1-start1)+"ms\n");
		   N=N*2;
		   
	   }
   }
}

实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值