Last active
October 15, 2021 13:48
-
-
Save geolffreym/ca869b97a46ec924d91f0c188d74170d to your computer and use it in GitHub Desktop.
Quick-Find Percolation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package DynamicConnection.example; | |
import DynamicConnection.WeightedQuickUnion; | |
public class Percolation { | |
private enum States { | |
CLOSE, | |
OPEN | |
} | |
private final int N; | |
private final int TOP = 0; | |
private int openBoxes = 0; | |
private final States[][] boxes; | |
private final WeightedQuickUnion unionFind; | |
// creates n-by-n grid, with all sites initially blocked | |
public Percolation(int n) { | |
if (n <= 0) throw new IllegalArgumentException(); | |
this.N = n; // Grid size | |
this.boxes = new States[n][n]; | |
// Allow to Quick-Find holds virtual root node top and bottom | |
// Add an extra object to top and bottom eg: 5 x 5 + 2 | |
// Not Zer0-based indexes in operations params row, col | |
unionFind = new WeightedQuickUnion(n * n + 2); | |
// Init Blocking all boxes | |
for (int k = 0; k < this.N; k++) { | |
for (int j = 0; j < this.N; j++) { | |
boxes[k][j] = States.CLOSE; | |
} | |
} | |
} | |
private void virtualTopUnion(int row, int col) { | |
// Populate top virtual component tree | |
// Get the corresponding index for flat 1D | |
int index = find1DIndexFrom2D(row, col); | |
unionFind.union(index, this.TOP); // 0 root tree | |
} | |
private void virtualBottomUnion(int row, int col) { | |
// Populate top virtual component tree | |
// Get the corresponding index for flat 1D | |
int index = find1DIndexFrom2D(row, col); | |
// Add object to virtual bottom component this.N * this.N + 1 | |
// eg: 5 x 5 + 1 = 26 <= root for bottom | |
unionFind.union(index, this.N * this.N + 1); | |
} | |
/** | |
* Open box in grid (row,col) if not open, also add union | |
* | |
* @param row index in grid | |
* @param col index in grid | |
*/ | |
public void open(int row, int col) { | |
if (row < 1 || row > this.N || col < 1 || col > this.N) | |
throw new IllegalArgumentException(); | |
// Row and col are passed as non-zero based numbering | |
boxes[row - 1][col - 1] = States.OPEN; | |
if (row == 1) virtualTopUnion(row, col); | |
if (row == this.N) virtualBottomUnion(row, col); | |
unionDirection(row, col, row - 1, col); // Top union | |
unionDirection(row, col, row + 1, col);// Bottom union | |
unionDirection(row, col, row, col + 1); // Right union | |
unionDirection(row, col, row, col - 1); // Left union | |
openBoxes++; | |
} | |
/** | |
* Is open site in (row, col) in grid? | |
* | |
* @param row index in grind | |
* @param col index in grid | |
* @return - true if box is OPEN state else return false | |
* @throws IllegalArgumentException - if both 1 > row > N and 1 > col > N | |
*/ | |
public boolean isOpen(int row, int col) { | |
if (row < 1 || row > this.N || col < 1 || col > this.N) | |
throw new IllegalArgumentException(); | |
return boxes[row - 1][col - 1] == States.OPEN; | |
} | |
/** | |
* Add union in diff directions for grid | |
* | |
* @param x1 from row | |
* @param y1 from col | |
* @param x2 to row | |
* @param y2 to col | |
*/ | |
private void unionDirection(int x1, int y1, int x2, int y2) { | |
// Avoid index over/under flow | |
// If x2 underflow or x2 overflow or y2 underflow or y2 overflow | |
if (x2 < 1 || x2 > this.N || y2 < 1 || y2 > this.N) return; | |
if (!this.isOpen(x2, y2)) return; | |
int from = this.find1DIndexFrom2D(x1, y1); | |
int to = this.find1DIndexFrom2D(x2, y2); | |
unionFind.union(from, to); | |
} | |
/** | |
* Ss the site (row, col) full? | |
* | |
* @param row index in grid | |
* @param col index in grid | |
* @return - true if is object for row, col is connected to TOP else return false | |
* @throws IllegalArgumentException - if both 1 > row > N and 1 > col > N | |
*/ | |
public boolean isFull(int row, int col) { | |
if (row < 1 || row > this.N || col < 1 || col > this.N) | |
throw new IllegalArgumentException(); | |
return unionFind.connected(find1DIndexFrom2D(row, col), TOP); | |
} | |
// returns the number of open sites | |
public int numberOfOpenSites() { | |
return openBoxes; | |
} | |
// does the system percolate? | |
public boolean percolates() { | |
// Is the bottom connected with top over open boxes? | |
return unionFind.connected(TOP, this.N * this.N + 1); | |
} | |
/** | |
* Locate index for 1D flat array from 2D corresponding array | |
* | |
* @param row in 2D - Not zero-based index | |
* @param col in 2D - Not zero-based index | |
* @return int projected 1D index from 2D array | |
*/ | |
private int find1DIndexFrom2D(int row, int col) { | |
// Current N size eg. 5x5, get in 2D (col 2, row 2) = 7 in 1D | |
// [ | |
// 0=> 0,1,2,3,4, | |
// 1=> 5,6,7,8,9 | |
// 2=> .... | |
// 3=> .... | |
// ] | |
return (this.N * (row - 1)) + col; | |
} | |
public static void main(String[] args) { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import edu.princeton.cs.algs4.StdOut; | |
import edu.princeton.cs.algs4.StdRandom; | |
import edu.princeton.cs.algs4.StdStats; | |
public class PercolationStats { | |
private double percolationResults[]; | |
private int trials; | |
// perform independent trials on an n-by-n grid | |
public PercolationStats(int n, int trials) { | |
if (n <= 0 || trials <= 0) throw new IllegalArgumentException(); | |
this.percolationResults = new double[trials]; | |
this.trials = trials; | |
for (int i = 0; i < trials; i++) { | |
Percolation percolation = new Percolation(n); | |
while (!percolation.percolates()) { | |
int randomCol = StdRandom.uniform(1, n + 1); | |
int randomRow = StdRandom.uniform(1, n + 1); | |
percolation.open(randomRow, randomCol); | |
} | |
this.percolationResults[i] = percolation.numberOfOpenSites() / Math.pow(n, 2); | |
} | |
} | |
// sample mean of percolation threshold | |
public double mean() { | |
return StdStats.mean(this.percolationResults); | |
} | |
// sample standard deviation of percolation threshold | |
public double stddev() { | |
return StdStats.stddev(this.percolationResults); | |
} | |
// low endpoint of 95% confidence interval | |
public double confidenceLo() { | |
return mean() - (1.96 / Math.sqrt(this.trials)); | |
} | |
// high endpoint of 95% confidence interval | |
public double confidenceHi() { | |
return mean() + (1.96 / Math.sqrt(this.trials)); | |
} | |
public static void main(String[] args) { | |
int n = Integer.parseInt(args[0]); | |
int trials = Integer.parseInt(args[1]); | |
PercolationStats percolationTest = new PercolationStats(n, trials); | |
StdOut.println(String.format("mean = %f", percolationTest.mean())); | |
StdOut.println(String.format("stddev = %f", percolationTest.stddev())); | |
StdOut.println( | |
String.format( | |
"95%% confidence interval = [ %f, %f ]", | |
percolationTest.confidenceLo(), | |
percolationTest.confidenceHi() | |
) | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package DynamicConnection; | |
public class WeightedQuickUnion { | |
private final int[] tree; | |
private final int[] sizes; | |
public WeightedQuickUnion(int N) { | |
// Initialize with N objects | |
this.tree = new int[N]; | |
this.sizes = new int[N]; | |
// Initial fill components with value=index | |
for (int i = 0; i < this.tree.length; i++) { | |
this.tree[i] = i; | |
this.sizes[i] = 0; | |
} | |
} | |
/** | |
* Return the root in tree for node i | |
* Auto path compression to avoid tall tree. | |
* Move `i` subtree root to 1 depth below main root. | |
* | |
* @param i node to search root for | |
* @return the root for node i | |
*/ | |
private int find(int i) { | |
// Go up through the tree until find the `reflexive node` = root | |
while (i != this.tree[i]) { | |
// Root for `i` will be one degree below main root now. | |
this.tree[i] = this.tree[this.tree[i]]; | |
i = this.tree[i]; | |
} | |
return i; | |
} | |
/** | |
* Check if node p 's root belongs to node q 's root | |
* If the value is the same then they are connected | |
* | |
* @param p index | |
* @param q index | |
* @return boolean true if connected else false | |
*/ | |
public boolean connected(int p, int q) { | |
return find(p) == find(q); | |
} | |
/** | |
* Replace one value only in array to connect components | |
* With p and q 's root corresponding [0,1] => [1,1] p is now sub-tree of q's root | |
* | |
* @param p index | |
* @param q index | |
*/ | |
public void union(int p, int q) { | |
// root for index p | |
int pRoot = find(p); | |
// root for index q | |
int qRoot = find(q); | |
// Same root? Do nothing! | |
if (pRoot == qRoot) return; | |
weightedUnion(pRoot, qRoot); | |
} | |
/** | |
* Check for size in two trees. | |
* Add the smaller tree below the big one. | |
* | |
* @param pRoot root for p | |
* @param qRoot root for q | |
*/ | |
void weightedUnion(int pRoot, int qRoot) { | |
int pSize = this.sizes[pRoot]; | |
int qSize = this.sizes[qRoot]; | |
if (pSize < qSize) { | |
this.tree[pRoot] = qRoot; | |
this.sizes[qRoot] += pSize; | |
} else { | |
this.tree[qRoot] = pRoot; | |
this.sizes[pRoot] += qSize; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment