題目來源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 }