Coursera Algorithms Programming Assignment 1: Percolation(100分)
题目来源http://coursera.cs.princeton.edu/algs4/assignments/percolation.html
作业分为两部分:建立模型和仿真实验。
最关键的部分就是建立模型对象。模型对象要求如下:
The model. We model a percolation system using an n-by-n grid of sites. Each site is either open or blocked. A full site is an open site that can be connected to an open site in the top row via a chain of neighboring (left, right, up, down) open sites. We say the system percolates if there is a full site in the bottom row. In other words, a system percolates if we fill all open sites connected to the top row and that process fills some open site on the bottom row. (For the insulating/metallic materials example, the open sites correspond to metallic materials, so that a system that percolates has a metallic path from top to bottom, with full sites conducting. For the porous substance example, the open sites correspond to empty space through which water might flow, so that a system that percolates lets water fill open sites, flowing from top to bottom.)
边界要求: By convention, the row and column indices are integers between 1 and n, where (1, 1) is the upper-left site: Throw a java.lang.IllegalArgumentException if any argument to open(), isOpen(), or isFull() is outside its prescribed range. The constructor should throw a java.lang.IllegalArgumentException if n ≤ 0.
性能要求: The constructor should take time proportional to n2; all methods should take constant time plus a constant number of calls to the union–find methods union(), find(), connected(), and count().
我的分析
本次作业根据教授在视频课上提示,可以在grid的上方和下方各加入一个虚节点,grid第一行的open节点都与top虚节点连通,grid最后一行的open节点都与bottom虚节点连通。这样只需判断top虚节点与bottom虚节点是否连通就知道grid是否渗透,而不需要去一一选取特定节点比对了。照着这个思路,我实现了下述模型代码。值得注意的是,模型代码的main中测试方法不是仅仅进行各本地测试就可以了,提交作业的时候会进行自动脚本测试,所以提交的版本main方法中必须读取args[0]中的文件名,并加载文件内容进行生成grid和open对应的site。
1 import edu.princeton.cs.algs4.In; 2 import edu.princeton.cs.algs4.StdOut; 3 import edu.princeton.cs.algs4.WeightedQuickUnionUF; 4 /** 5 * @author evasean www.cnblogs.com/evasean/ 6 */ 7 public class Percolation { 8 private static final boolean BLOCK = false; // block state 9 private static final boolean OPEN = true; // open state 10 11 /* topUF bottomUF n 均为final是因为它们只在构造函数时初始化,后续其值未发生变化 */ 12 private final WeightedQuickUnionUF topUF; // 用来记录与top虚节点的连通性 13 private final WeightedQuickUnionUF bottomUF;// 用来记录与bottom虚节点的连通性 14 private final int n; 15 16 private boolean[][] grid; 17 private boolean percolateFlag = false; // grid是否渗透的标志 18 private int openedNum = 0;// 已经open的site数目 19 20 public Percolation(int n) { 21 // create n-by-n grid, with all sites blocked 22 if (n < 1) 23 throw new IllegalArgumentException("grid size should be bigger than one !"); 24 this.n = n; 25 topUF = new WeightedQuickUnionUF(n * n + 1); // 多了一个节点的空间,位置n*n处用来代表虚节点 26 bottomUF = new WeightedQuickUnionUF(n * n + 1); // 多了一个节点的空间,位置n*n处用来代表虚节点 27 grid = new boolean[n][n]; 28 // 初始化grid设为block 29 for (int i = 0; i < n; i++) 30 for (int j = 0; j < n; j++) 31 grid[i][j] = BLOCK; 32 } 33 34 private void validate(int row, int col) { 35 if (row < 1 || col < 1 || row > n || col > n) 36 throw new IllegalArgumentException("input row or col is not illegal!"); 37 } 38 39 public void open(int row, int col) { 40 // open site (row, col) if it is not open already 41 validate(row, col); 42 if (grid[row - 1][col - 1] == OPEN) 43 return; 44 45 grid[row - 1][col - 1] = OPEN; 46 openedNum++; 47 48 // n为1时,open一个节点就达到渗透要求 49 if (n == 1) { 50 topUF.union(0, 1); 51 bottomUF.union(0, 1); 52 percolateFlag = true; 53 return; 54 } 55 56 // 第一行的所有节点都与top虚节点连通 57 if (row == 1) 58 topUF.union(n * n, col - 1); 59 60 // 最后一行的所有节点都与bottom虚节点连通 61 if (row == n) 62 bottomUF.union(n * n, (n - 1) * n + col - 1); 63 64 // 与上方节点的连通性 65 if (row > 1 && grid[row - 2][col - 1] == OPEN) { 66 topUF.union((row - 2) * n + col - 1, (row - 1) * n + col - 1); 67 bottomUF.union((row - 2) * n + col - 1, (row - 1) * n + col - 1); 68 } 69 70 // 与下方节点的连通性 71 if (row < n && grid[row][col - 1] == OPEN) { 72 topUF.union(row * n + col - 1, (row - 1) * n + col - 1); 73 bottomUF.union(row * n + col - 1, (row - 1) * n + col - 1); 74 } 75 76 // 与左侧节点的连通性 77 if (col > 1 && grid[row - 1][col - 2] == OPEN) { 78 topUF.union((row - 1) * n + col - 2, (row - 1) * n + col - 1); 79 bottomUF.union((row - 1) * n + col - 2, (row - 1) * n + col - 1); 80 } 81 82 // 与右侧节点的连通性 83 if (col < n && grid[row - 1][col] == OPEN) { 84 topUF.union((row - 1) * n + col, (row - 1) * n + col - 1); 85 bottomUF.union((row - 1) * n + col, (row - 1) * n + col - 1); 86 } 87 88 /* 89 * 判断条件!percolateFlag是为了防止渗透以后的重复判断 判断条件openedNum>=n 90 * 是因为openedNum达到n时才有可能渗透,在未达到n之前,不需要进行后续判断 91 * 一个节点open的时候刚好使grid渗透的条件是该节点同时与top虚节点和bottom虚节点连通 92 */ 93 if (!percolateFlag && openedNum >= n && topUF.connected(n * n, (row - 1) * n + col - 1) 94 && bottomUF.connected(n * n, (row - 1) * n + col - 1)) 95 percolateFlag = true; 96 97 } 98 99 public boolean isOpen(int row, int col) { 100 // is site (row, col) open? 101 validate(row, col); 102 return grid[row - 1][col - 1] == OPEN; 103 } 104 105 /** 106 * 一个节点只有同时在open状态并且与top虚节点连通时才是full状态 107 * @param row 108 * @param col 109 * @return 110 */ 111 public boolean isFull(int row, int col) { 112 // is site (row, col) full? 113 validate(row, col); 114 if (isOpen(row, col) && topUF.connected(n * n, (row - 1) * n + col - 1)) 115 return true; 116 else 117 return false; 118 } 119 120 public int numberOfOpenSites() { 121 // number of open sites 122 return openedNum; 123 } 124 125 public boolean percolates() { 126 // does the system percolate? 127 return percolateFlag; 128 } 129 130 //打印一些便于查看的信息 131 private void printCheckResult(int row, int col) { 132 StdOut.println("p(" + row + "," + col + ") is open=" + isOpen(row, col) + ";is full=" + isFull(row, col) 133 + ";percolates=" + percolates()); 134 } 135 136 /** 137 * 作业提交时main需要调用该方法,因为提交后在线脚本要用一堆input文件进行测试 138 * 139 * @param arg0 140 */ 141 private static void fileInputCheck(String arg0) { 142 // test client (optional) 143 In in = new In(arg0);//读入input文件名,并加载文件内容 144 String s = null; 145 int n = -1; 146 //读入grid的n 147 while (in.hasNextLine()) { 148 s = in.readLine(); 149 if (s != null && !s.trim().equals("")) 150 break; 151 } 152 s = s.trim(); 153 n = Integer.parseInt(s); 154 Percolation p = new Percolation(n); 155 156 //读入open的site坐标 157 while (in.hasNextLine()) { 158 s = in.readLine(); 159 if (s != null && !s.trim().equals("")) { 160 s = s.trim();//去掉输入字符串头尾空格 161 String[] sa = s.split("\\s+");//去掉中间所有空格 162 if (sa.length != 2) 163 break; 164 int row = Integer.parseInt(sa[0]); 165 int col = Integer.parseInt(sa[1]); 166 p.open(row, col); 167 } 168 } 169 170 } 171 172 /** 173 * 本地测试专用 174 */ 175 private static void generateCheck() { 176 // test client (optional) 177 Percolation p = new Percolation(3); 178 int row = 1, col = 3; 179 p.open(row, col); 180 p.printCheckResult(row, col); 181 row = 2; 182 col = 3; 183 p.open(row, col); 184 p.printCheckResult(row, col); 185 row = 3; 186 col = 3; 187 p.open(row, col); 188 p.printCheckResult(row, col); 189 row = 3; 190 col = 1; 191 p.open(row, col); 192 p.printCheckResult(row, col); 193 row = 2; 194 col = 1; 195 p.open(row, col); 196 p.printCheckResult(row, col); 197 row = 1; 198 col = 1; 199 p.open(row, col); 200 p.printCheckResult(row, col); 201 } 202 203 public static void main(String[] args) { 204 //generateCheck(); 205 fileInputCheck(args[0]); 206 } 207 }
仿真分析这一部分比较简单,其中需要注意的地方就是“随机选取row和col进行open”,如果简单的用random(int n),选取[0,n)获取row和col,会有很多重复节点被选中,随着n越大,命中率就越低。于是我采用生成一个[0,n*n)的数组,数组内容随机排序,依次读取数组内容,就相当于随机取site。
1 import edu.princeton.cs.algs4.StdOut; 2 import edu.princeton.cs.algs4.StdRandom; 3 import edu.princeton.cs.algs4.StdStats; 4 /** 5 * @author evasean www.cnblogs.com/evasean/ 6 */ 7 public class PercolationStats { 8 /* t fractions 均为final是因为它们只在构造函数时初始化,后续其值未发生变化*/ 9 private final int t;//尝试次数 10 private final double[] fractions;//每一次尝试的渗透率得分 11 12 private double mean; 13 private double stddev; 14 15 public PercolationStats(int n, int trials) { 16 // perform trials independent experiments on an n-by-n grid 17 if (n <= 0 || trials <= 0) 18 throw new IllegalArgumentException("n ≤ 0 or trials ≤ 0"); 19 t = trials; 20 fractions = new double[t]; 21 for (int i = 0; i < t; i++) {//t次尝试 22 Percolation p = new Percolation(n); 23 int openNum = 0; 24 //为了实现随机open一个site,模仿QuickUnion的定位方法 25 //先生成一个[0,n*n)的数组,数组内容随机排序,依次读取数组内容,就相当于随机取site 26 int[] rand = StdRandom.permutation(n * n); 27 for (int pos : rand) { 28 //pos = (row-1)*n + col -1 29 int row = pos / n + 1; 30 int col = pos % n + 1; 31 p.open(row, col); 32 openNum++; 33 //只有openNum>=n时才有判断是否渗透的必要 34 if (openNum >= n && p.percolates()) 35 break; 36 } 37 double pt = (double) openNum / (n * n);//单次尝试的渗透率 38 fractions[i] = pt; 39 } 40 /* 作业提交时的某个测试案例要求mean()、stddev()、confidenceLo()、confidenceHi() 41 * 在任何时候任何次序调用的情况下都必须返回相同的值,故需要在构造函数中计算mean和stddev 42 */ 43 //作业提交时的某个测试案例要调用一次StdStats.mean方法 44 mean = StdStats.mean(fractions); 45 //作业提交时的某个测试案例要求要调用一次StdStats.stddev方法 46 stddev = StdStats.stddev(fractions); 47 } 48 49 public double mean() { 50 // sample mean of percolation threshold 51 return mean; 52 } 53 54 public double stddev() { 55 // sample standard deviation of percolation threshold 56 return stddev; 57 } 58 59 public double confidenceLo() { 60 // low endpoint of 95% confidence interval 61 return mean - 1.96 * stddev / Math.sqrt(t); 62 } 63 64 public double confidenceHi() { 65 // high endpoint of 95% confidence interval 66 return mean + 1.96 * stddev / Math.sqrt(t); 67 } 68 69 public static void main(String[] args) { 70 // test client (described below) 71 int n = Integer.parseInt(args[0]); 72 int t = Integer.parseInt(args[1]); 73 PercolationStats ps = new PercolationStats(n, t); 74 StdOut.printf("%-25s %s %f \n", "means", "=", ps.mean()); 75 StdOut.printf("%-25s %s %f \n", "stddev", "=", ps.stddev()); 76 StdOut.printf("%-25s %s%f%s%f%s\n", "95% confidence interval", "= [", ps.confidenceLo(), ", ", 77 ps.confidenceHi(), "]"); 78 } 79 }
本文版权归evasean所有,转载请标明出处
http://www.cnblogs.com/evasean/