用hadoop计算PI值

16 篇文章 0 订阅
12 篇文章 1 订阅

一、计算PI值的方式与原理

      百度一下,计算PI的方法还真不少。但在hadoop examples代码中的注释写的是:是采用 Quasi-Monte Carlo 算法来估算PI的值。 

      维基百科中对Quasi-Monte Carlo的描述比较理论,好多难懂的公式。 

      好在google了一把,找到了斯坦福大学网站上的一篇文章:《通过扔飞镖也能得出PI的值?》,文章很短,图文并茂,而且很好理解。 

      我这里将那篇文章的重要部分截了个图: 



      对上面的图再稍微解释一下: 
      1、Figure2是Figure1的右上角的部分。 
      2、向Figure2中投掷飞镖若干次(一个很大的数目),并且每次都仍在不同的点上。 
      3、如果投掷的次数非常多,Figure2将被刺得“千疮百孔”。 
      4、这时,“投掷在圆里的次数”除以“总投掷次数”,再乘以4,就是PI的值!(具体的推导过程参见原文) 

        也可以这样子理解,在一个正方形上抛飞镖,假设有一个半径为正方形边长的1/4圆,则抛的飞镖在1/4圆内的概率为1/4圆面积除以正方形

面积,即(pi*r^2/4)/r^2,假设抛飞镖在圆内的概率为n,则pi=4n,通过蒙特卡洛模拟则可算出一个较接近的pi。由于频率是不断接近概率的,因此必须抛很多次飞镖,这里采用了Hadoop去模拟。


      在这个算法中,很重要的一点是:如何做到“随机地向Figure2投掷”,就是说如何做到Figure2上的每个点被投中的概率相等。 

      hadoop examples代码中,使用了Halton sequence保证这一点,关于Halton sequence,大家可以参考维基百科

      我这里再总结一下Halton sequence的作用: 在1乘1的正方形中,产生不重复,并且均匀的点。每个点的横坐标和纵坐标的值都在0和1之间。 

正是这样,保证了能够做到“随机地向Figure2投掷”。

      有人总结了一下,这个实际上叫做蒙特卡洛算法,我们取一个单位的正方形(1×1) 里面做一个内切圆(单位圆),则 单位正方形面积 : 内切单位圆面积 = 单位正方形内的飞镖数 : 内切单位圆内的飞镖数 ,通过计算飞镖个数就可以把单位圆面积算出来, 通过面积,在把圆周率计算出来。 
注意 ,精度和你投掷的飞镖次数成正比。

 

二,运行hadoop估算PI的命令

[java] view plaincopyprint?

  1. <span style="white-space:pre">    </span>hadoop jar $HADOOP_HOME/hadoop-*-examples.jar pi 100 100000000  

     后面2个数字参数的含义: 
     第1个100指的是要运行100次map任务 
     第2个数字指的是每个map任务,要投掷多少次 

     2个参数的乘积就是总的投掷次数。 

     我运行的结果: 
Screenshot from 2014-08-30 10_04_15


三,总结

      hadoop的examples中的计算PI的方法属于是采用大量采样的统计学方法,还是属于数据密集型的工作。 

      

    转载请注明出处: http://www.ming-yue.cn/hadoop-pi/

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Hadoop计算Pi的源代码及注释: ```java // PiMapper.java import java.io.IOException; import java.util.Random; import org.apache.hadoop.io.*; import org.apache.hadoop.mapreduce.*; public class PiMapper extends Mapper<Object, Text, IntWritable, IntWritable> { private final static IntWritable one = new IntWritable(1); private static Random rndm = new Random(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { int numThrows = 10000; // 每个Mapper的投掷次数 int inCircle = 0; // 落在圆内的次数 for (int i = 0; i < numThrows; i++) { // 在 [-1, 1] 的范围内随机生成一个点 double x = 2 * rndm.nextDouble() - 1.0; double y = 2 * rndm.nextDouble() - 1.0; // 判断该点是否落在圆内 if (x * x + y * y <= 1.0) { inCircle++; } } // 将落在圆内的次数作为Key,1作为Value写入Context context.write(new IntWritable(inCircle), one); } } ``` ```java // PiReducer.java import java.io.IOException; import org.apache.hadoop.io.*; import org.apache.hadoop.mapreduce.*; public class PiReducer extends Reducer<IntWritable, IntWritable, IntWritable, DoubleWritable> { private DoubleWritable result = new DoubleWritable(); public void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; int count = 0; // 统计Key对应的Value的总和 for (IntWritable val : values) { sum += val.get(); count++; } // 计算Pi的近似 double pi = ((double) sum / (double) count) * 4.0; // 将近似作为Value写入Context result.set(pi); context.write(key, result); } } ``` ```java // PiDriver.java import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.*; import org.apache.hadoop.mapreduce.*; public class PiDriver { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "Pi Estimation"); // 设置Mapper job.setMapperClass(PiMapper.class); job.setMapOutputKeyClass(IntWritable.class); job.setMapOutputValueClass(IntWritable.class); // 设置Reducer job.setReducerClass(PiReducer.class); job.setOutputKeyClass(IntWritable.class); job.setOutputValueClass(DoubleWritable.class); // 设置InputFormat job.setInputFormatClass(TextInputFormat.class); // 设置OutputFormat job.setOutputFormatClass(TextOutputFormat.class); // 设置输入路径和输出路径 FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); // 提交作业并等待完成 System.exit(job.waitForCompletion(true) ? 0 : 1); } } ``` 注释: 1. `PiMapper`类是一个Mapper类,它继承自`Mapper<Object, Text, IntWritable, IntWritable>`。它的输入键是`Object`,输入是`Text`,输出键是`IntWritable`,输出是`IntWritable`。 2. 在`PiMapper`类中,我们首先定义了一个`IntWritable`类型的常量`one`,它的为1。接着,我们使用`java.util.Random`类创建了一个随机数生成器`rndm`。 3. `map`方法是`Mapper`类中的一个重要方法,它的作用是将输入键对转换为输出键对。在`PiMapper`类中,我们将每个Mapper的投掷次数设置为10000,然后在这个范围内随机生成一个点,判断该点是否落在圆内。如果是,我们将`inCircle`变量加1。最后,将落在圆内的次数作为Key,1作为Value写入Context。 4. `PiReducer`类是一个Reducer类,它继承自`Reducer<IntWritable, IntWritable, IntWritable, DoubleWritable>`。它的输入键是`IntWritable`,输入是`IntWritable`,输出键是`IntWritable`,输出是`DoubleWritable`。 5. 在`PiReducer`类中,我们首先定义了一个`DoubleWritable`类型的变量`result`。在`reduce`方法中,我们统计了Key对应的Value的总和和Value的数量。然后,根据公式计算Pi的近似,将近似作为Value写入Context。 6. `PiDriver`类是一个驱动程序。在`main`方法中,我们首先创建了一个`Configuration`对象和一个`Job`对象。然后,我们设置了Mapper、Reducer、InputFormat和OutputFormat。接着,我们设置了输入路径和输出路径。最后,我们提交作业并等待完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值