普林斯顿大学算法第一部分学习总结(Week1-Percolation)

Algorithms Part1课程第一周的Programming Assignment是Percolation问题,该问题是Union-Find算法的一个应用实例。

作业要求见以下链接:http://coursera.cs.princeton.edu/algs4/assignments/percolation.html,要求用Java实现算法。此外该课程使用了作者提供的API用以简化编程,如基本输入、输出、随机数生成、数据分析等库函数,在上述链接中可直接下载。

模型描述:

Percolation即渗透过程,其模型如下:一个方形“水槽”(system)由N*N个方格(site)组成,每个方格有开启(open)或关闭(blocked)两个状态,相邻(即上下左右)的开启格子能够构成一条同路。如果一个格子处于开启状态,并且与顶端的一行能够通过开启的格子实现连通(connected)通路,我们说这个格子处于充满水(full)的状态;如果一个“水槽”(system)的顶端格子与底部格子通过开启的格子实现了连通,我们称这个“水槽”是可以渗透(percolates)的。如下图所示:

普林斯顿大学算法第一部分学习总结(Week1-Percolation)_第1张图片

问题描述:

本次作业要求实现上述模型,并计算出“水槽”达到渗透状态时开启格子的数量占所有格子总数的比例(p)。通过蒙特卡洛模拟方法,采用多次模拟采样进行p值的估算。对于不同大小的“水槽”有如下模拟曲线,左图为20*20大小,右图为100*100大小:

普林斯顿大学算法第一部分学习总结(Week1-Percolation)_第2张图片

Percolation编程分析:

按照作业要求,需要按照以下API创建一个Percolation类作为数据抽象类型:

  • Percolation(int N):该构造方法对模型初始化,我们创建一个N*N大小的一维布尔数组作为“水槽”,数组值记录每个格子的开闭状态,初始状态为关闭。此外,我们需要另一个(N*N+2)大小的一维数组存储连通关系,根据Union-Find算法的学习,我们创建类型为WeightedQuickUnionUF的数组。大小之所以为(N*N+2),是因为在检查是否渗透的时候,可以通过在顶部与底部设置两个虚拟点,其与顶端或底部的一排是相连通的,这样检查是否渗透的时候,无需遍历顶部或底部一排的每一个点,只需要检查两个虚拟点是否连通即可,如下图所示:

普林斯顿大学算法第一部分学习总结(Week1-Percolation)_第3张图片

  • open(int i, int j):检查坐标为(i, j)的点是否开启,如果没有开启,则将其开启。注意i,j的取值范围为1~N。此外,开启格子后,需要检查其上下左右的格子是否同样已经打开,如果已经打开,则通过WeightedQuickUnionUF的union()方法将两个格子连通起来;另外根据作业要求,需要首先检查是否越界,抛出java.lang.IndexOutOfBoundsException()错误;
  • isOpen(int i, int j):只需要返回该点的开闭状态值;
  • isFull(int i, int j):检查该点是否已经打开并且已经与顶部的虚拟点(相当于顶部的一排)连通,用WeightedQuickUnionUF的connected()方法检测;
  • percolates():检查顶部的虚拟点与底部的虚拟点是否连通,用WeightedQuickUnionUF的connected()方法检测;

注意的一点是,在用一维数组模拟二维“水槽”时,我们用i,j坐标进行表示,(i, j)表示的是数组中序号为(i - 1)*N + (j - 1)的元素;此外,由于虚拟点的设置,坐标的对应容易出现混乱,需要首先考虑清楚这个问题。

更重要的一个问题:BackWash即回流/倒灌问题,按照上面的方法设置虚拟点虽然使得检查是否percolate变得方便,但如果水已经渗透到底部,由于虚拟点与底部一排都是连通的,则会通过底部开启的格子向上“灌水”,如下图所示:

普林斯顿大学算法第一部分学习总结(Week1-Percolation)_第4张图片

解决的方法是:在构造方法中额外定义一个(N*N+1)大小的数组,其不包括底部的虚拟点,其他与(N*N+2)大小的数组一致,在isFull()方法中,我们使用这个(N*N+1)的数组进行检查,也就是说,如果底部的格子确实没有与顶部的虚拟点连通,是不会“灌满”的。下面用图来说明这一过程:

普林斯顿大学算法第一部分学习总结(Week1-Percolation)_第5张图片             普林斯顿大学算法第一部分学习总结(Week1-Percolation)_第6张图片

左图中由于底部有虚拟点,右下角的格子出现了倒灌;而右图中由于底部没有虚拟点,不会出现倒灌。注意仅仅在执行percolates()方法时有这一区别。

PercolationStats编程分析:

用蒙特卡洛(Monte Carlo)方法进行仿真分析,确定渗透阈值p,具体方法是:

  • 初始化将所有N*N个格子置于关闭状态;
  • 每次随机选择一个格子进行开启操作,直到系统达到渗透状态;
  • 统计此时开启的格子数,与全部格子数相比计算一个p值的样本;
  • 独立重复T次上述实验,最后计算出T个p值的样本,记为x1,x2……xT。

进行以下统计操作。

按照下式计算均值与方差:

假设T值足够大(至少30次),则可以根据下式计算置信区间在95%的的渗透阈值范围:

所有代码见本机。

总结:

如何根据API编译JAVA程序:

1.把API所需要做的内容全都用文字详细写出来,每个步骤该做什么;

2.可以写出伪代码,类似于算法课上所要求的,详尽写出;

3.选好数据结构,简单变成不需要;

4.Coding

经验教训:

0.参数的加入,在Run--Run Configuration--Java application--Arguments--Program Arguments里面加入text的含text的文件名,该文件要存放在项目的子目录下(进入项目的目录放在和bin,src在一起);

1.有bound限制条件的,先把bound写好,再写其他的条件(if(y-1>=0 && openState[x*N+(y-1)] );

2.新建的对象一定要在构造函数里面初始化,且不能在构造函数里面再写一次类名,否则会认为是一个新的对象;

grid = new WeightedQuickUnionUF(N*N+2);

WeightedQuickUnionUF grid;

3.若遇到“重复直到”,“repeat until”这样的语句,要用到while进行判定;

while (!perco.percolates()) 

4.double/int = int, 出现出现很多隐藏错误,比如如下结果如果不转化就是0. 需要重新令一个变量double x = int,然后再用除法相除。

double transferNsqure = N*N;

int opened++;

x[i] = opened/transferNsqure;


你可能感兴趣的:(算法自学)