实验一: 渗透问题

一、 实验目的

使用合并-查找(union-find)数据结构,编写程序通过蒙特卡罗模拟 (Monte Carlo simulation)来估计渗透阈值的值。

二、 题目描述

我们使用 N*N 网格点来模型一个渗透系统。每个格点或是 open 格点或 是 blocked 格点。一个 full site 是一个 open 格点,它可以通过一连串的 邻近(左,右,上,下)open 格点连通到顶行的一个 open 格点。如果在底 行中有一个 full site 格点,则称系统是渗透的。(对于绝缘/金属材料的 例子,open 格点对应于金属材料,渗透系统有一条从顶行到底行的金属路 径,且 full sites 格点导电。对于多孔物质示例,open 格点对应于空格, 水可能流过,从而渗透系统使水充满 open 格点,自顶向下流动。)

问题:在一个著名的科学问题中,研究人员对以下问题感兴趣:如果 将格点以空置概率 p 独立地设置为 open 格点(因此以概率 1-p 被设置为 blocked 格点),系统渗透的概率是多少?当 p=0 时,系统不会渗出; 当 p=1 时,系统渗透。下图显示了 2020 随机网格(左)和 100100 随机网格 (右)的格点空置概率 p 与渗滤概率。

当 N 足够大时,存在阈值 p*,使得当 p<p*,随机 NN 网格几乎不会渗 透,并且当 p>p时,随机 NN 网格几乎总是渗透。尚未得出用于确定渗滤 阈值 p的数学解。我们需要编写一个计算机程序来估计 p*。

Untitled

Untitled

三. 实验思路

先得到一个2020的矩阵,随机生成设置为open的点,通过合并查找算法,将所有open且可以联通的点加入并查集之中,最后测试矩阵的第一行与最后一行是否可以连通,若可以连通则为可以渗透。 通过改变空置占比,来估计p的值,每组采用一定数量的样本,并统计出可以渗透的样本占所有样本之比。

public class Shen {
 class QuickUnionUF
 {
	private int count;
 	private int [] id;
 	public QuickUnionUF(int N)
 	{
 		id=new int[N];
 		count = N;
 		Init();
 	}
 	private int root(int i) {
 		while(i!=id[i]) i=id[i];
 		return i;
 	}//求某一点的根节点,将根节点储存在id数组中。
 	public void Init() {
 		for(int i=0;i<count;i++)id[i]=i;
 	}//初始化矩阵,内容为编号
 	public boolean connected(int p,int q) {
 		return root(p)==root(q);
 	}//判断p和q是否在同一并查集当中。
 	public void union(int p,int q) {
 		int i=root(p);
 		int j=root(q);
 		id [i]=j;
 	}将并查集p和q合并,并且将p加入到j之中
 }

boolean IsNear(int i, int j) {
	return i - j == 1 || j - i == 1 || i - j == size || j - i == size;
}//判断两个格子是否相邻

float scan(int n,int t) {
	float sum=0;

	for (int k = 0; k < t; k++) {
		u.Init();
		Random r = new Random(System.nanoTime());
		Collections.shuffle(randArr, r);
 //初始化矩阵,并通过洗牌算法减少极端情况的出现
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < i; j++) {
				if (IsNear(randArr.get(i), randArr.get(j)) && !u.connected(randArr.get(i), randArr.get(j))) {
					u.union(randArr.get(i), randArr.get(j));
				}
			}
		}//判断格子是否相邻,若相邻且都为联通,将两个格子合并。

		boolean isDone = false;
		for (int i = 0; i < size; i++) {
			for (int j = size*(size - 1); j < size * size; j++) {
				if (u.connected(i, j)) {
					isDone = true;
					break;
				}
			}
			if (isDone) {
				break;
			}
		}
		if (isDone) {
			sum++;
		}

	}判断将矩阵第一行与矩阵最后一行的元素是否联通,并统计通过的数量sum;
	System.out.println(sum/t + " " + ((float)n) / size / size);
	return ((float)n) / size / size;

}//计算该p值下渗透的概率

int size = 50;//设矩阵为50*50大小
QuickUnionUF u = new QuickUnionUF(size*size);
ArrayList<Integer> randArr = new ArrayList<Integer>();

public static void main(String[] args) {
	Shen shen = new Shen();
	for (int i = 0; i < shen.size * shen.size; i++) {
		shen.randArr.add(i);
	}

	for(int i=0;i<=shen.size*shen.size;i++) {
		shen.scan(i,30);//每个p值的样本设为30
	}
}

五. 实验结果

可以发现在p=0.59到p=0.60时,渗透概率会发生徒增在p=0.60之后几乎一直为1,与题目中的0.593还有一定差异,我觉得时样本数量和矩阵大小所带来的误差,如果增加样本数量(实验中设为30)可能会使结果更加精确。

实验二:排序算法比较

一、 实验目的

通过对多种排序算法的实现,针对不同输入规模的数据进行实验,比较各种排序算法的时间性能。

二、 题目描述

实现插入排序(Insertion Sort,IS),自顶向下归并排序(Top-down Mergesort,TDM),自底向上归并排序(Bottom-up Mergesort,BUM),随机快速排序(Random Quicksort,RQ),Dijkstra 3-路划分快速排序 (Quicksort with Dijkstra 3-way Partition,QD3P)

三、 实验要求