各种求圆周率π的算法(蒙特卡洛法的Java实现)

来源:互联网 发布:贵阳大数据交易所地址 编辑:程序博客网 时间:2024/06/10 14:46

什么是算法?简单地说,算法就是有穷规则构成的用于解决某一类问题的运算序列或执行步骤。在《算法之美:隐匿在数据结构背后的原理》第1章中我们讲到要解决一个问题可能会有不同的方法,当时所举的例子就是求圆周率π的近似值。对于这个问题你能想到多少种算法呢?

探秘算法世界,求索数据结构之道;

汇集经典问题,畅享编程技法之趣;

点拨求职热点,敲开业界名企之门。

本书内容简介及勘误表请参见《算法之美隆重上市欢迎关注(另附勘误表在此)》。


方法(1):首先根据微积分中的泰勒公式,可以使用下面这个公式来求π的近似值


方法(2):其次可以还可以利用下面这个马青公式来求解π的近似值:


马青公式由英国天文学家约翰·马青(John Machin,1686 –1751)于1706年发现,他利用这个公式计算到了100位的圆周率。此外,上式中的反三角函数可以领用下面这个泰来展开式进行计算:


方法(3):最后,也是我们准备来动手实践一下的就是利用蒙特卡洛法。这个方法背后的理论基础是概率论中的伯努利大数定律。众所周知,圆的面积公式为 πr2,当 r = 1 时,S = π。如果在一个边长为1的正方形中有一个内切圆,那么该圆的面积就是π/4。利用这一原理,可以假设有大量的随机点等概率均匀地落入此正方形中。若正方形的面积为S',内切圆的面积为S,如果落入正方形中的点数为 N',落入内切圆的面积为N,这时便有 S'/S = N'/N,所以可据此求得π = N/N'

下面就编写一个Java程序来实现蒙特卡洛法求π的近似值:

[java] view plain copy
  1. import java.io.BufferedReader;  
  2. import java.io.InputStreamReader;  
  3.   
  4. public class MonteCarloPi {  
  5.   
  6.     public static void main(String[] args) throws Exception{  
  7.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
  8.         System.out.print("How many darts shoud I toss at the board?\n");  
  9.         String s = br.readLine();  
  10.         int numberOfDarts = Integer.parseInt(s.trim());  
  11.         double radius = 1.0;  
  12.         Dartboard d = new Dartboard(radius);  
  13.           
  14.         for(int i=1; i<=numberOfDarts; i++){  
  15.             Toss t = Toss.getRandom(radius);  
  16.             d.strike(t);  
  17.         }  
  18.           
  19.         double fractionIn = d.getFractionIn();  
  20.         double pi = 4.0 * fractionIn;  
  21.         System.out.println("Pi is approximately "+pi);  
  22.     }  
  23.   
  24. }  
  25.   
  26. class Dartboard{  
  27.       
  28.     private double radius;  
  29.     private int insideCircle, outsideCircle;  
  30.       
  31.     public Dartboard(double radius){  
  32.         this.radius = radius;  
  33.         insideCircle = 0;  
  34.         outsideCircle = 0;  
  35.     }  
  36.       
  37.     public void strike(Toss toss){  
  38.         double x = toss.getX();  
  39.         double y = toss.getY();  
  40.           
  41.         if(Math.sqrt(x*x + y*y) < radius)  
  42.             insideCircle++;  
  43.         else  
  44.             outsideCircle++;  
  45.     }  
  46.       
  47.     public double getFractionIn() {  
  48.         double total = (double) (insideCircle + outsideCircle);  
  49.         return (double) insideCircle/total;  
  50.     }  
  51. }  
  52.   
  53. class Toss{  
  54.       
  55.     private double x,y;  
  56.       
  57.     public Toss(double x, double y){  
  58.         this.x = x;  
  59.         this.y = y;  
  60.     }  
  61.     public double getX(){return x;}  
  62.     public double getY(){return y;}  
  63.       
  64.     public static Toss getRandom(double radius){  
  65.           
  66.         double x,y;  
  67.         double size = Math.random();  
  68.         double sign = Math.random();  
  69.         size = size * radius;  
  70.           
  71.         if(sign > 0.5)  
  72.             x = size;  
  73.         else  
  74.             x = -size;  
  75.           
  76.         size = Math.random();  
  77.         sign = Math.random();  
  78.         size = size * radius;  
  79.           
  80.         if(sign > 0.5)  
  81.             y = size;  
  82.         else  
  83.             y = -size;  
  84.           
  85.         return new Toss(x,y);  
  86.     }  
  87. }  

执行一下上述代码,然后我们来验证一下其效果如何...
[plain] view plain copy
  1. How many darts shoud I toss at the board?  
  2. 1000000  
  3. Pi is approximately 3.142028  

可见所得到的结果较好地近似于圆周率 π 。

(本文完)