1.Pi值估算原理
Hadoop自带的例子中,有一个计算Pi值的例子。这例子比较全面,它用的API是旧版的。本章先分析一下这个例子,然后再用新版的API重新实现一下。
这个程序的原理是这样的。假如有一个边长为1的正方形。以正方形的一个端点为圆心,以1为半径,画一个圆弧,于是在正方形内就有了一个直角扇形。在正方形里随机生成若干的点,则有些点是在扇形内,有些点是在扇形外。正方形的面积是1,扇形的面积是0.25*Pi。设点的数量一共是n,扇形内的点数量是nc,在点足够多足够密集的情况下,会近似有nc/n的比值约等于扇形面积与正方形面积的比值,也就是nc/n= 0.25*Pi/1,即Pi = 4*nc/n。
如何生成随机点?最简单的方式是在[0,1]的区间内每次生成两个随机小数作为随机点的x和y坐标。可惜这种生成方式效果不够好,随机点之间有间隙过大和重叠的可能,会让计算精度不够高。Halton序列算法生成样本点的效果要好得多,更均匀,计算精度比随机生成的点更高,因此这个例子用Halton序列算法生成点集。关于Halton序列可以参考这里http://orion.math.iastate.edu/reu/2001/voronoi/halton_sequence.html和这里http://www.aae.wisc.edu/dphaneuf/AAE%20875/Halton%20sequences.pdf,在这里就不详细说了。
在正方形内生成的样本点越多,计算Pi值越精确,这样,这个问题就很适合用Hadoop来处理啦。假设要在正方形内生成1000万个点,可以设置10个Map任务,每个Map任务处理100万个点,也可以设置100个Map任务,每个Map任务处理10万个点。
2.旧版API的Pi值估算MapReduce程序
此处带来来自Hadoop的示例程序。
为了计算,设置10个Map任务,每个任务处理1000个点,具体流程是这样的:
1)运行PiEstimator的MapReduce程序,输入参数是10,1000,意思是设置10个Map任务,每个Map任务处理1000个点。
2)PiEstimator进行初始化。初始化时,有一个步骤是在HDFS上生成一个目录,也就是输入目录。这个目录下有10个序列文件。Map任务的数量的数量决定序列文件的数量,PiEstimator就生成有10个序列文件。每个序列文件保存两个整数,分别是要处理的样本点在Halton序列的序号和生成样本点的数量。也就是说,第一个文件的内容是”0,1000”,第二个文件的内容是”1000,1000”,第三个文件的内容是“2000,1000”,第四个文件的内容是“3000,1000”,以此类推。如果用Halton序列算法生成一万个样本点,那么,第一个Map任务生成的点的序号是从0到999,第二个Map任务生成的点的序号是从1000到1999,第三个Map任务生成的点的序号是从2000到2999,以此类推。Halton序列算法生成随机点的的唯一参数是序号。
3)PiEstimator运行MapReduce任务。
4)PiEstimator从MapReduce的输出目录读取两个整数,它们分别是直角扇形内的点的数量和直角扇形外的点的数量。
5)根据4)的结果数值,计算Pi值,然后返回。
PiEstimator.java文件的对应PiEstimator类。PiEstimator类有三个内部类,分别是HalthonSequence类,PiMapper类,PiReducer类。HalthonSequence类负责产生样本点,PiMapper类是Map过程,PiReducer类是Reduce过程。
PiRefuce.java的代码如下:
-
packageorg.apache.hadoop.examples;
importjava.io.IOException;
importjava.math.BigDecimal;
importjava.util.Iterator;
importorg.apache.hadoop.conf.Configured;
importorg.apache.hadoop.fs.FileSystem;
importorg.apache.hadoop.fs.Path;
importorg.apache.hadoop.io.BooleanWritable;
importorg.apache.hadoop.io.LongWritable;
importorg.apache.hadoop.io.SequenceFile;
importorg.apache.hadoop.io.Writable;
importorg.apache.hadoop.io.WritableComparable;
importorg.apache.hadoop.io.SequenceFile.CompressionType;
importorg.apache.hadoop.mapred.FileInputFormat;
importorg.apache.hadoop.mapred.FileOutputFormat;
importorg.apache.hadoop.mapred.JobClient;
importorg.apache.hadoop.mapred.JobConf;
importorg.apache.hadoop.mapred.MapReduceBase;
importorg.apache.hadoop.mapred.Mapper;
importorg.apache.hadoop.mapred.OutputCollector;
importorg.apache.hadoop.mapred.Reducer;
importorg.apache.hadoop.mapred.Reporter;
importorg.apache.hadoop.mapred.SequenceFileInputFormat;
importorg.apache.hadoop.mapred.SequenceFileOutputFormat;
importorg.apache.hadoop.util.Tool;
importorg.apache.hadoop.util.ToolRunner;
public classPiEstimator extends Configured implements Tool {
//临时目录的路径,保存运行中的文件数据。
static privatefinal Path TMP_DIR = new Path(
PiEstimator.class.getSimpleName()+ "_TMP_3_141592654");
//Halton序列类,产生随机点。
private staticclass HaltonSequence {
static finalint[] P = {2, 3};
static finalint[] K = {63, 40};
private longindex;
privatedouble[] x;
privatedouble[][] q;
privateint[][] d;
//构造函数
HaltonSequence(longstartindex) {
index =startindex;
x = newdouble[K.length];
q = newdouble[K.length][];
d = newint[K.length][];
for(int i= 0; i < K.length; i++) {
q[i] =new double[K[i]];
d[i] =new int[K[i]];
}