I am studying Algorithms by Princeton University. The first week assignment is to simulate percolation by using Java.
https://coursera.cs.princeton.edu/algs4/assignments/percolation/specification.php
Since I am learning C++, I'm using C++ to do the assignment. Unfortunately, there is no grader for C++. Therefore, I came to here to ask for code review.
Core algorithm (from course website)
The core algorithm is weighted quick union with path compression.
The WeightedQuickUnionUF class represents a union–find data type (also known as the disjoint-sets data type). It supports the classic union and find operations, along with a count operation that returns the total number of sets.
The union–find data type models a collection of sets containing n elements, with each element in exactly one set. The elements are named 0 through n–1.Initially, there are n sets, with each element in its own set.The canonical element of a set (also known as the root) is one distinguished element in the set. Here is a summary of the operations:
- root(p) returns the canonical element(root) of the set containing p. It compress the path by updating the parent of p to its grandparent.
- The connected operation returns the same value for two elements if and only if they are in the same set.
- WeightedUnion(p, q) merges the set containing element p with the set containing element q. That is, if p and q are in different sets, replace these two sets with a new set that is the union of the two.
- count() returns the number of sets.
Percolation
- 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.
- 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.
Goal
Find the threshold that the system will percolate. That is, the percentage of open site in the system for it to be percolated
WeightedQuickUnionUF.h
#pragma once
#include <vector>
class WeightedQuickUnionUF{
private:
std::vector<int> parent; //parent link(site indexed)
std::vector<int> sz; //size of component for roots(site indexed) / no of element in tree
int count_; //number of components/tree
public:
WeightedQuickUnionUF(int n);
int count()const; //return no of component
bool connected(int p, int q); //check if their components' are equal
void WeightedUnion(int p, int q); //merge trees tgt, smaller tree becomes child of larger tree
private:
int root(int p); //return root of p
};
WeightedQuickUnionUF.cpp
#include "WeightedQuickUnionUF.h"
WeightedQuickUnionUF::WeightedQuickUnionUF(int n){
count_ = n;
parent.reserve(n);
for (int i = 0; i < n; i++){
parent.push_back(i);
}
sz.reserve(n);
for (int i = 0; i < n; i++){
sz.push_back(i);
}
}
int WeightedQuickUnionUF::count()const{
return count_;
}
int WeightedQuickUnionUF::root(int p){
while (p != parent[p]){ //chase parent pointer until it reaches root => parent = parent's root => parent = root
parent[p] = parent[parent[p]]; //change root of p to its grandparent: path-compression
p = parent[p]; //go to grandparent
}
return p;
}
bool WeightedQuickUnionUF::connected(int p, int q){
return root(p) == root(q);
}
void WeightedQuickUnionUF::WeightedUnion(int p, int q){
int rootP = root(p);
int rootQ = root(q);
if (rootP == rootQ) return;
if (sz[rootP] < sz[rootQ]){
parent[rootP] = rootQ;
sz[rootQ] += sz[rootP];
}
else {
parent[rootQ] = rootP;
sz[rootP] += sz[rootQ];
}
--count_;
}
gen_uf.h
#pragma once
#include <random>
int rand_uf(int range_from, int range_to); //generate random number
gen_uf_im.cpp
#include "gen_uf.h"
#include <random>
int rand_uf(int range_from, int range_to){
static std::random_device r1; //static: ensure only one seed in runtime
static std::default_random_engine reng(r1()); //seed the random engine
std::uniform_int_distribution<> distr(range_from, range_to); //initialize the range for the distribution
return distr(reng); //transform the random unsigned int generated by reng into an int
}
Percolation.h
#pragma once
#include <vector>
#include <iostream>
#include "WeightedQuickUnionUF.h"
#include "gen_uf.h"
class Percolation: private WeightedQuickUnionUF{
private:
//n^2 site
std::vector<int> grid;
//size of grid, 5-by-5 grid has size 5
int sz_grid;
const int m_open = 1;
const int closed = 0;
//top virtual site
int top;
//bottom virtual site
int bottom;
int numberOfOpenSites_;
public:
//create n-by-n grid, with all sites initially closed
Percolation(int n);
//open the site (row, col) if it is not open already
void open(int row, int col);
// is the site (row, col) open?
bool isOpen(int row, int col)const;
// is the site (row, col) Full?
bool isFull(int row, int col);
// return the number of open site
int numberOfOpenSites()const;
// does the system percolates?
bool percolates();
//Monte Carlo Simulation
double testPercolateThreshold();
};
Percolation.cpp
#include "Percolaton.h"
#include "WeightedQuickUnionUF.h"
#include "gen_uf.h"
#include <vector>
#include <iostream>
Percolation::Percolation(int n):
WeightedQuickUnionUF(n * n + 2)
{
sz_grid = n;
numberOfOpenSites_ = 0;
grid.reserve(n * n);
for (int i = 0; i < n * n; i++){ //n^2 complexity
grid.push_back(closed); //initially all sites are closed
}
top = n * n; //top virtual site
bottom = n * n + 1; //bottom virtual site
int bot_tmp_ix = n * (n - 1) + 0; //bottom edge starts from [n - 1][0]
for (int i = 0; i < n; i++){
WeightedUnion(i, top); //connect top edge and top
WeightedUnion(bot_tmp_ix, bottom); //connect bottom edge and bottom
bot_tmp_ix++;
}
}
void Percolation::open(int row, int col){
//row:1 col:0 for size 5 = grid[5]
// = size(row) + col = 5(1) + 0
int ix = sz_grid * row + col;
if (!isOpen(row, col)) grid[ix] = m_open;
++numberOfOpenSites_;
//connect to up, left, right, down sites if they are opened
if ((row - 1) >= 0){ //up
if(isOpen(row - 1, col)){
int up_ix = sz_grid * (row - 1) + col;
WeightedUnion(ix, up_ix);
}
}
if ((col - 1) >= 0){ //left
if(isOpen(row, col - 1)){
int left_ix = sz_grid * row + (col - 1);
WeightedUnion(ix, left_ix);
}
}
if ((col + 1) <= sz_grid - 1){ //right
if(isOpen(row, col + 1)){
int right_ix = sz_grid * row + (col + 1);
WeightedUnion(ix, right_ix);
}
}
if ((row + 1) <= sz_grid - 1){ //down
if(isOpen(row + 1, col)){
int down_ix = sz_grid * (row + 1) + col;
WeightedUnion(ix, down_ix);
}
}
}
bool Percolation::isOpen(int row, int col)const{
int ix = sz_grid * row + col;
if (grid[ix] == m_open) return true;
else return false;
}
bool Percolation::isFull(int row, int col){
int ix = sz_grid * row + col;
return connected(ix, top);
}
int Percolation::numberOfOpenSites()const{
return numberOfOpenSites_;
}
bool Percolation::percolates(){
return connected(top, bottom);
}
double Percolation::testPercolateThreshold(){
while (!percolates()){
int row = rand_uf(0, sz_grid - 1);
int col = rand_uf(0, sz_grid - 1);
if (!isOpen(row, col)){
open(row, col);
}
}
double threshold = numberOfOpenSites() / static_cast<double>(sz_grid * sz_grid);
return threshold;
}
PercolationStat.h
#pragma once
#include "Percolaton.h"
class PercolationStat{
private:
double thresholdSum;
int size;
int trials;
public:
//perform independent trials on a n-by-n grid
PercolationStat(int sz, int times);
//sample mean of percolation threshold
double PercolationMean();
};
PercolationStat.cpp
#include "PercolationStat.h"
#include "Percolaton.h"
PercolationStat::PercolationStat(int sz, int times): size(sz), trials(times)
{
thresholdSum = 0;
for (int i = 0; i < times; i++){
Percolation p(sz);
thresholdSum += p.testPercolateThreshold();
}
}
double PercolationStat::PercolationMean(){
return thresholdSum / trials;
}
ufmain.cpp
#include <iostream>
#include "WeightedQuickUnionUF.h"
#include "Percolaton.h"
#include "gen_uf.h"
#include "PercolationStat.h"
using namespace std;
int main(){
int size;
cout << "Enter the size(N) of percolation grid(N-by-N):";
cin >> size;
cout << "Enter number of time of simulation:";
int time;
cin >> time;
PercolationStat pStat(size, time);
cout << "Mean: " << pStat.PercolationMean() << endl;
}