(一)渗透问题(Percolation)
-
问题描述:
我们使用N×N网格点来模型一个渗透系统。每个格点或是open格点或是blocked格点。 一个full site是一个open格点,它可以通过一连串的邻近(左,右,上,下)open格点连通到顶行的一个open格点。如果在底行中有一个full site格点,则称系统是渗透的。 -
数据类型
Percolation数据类型:模型化一个Percolation系统,创建含有以下API的数据类型Percolation。
public class Percolation {
public Percolation(int N) // create N-by-N grid, with all sites blocked
public void open(int i, int j) // open site (row i, column j) if it is not already
public boolean isOpen(int i, int j) // is site (row i, column j) open?
public boolean isFull(int i, int j) // is site (row i, column j) full?
public boolean percolates() // does the system percolate?
public static void main(String[] args) // test client, optional
}
PercolationStats数据类型:我们创建数据类型PercolationStats来执行一系列计算实验,包含以下API。
public class PercolationStats {
public PercolationStats(int N, int T) // perform T independent computational experiments on an N-by-N grid
public double mean() // sample mean of percolation threshold
public double stddev() // sample standard deviation of percolation threshold
public double confidenceLo() // returns lower bound of the 95% confidence interval
public double confidenceHi() // returns upper bound of the 95% confidence interval
public static void main(String[] args) // test client, described below
}
- 问题分析:
首先,可以确定该问题使用合并-查找(union-find)数据结构,把一个节点和他四周相邻的任意open节点合并(union),只要第一行的任意节点和第N行的任意节点处于同一并查集里,及渗透系统可渗透,问题解决。
初始思路是用一个复杂度N*N的两层for循环,检查每一个第一行节点和每一个第N行的节点是否相连,但是如果N非常大,该思路的效率就很低,于是思考,在第一行之上加一个虚拟节点p,和第一行的每个open节点联通,在第N行之下加一个虚拟节点q,和第N行的每个open节点联通,这样只用判断p和q是否连通即可。 - 测试渗透算法PercolationStats
- 初始化所有格点为blocked。
- 重复以下操作直到系统渗出:
①在所有blocked的格点之间随机均匀选择一个格点 (row i, column j)。
②设置这个格点(row i, column j)为open格点。 - open格点的比例提供了系统渗透时渗透阈值的一个估计。
重复测试渗透阈值,计算均值、标准差、95%置信区间上限及下限。
- 比较不同的合并-查找算法
规模:N=50,T=100
规模:N=100,T=100
规模:N=200,T=100
- 示例输出
(二)几种排序算法的实验性能比较
- 问题描述:
实现插入排序(Insertion Sort,IS),自顶向下归并排序(Top-down Mergesort,TDM),自底向上归并排序(Bottom-up Mergesort,BUM),随机快速排序(Random Quicksort,RQ),Dijkstra 3-路划分快速排序(Quicksort with Dijkstra 3-way Partition,QD3P)。在你的计算机上针对不同输入规模数据进行实验,对比上述排序算法的时间及空间占用性能。要求对于每次输入运行10次,记录每次时间/空间占用,取平均值。 - 问题分析:
写出5种排序算法,并随机生成100000个随机数分别用5种排序算法进行排序,记录运行时间和内存空间占用 - 几种排序算法性能比较
数据规模:随机生成10000个1到100000的随机整数用不同排序算法排序,每次计算100次排序的时间总和
Comparison of running time of sorting algorithms (in Micro Seconds)
Comparison of space usage of sorting algorithms (in Kilo Bytes)
- 结果分析
通过上面两个表格可知,对于随机整数(顺序打乱):
插入排序最慢,且与其他几种排序形成鲜明对比,但内存占用最小,若内存空间紧张且对时间性能要求不高,可以选择插入排序。
两种归并排序由于需要辅助数组,内存空间占用很大,但时间性能比插入排序快很多。
快速排序和三路快排时间和空间性能都较为优秀。
(三)地图路由(Map Routing)
- 问题描述
实现经典的Dijkstra最短路径算法,并对其进行优化。
想法1. Dijkstra算法的朴素实现检查图中的所有V个顶点。 减少检查的顶点数量的一种策略是一旦发现目的地的最短路径就停止搜索。 通过这种方法,可以使每个最短路径查询的运行时间与E’ log V’成比例,其中E’和V’是Dijkstra算法检查的边和顶点数。 然而,这需要一些小心,因为只是重新初始化所有距离为∞就需要与V成正比的时间。由于你在不断执行查询,因而只需重新初始化在先前查询中改变的那些值来大大加速查询。
想法2. 你可以利用问题的欧式几何来进一步减少搜索时间,这在算法书的第21.5节描述过。对于一般图,Dijkstra通过将d[w]更新为d[v] + 从v到w的距离来松弛边v-w。 对于地图,则将d[w]更新为d[v] + 从v到w的距离 + 从w到d的欧式距离 从v到d的欧式距离。 这种方法称之为A*算法。这种启发式方法会有性能上的影响,但不会影响正确性。 - 问题分析
这个问题要求我们先读取地图数据,根据数据画出地图,输入两个点,输出这两个点之间的最短路径,以及最短路径的长度。这是经典Dijkstra就可以实现的。但经典Dijkstra对于该问题来说,有一个最大的问题就是冗余计算,所以我们要根据问题描述里的想法一和想法二进行优化。
我对于想法一的优化是:检测索引优先队列每次delmin()弹出的节点,若弹出的节点正是输入的终点,就停止程序的继续检索。
我对于想法二的优化是:建立无向边Edge的类,以表示边的数据结构,Edge里包含了v(边的顶点之一),w(边的另一个顶点),weight(边的权重)这三个属性,在读取地图信息时,每读一个边,就计算他的权重,存入WEIGHT。 - 结果分析
画出地图并标出第1到第999个节点的最短路径(黑色为地图,红色为最短路径)
路径的具体内容:
from: 1 to: 999
1-4 1.00000
4-18 13.60147
18-34 25.49510
34-37 1.41421
37-46 10.29563
46-50 2.23607
50-57 4.00000
57-78 8.00000
78-84 3.60555
84-87 3.16228
87-94 6.32456
94-120 18.78829
120-125 3.16228
125-143 12.80625
143-169 25.94224
169-183 6.70820
183-206 23.32381
206-215 9.84886
215-232 23.60085
232-271 23.43075
271-278 2.82843
278-279 1.41421
279-285 0.00000
285-303 5.00000
303-312 1.41421
312-340 10.04988
340-360 10.00000
360-410 18.02776
410-422 3.16228
422-441 8.54400
441-459 8.94427
459-479 9.00000
479-505 16.03122
505-545 14.31782
545-585 5.09902
585-600 5.00000
600-632 7.00000
632-638 2.23607
638-660 11.04536
660-670 6.32456
670-678 5.09902
678-681 1.41421
681-685 2.23607
685-697 6.08276
697-729 16.49242
729-734 2.23607
734-760 9.43398
760-785 8.60233
785-804 7.28011
804-811 6.32456
811-856 33.61547
856-859 2.23607
859-892 17.00000
892-896 2.23607
896-900 8.60233
900-921 11.40175
921-923 2.23607
923-945 14.21267
945-944 2.00000
944-981 37.57659
981-991 3.16228
991-999 2.00000
路径总权重:573.6662959705246
(源代码太长了,所以只叙述思想)