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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM