Coursera 普林斯顿算法课第一周作业percolation(渗透)心得解法
心得体会
这门课程对于编程基础不错的小伙伴们来说应该是蛮容易上手的,但对于我这种leetcode简单level都弄不明白的小白真的不太友好,在完成的过程中遇到许多困难也参考了蛮多大神的思路才慢慢捋清做题步骤,从中也受益良多,而写这边文章的目的就是用新手的方式帮助和我一样的萌新可以不用走太多弯路。因为本人基础不好可能会犯一些概念上的错误也望大家多多指教。
题目:通过蒙特卡洛仿真模型来估算渗透阈值
这里是题目要求:题目要求
题目大意
渗滤: 给定一个由随机分布的绝缘材料和金属材料组成的复合系统:需要多大比例的材料是金属才能使复合系统成为电导体?给定一个表面有水(或下面有油)的多孔景观,在什么条件下水能够流到底部(或油涌到地表)?科学家们定义了一个称为渗透的抽象过程 来模拟这种情况。
模型: 我们使用n × n站点网格对渗透系统进行建模。每个站点要么是打开的,要么是被阻止的。一个完整的网站是一个开放的网站,可以通过周边(左,右,上,下)打开网站的链连接到顶部排在一个开放的网站。我们说系统渗透如果底行有一个完整的站点。换句话说,如果我们填充连接到顶行的所有开放站点并且该过程填充底行的一些开放站点,则系统会渗透。(对于绝缘/金属材料示例,开放位置对应于金属材料,因此渗透的系统具有从上到下的金属路径,并且全部位置导电。对于多孔物质示例,开放位置对应于空的空间水可能会流过它,所以一个渗透系统让水充满开放的地方,从上到下流动。)
问题: 在一个著名的科学问题中,研究人员对以下问题感兴趣:如果站点被独立设置为以概率p开放(因此以概率 1 - p阻塞),系统渗透的概率是多少?当p等于 0 时,系统不渗透;当p等于 1 时,系统会渗透。下图显示了场地空置概率p与 20×20 随机网格(左)和 100×100 随机网格(右)的渗透概率。
当n足够大时,有一个阈值p * 使得当p < p * 随机n × n网格几乎不会渗透,而当p > p * 时,随机n × n网格几乎总是渗透. 尚未推导出用于确定渗透阈值p * 的数学解决方案。你的任务是编写一个计算机程序来估计p *。
蒙特卡洛模型思想:1.初始化所有站点为闭合模式(可以用一个Boolean数组遍历所有的点,若闭合为false,打开为open)2.重复以下操作:在所有关闭的站点中随机选择一个站点并将其打开直到渗透。3.系统打开后打开的站点数量比例为渗透阈值估算值,再进行多次试验精确估算值。
解题思想
Percolation类:
1.这个类是为PercolationStats做准备的类,也就是在前者中构造一些方法便于后者计算P值时调用方法。
2.我建立了一个Boolean二维数组来初始化一张NN的图,然后遍历赋值为false(关闭状态),然后我创建了一个trans方法用来转换数字,将数组中每一个点转换成从1到NN的数字形态。
3.在课程视频中老师提到了虚头节点和虚尾节点,为的是减少复杂度,因此就需要创建两个虚拟节点,头节点我赋值为0,尾节点赋值为NN+1,这样整张图就有NN+2个节点,
因此在实例化方法的时候传参应为N*N+2。这里还有一个值得注意的点是我们需要创建两个WeightedQuickUnionUf对象(不创建也行就是分数会低一些),第一个对象包含虚头节点与虚尾结点,而第二个对象只包含虚头结点,因为在作业打分的时候会有一项测试叫backwash测试,就是判定在渗透状态下是否出现一个点与虚尾结点相连但是未与虚头节点相连但是却显示为isFull情况。便于理解我贴个图:
可以看到图中下面七个点虽然没有与头节点相连却也显示为Full的状态,原因就是只设置了含头尾节点的对象,所以一旦系统渗透,也就是虚头虚尾节点相连的那一刻,所有连接尾节点的点都会显示为Full状态,这显然是不严谨的。
4.open类就是简单粗暴地连接点。首先判定要打开的点是否为关闭状态,是的话就将其打开将点赋值为true,再进行判定如果点位于第一行则将其用union方法与虚头节点连接(这里注意要用两个对象都与虚头节点相连),同理如果点在最后一行就将其与虚尾结点连接。再依次判定该点上下左右各点是否为打开状态,如果是则连接两点。
PercolationStats类:
1.这个类我用的是for循环来执行实验次数,然后在其中嵌套了while循环用来打开随机点直到系统渗透
新手需要注意的点
1.提醒一下一些不细心的小伙伴:上传作业前要把所有的包语句注释掉
2.将所有方法创建为private
下面是代码
Percolation.java
import edu.princeton.cs.algs4.WeightedQuickUnionUF;
public class Percolation {
private final WeightedQuickUnionUF uf;
private final WeightedQuickUnionUF uf1; // 用于连接虚头结点,判断isFull
private final boolean[][] id; // 一开始用的是int二维数组存储,若点是关闭的则赋值为0,但分数较低
private final int virtualTop;
private final int virtualBot;
private int count;
private final int n;
public Percolation(int n) {
if (n <= 0) {
throw new IllegalArgumentException("The number has been a positive number.");
}
id = new boolean[n][n];
// 初始化将整张图关闭的点位赋值为false,打开后赋值为true initialize array and value the empty sites to false
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
id[i][j] = false;
}
}
this.n = n;
// 设置虚节点 create virtual point
virtualTop = 0;
virtualBot = n*n+1;
uf = new WeightedQuickUnionUF(n * n + 2);
uf1 = new WeightedQuickUnionUF(n * n + 1);
}