算法分析与设计第一次实验——渗透问题

算法课的第一次实验。

实验内容:渗透问题

关于环境

由于需要用到课程给的库里面的函数,所以需要先把课程给的包 algs4.jar 导入。

对于 IDE,我并没有使用课程推荐的 Drjava 和 IntelliJ,而是用了大一下学 Java 时所用的 Eclipse。

问题分析

发现问题是让我们在一个初始所有格点都为 block 状态的 $N \times N$ 的网格上随机地选一个点使其变为 open 状态,如此重复操作直至第一行存在 open 格点 $x$ 和 $y$ ,分别位于第一行和最后一行,它们位于同一个连通块中。

连通块,自然想到用并查集来维护,每次把一个点变成 open 状态时,把其上下左右的 open 格点与其 Union

为了方便编写,考虑把 $N\times N$ 的网格映射到一个一维数组上,可以这样映射:第 $i$ 行第 $j$ 列 $\to$ 第 $(i-1)\times N+j$ 个值。这样,就映射到了 $1\sim N^2$ 上。

类似于最短路问题的“超级源点”,考虑把 $0$ 和 $N^2+1$ 设为超级源点,第一行所有格点变为 open 状态时,除了与上下左右的 open 格点 Union ,还与 $0$ 号 Union;最后一行同理,与 $N^2+1$ 格点 Union

实验要求比较朴素并查集与按秩合并并查集的效率。于是考虑写一个朴素并查集类 QuickFind 和按秩合并并查集类 WeightedQuickUnion

于是便可以按照文件中所写的那样,建立一个 Percolation 类,利用两个并查集类。

蒙特卡洛模拟部分,直接按照文件中给的函数实现对应的功能就行了。计算均值、标准差、置信区间需要调用 algs4.jar 库里面的函数,直接 import 即可。

代码

没怎么写过 Java ,写的很丑(

QuickFind 类

package percolation;

public class QuickFind {
    private int n;
    private int[] id;
    public QuickFind(int n) {
        this.n = n;
        id = new int[n];
        for(int i = 0; i < n; i++) id[i] = i;
    }
    public int Find(int p) {
        return p == id[p] ? p : Find(id[p]);
    }
//  public boolean Connected(int u,int v) {
//      return Find(u) == Find(v);
//  }
//  public void Union(int u, int v) {
//      int fu = Find(u), fv = Find(v);
//      id[fv] = fu;
//  }
    public boolean Connected(int u,int v) {
        return id[u] == id[v];
    }
    public void Union(int u, int v) {
        int fu = id[u], fv = id[v];
        for(int i = 0; i < n; i++) {
            if(id[i] == fv) id[i] = fu;
        }
    }
}

注释部分为路径压缩的写法。

WeightedQuickUnion 类

package percolation;

public class WeightedQuickUnion {
    private int n;
    private int[] id, sz;
    public WeightedQuickUnion(int n) {
        this.n = n;
        id = new int[n];
        sz = new int[n];
        for(int i = 0; i < n; i++) {
            id[i] = i;
            sz[i] = 1;
        }
    }
    public int GetN() {
        return n;
    }
    public int Find(int p) {
        while(p != id[p]) p = id[p];
        return p;
    }
    public boolean Connected(int u, int v) {
        return Find(u) == Find(v);
    }
    public void Union(int u, int v) {
        int fu = Find(u), fv = Find(v);
        if(fu == fv) return ;
        if(sz[fu] < sz[fv]) {
            id[fu] = fv;
            sz[fv] += sz[fu];
        }
        else {
            id[fv] = fu;
            sz[fu] += sz[fv];
        }
        n--;
    }
}

Percolation 类

package percolation;

public class Percolation {
    private int n, opt;
    private int[][] a;
    private final int[] dx = {0, 0, 1, -1};
    private final int[] dy = {1, -1, 0, 0};
    private QuickFind qf;
    private WeightedQuickUnion wqu;
    private int GetVal(int i,int j) {
        return (i - 1) * n + j;
    }
    public Percolation(int n, int opt) {
        if(n <= 0) throw new IllegalArgumentException("n is not a postive number.");
        this.n = n;
        this.opt = opt;
        a = new int[n + 2][n + 2];
        if(opt == 0) qf = new QuickFind(n * n + 2);
        else wqu = new WeightedQuickUnion(n * n + 2);
        a[1][0] = a[n][n + 1] = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) this.a[i][j] = -1;
        }
    }
    public void Open(int i, int j) {
        if(i > n || i < 1 || j > n || j < 1) throw new IndexOutOfBoundsException("i or j are not in the correct range.");
        if(a[i][j] != 0) {
            a[i][j] = 0; 
            if(opt == 0) {
                if(i == 1) qf.Union(GetVal(1, 0), GetVal(i, j));
                else if(i == n) qf.Union(GetVal(n, n + 1),GetVal(i, j));
                for(int k = 0; k < 4; k++) {
                    int x = i + dx[k], y = j + dy[k];
                    if(x > n || x < 1 || y > n || y < 1 || this.a[x][y] == -1) continue;
                    qf.Union(GetVal(x, y),GetVal(i, j));
                }
            }
            else {
                if(i == 1) wqu.Union(GetVal(1, 0), GetVal(i, j));
                else if(i == n) wqu.Union(GetVal(n, n + 1),GetVal(i, j));
                for(int k = 0; k < 4; k++) {
                    int x = i + dx[k], y = j + dy[k];
                    if(x > n || x < 1 || y > n || y < 1 || this.a[x][y] == -1) continue;
                    wqu.Union(GetVal(x, y),GetVal(i, j));
                }
            }
        }
    } 
    public boolean IsOpen(int i, int j) {
        if(i > n || i < 1 || j > n || j < 1) throw new IndexOutOfBoundsException("i or j are not in the correct range.");
        return this.a[i][j] == 0;
    }
    public boolean IsFull(int i, int j) {
        if(i > n || i < 1 || j > n || j < 1) throw new IndexOutOfBoundsException("i or j are not in the correct range.");
        return this.a[i][j] == 1;
    }
    public boolean Percolates(int opt) {
        if(opt == 0) return qf.Connected(0, n*n+1);
        return wqu.Connected(0, n*n+1);
    }
}

PercolationStats 类

package percolation;

import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.StdStats;

public class PercolationStats {
    private int n, t;
    private double mean, stddev, confidenceLo, confidenceHi;
    private double[] data;
    public PercolationStats(int n, int t, int opt) {
        if(n <= 0 || t <= 0) throw new IllegalArgumentException();
        this.n = n;
        this.t = t;
        if(n == 1) {
            mean = 1;
            stddev = Double.NaN;
            confidenceLo = Double.NaN;
            confidenceHi = Double.NaN;
        }
        else {
            data = new double[n];
            for(int i = 0; i < n; i++) {
                Percolation percolation = new Percolation(n, opt);
                int num_open = 0;
                while(!percolation.Percolates(opt)) {
                    int row = StdRandom.uniformInt(n) + 1;
                    int col = StdRandom.uniformInt(n) + 1;
                    if(!percolation.IsOpen(row, col)) {
                        percolation.Open(row, col);
                        num_open++;
                    }
                }
                data[i] = 1.0 * num_open / (n * n);
            }
            mean = StdStats.mean(data);
            stddev = StdStats.stddev(data);
            confidenceLo = mean - 1.96 * stddev / Math.sqrt(t);
            confidenceHi = mean + 1.96 * stddev / Math.sqrt(t);
        }
    }
    public double GetN() {
        return n;
    }
    public double GetT() {
        return t;
    }
    public double Mean() {
        return mean;
    }
    public double Stddev() {
        return stddev;
    }
    public double ConfidenceLo() {
        return confidenceLo;
    }
    public double ConfidenceHi() {
        return confidenceHi;
    }
    public static void main(String[] args) {
        int n = 2000, t = 50;
        for(int i = 0; i < 2; i++) {
            if(i == 0) System.out.println("The algorithm now used is Quick_Find.");
            else System.out.println("The algorithm now used is Weighted_Quick_Union.");
            long starttime = System.nanoTime();
            PercolationStats percolationstats = new PercolationStats(n, t, i);
            System.out.println("Mean is :" + percolationstats.Mean());
            System.out.println("Stddev is : " + percolationstats.Stddev());
            System.out.println("ConfidenceLo is :" + percolationstats.ConfidenceLo());
            System.out.println("ConfidenceHi is :" + percolationstats.ConfidenceHi());
            long endtime = System.nanoTime();
            double cost_time = 1.0 * (endtime - starttime) / 1000000.0;
            System.out.println("The cost of time is :" + cost_time + " ms.");
        }
    }
}

运行示例

$N=200,T=100$ 时的运行结果。

Avatar photo
HoshiuZ
文章: 12

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注