美文网首页
随机模拟算法求解圆周率

随机模拟算法求解圆周率

作者: 丶legend | 来源:发表于2018-01-26 19:53 被阅读0次

圆周率(π)这个东西是从小学开始一直陪伴我们的,这里使用使用蒙特卡洛算法来产生大量的随机数求解π的近似值。

计算方式

首先我们知道 正方形的面积公式是S1 = a * a,圆形的面积S2 = π * r * r;
所以以圆的直径为正方形边长,可以得出π的表达式。

π = 4 * S2 / S1 

这样一来,重点就是求解正方形和圆形的面积,这里使用在一正方形区域内圆内产生相应的随机点,
为了便于可视化分析,在圆内和圆外的点分别用不同的颜色来表示,最后圆内产生的点数就近似记作圆的面积,区域内的点数记作正方形的面积。

可视化分析-小数据集

这里用Java swing来求解。

首先看看Circle的model类

public class Circle {

    private int x, y, r;

    public Circle(int x, int y, int r){
        this.x = x;
        this.y = y;
        this.r = r;
    }

    public int getX(){ return x; }
    public int getY(){ return y; }
    public int getR(){ return r; }
  // 判断点是否包含在圆内
    public boolean contain(Point p){
        return Math.pow(p.x - x, 2) + Math.pow(p.y - y, 2) <= r*r;
    }
}

这里定义了圆的坐标,以及圆的半径。
然后看看点的model类

public class MonteCarloPiData {

    private Circle circle;
    private LinkedList<Point> points;
    private int insideCircle = 0;

    public MonteCarloPiData(Circle circle){
        this.circle = circle;
        points = new LinkedList<Point>();
    }

    public Circle getCircle(){
        return circle;
    }
    // 得到点的数量
    public int getPointsNumber(){
        return points.size();
    }
  
    public Point getPoint(int i){
        if(i < 0 || i >= points.size()) {
            throw new IllegalArgumentException("out of bound in getPoint!");
        }

        return points.get(i);
    }
  //添加点到链表中
    public void addPoint(Point p){
        points.add(p);
        // 计算圆内点的数量
        if(circle.contain(p)) {
            insideCircle++;
        }
    }
    // π值的计算 
    public double estimatePi(){
        if(points.size() == 0) {
            return 0.0;
        }
        int circleArea = insideCircle;
        int squareArea = points.size();
        return (double)circleArea * 4 / squareArea;
    }
}

接下来就是辅助类

public class AlgoVisHelper {
     
    public static final Color Red = new Color(0xF44336);
    public static final Color Green = new Color(0x4CAF50);

    private AlgoVisHelper() {
    
    // 绘制圆
    public static void strokeCircle(Graphics2D g, int x, int y, int r){

        Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
        g.draw(circle);
    }
   // 填充圆
    public static void fillCircle(Graphics2D g, int x, int y, int r){

        Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
        g.fill(circle);
    }
   // 绘制矩形
    public static void strokeRectangle(Graphics2D g, int x, int y, int w, int h){

        Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
        g.draw(rectangle);
    }
  // 填充矩形
    public static void fillRectangle(Graphics2D g, int x, int y, int w, int h){

        Rectangle2D rectangle = new Rectangle2D.Double(x, y, w, h);
        g.fill(rectangle);
    }
  // 设置颜色
    public static void setColor(Graphics2D g, Color color){
        g.setColor(color);
    }
   // 设置绘制宽度
    public static void setStrokeWidth(Graphics2D g, int w){
        int strokeWidth = w;
        g.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    }
  // 延长动画时间
    public static void pause(int t) {
        try {
            Thread.sleep(t);
        }
        catch (InterruptedException e) {
            System.out.println("Error sleeping");
        }
    }
}

然后是渲染过程类

public class AlgoFrame extends JFrame{

   private int canvasWidth;
   private int canvasHeight;

   public AlgoFrame(String title, int canvasWidth, int canvasHeight){

       super(title);

       this.canvasWidth = canvasWidth;
       this.canvasHeight = canvasHeight;
        // 实例化画笔  刷新视图
       AlgoCanvas canvas = new AlgoCanvas();
       setContentPane(canvas);
       pack();

       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       setResizable(false);

       setVisible(true);
   }

   public AlgoFrame(String title){

       this(title, 1024, 768);
   }

   public int getCanvasWidth(){return canvasWidth;}
   public int getCanvasHeight(){return canvasHeight;}

   private MonteCarloPiData data;
   public void render(MonteCarloPiData data){
       this.data = data;
       repaint();
   }

   private class AlgoCanvas extends JPanel{

       public AlgoCanvas(){
           // 双缓存
           super(true);
       }

       @Override
       public void paintComponent(Graphics g) {
           super.paintComponent(g);

           Graphics2D g2d = (Graphics2D)g;

           // 抗锯齿
           RenderingHints hints = new RenderingHints(
                   RenderingHints.KEY_ANTIALIASING,
                   RenderingHints.VALUE_ANTIALIAS_ON);
           hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
           g2d.addRenderingHints(hints);

           // 具体绘制
           Circle circle = data.getCircle();
           AlgoVisHelper.setStrokeWidth(g2d, 3);
           AlgoVisHelper.strokeCircle(g2d, circle.getX(), circle.getY(), circle.getR());
           
           /**
             * 点在圆内 就绘制成红色
             * 在圆外 绘制成绿色
             */ 
           for(int i = 0 ; i < data.getPointsNumber() ; i ++){
               Point p = data.getPoint(i);
               if(circle.contain(p)) {
                   AlgoVisHelper.setColor(g2d, AlgoVisHelper.Red);
               } else {
                   AlgoVisHelper.setColor(g2d, AlgoVisHelper.Green);
               }
               // 将点填充到圆内
               AlgoVisHelper.fillCircle(g2d, p.x, p.y, 3);
           }
       }

       @Override
       public Dimension getPreferredSize(){
           return new Dimension(canvasWidth, canvasHeight);
       }
   }
}

最后就是视图类了

public class AlgoVisualizer {

    private static int DELAY = 40;
    private MonteCarloPiData data;
    private AlgoFrame frame;
    private int N;

    public AlgoVisualizer(int sceneWidth, int sceneHeight, int N){

        if(sceneWidth != sceneHeight) {
            throw new IllegalArgumentException("This demo must be run in a square window!");
        }
        this.N = N;

        Circle circle = new Circle(sceneWidth/2, sceneHeight/2, sceneWidth/2);
        data = new MonteCarloPiData(circle);

        // 初始化视图
        EventQueue.invokeLater(() -> {
            frame = new AlgoFrame("Get Pi with Monte Carlo", sceneWidth, sceneHeight);

            new Thread(() -> {
                run();
            }).start();
        });
    }
     // 动画主要逻辑
    public void run(){

        for(int i = 0 ; i < N ; i ++){
            // 每次绘制100个点
            if (i % 100 == 0) {
                frame.render(data);
                // 设置每次产生点的时间
                AlgoVisHelper.pause(DELAY);
                System.out.println(data.estimatePi());
            }
            int x = (int)(Math.random() * frame.getCanvasWidth());
            int y = (int)(Math.random() * frame.getCanvasHeight());
            data.addPoint(new Point(x,y));
        }
}
    public static void main(String[] args) {

        int sceneWidth = 800;
        int sceneHeight = 800;
        // 设置点的数量
        int N = 20000;

        AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N);
    }
}

运行后,就可以看到点的绘制过程,最终结果如下


捕获.PNG

很显然 可以看到结果 精确到小数点后一位,点就无法绘制了,要想更加精确,试试更大的数据集。

无视图-大数据集

public class MonteCarloExeperiment {
    private int squareSide;
    private int N;
    private int outputInterval = 100;

    public MonteCarloExeperiment(int squareSide, int N) {
        if (squareSide <= 0 || N <= 0) {
            throw new IllegalArgumentException("squareSide and N must large than 0!");
        }
        this.squareSide = squareSide;
        this.N = N;
    }
    public void setOutputInterval(int interval) {
        if (interval <= 0) {
            throw new IllegalArgumentException("interval must be larger than 0!");
        }
        this.outputInterval = interval;
    }
    public void run() {
        Circle circle = new Circle(squareSide/2,squareSide/2,squareSide/2);
        MonteCarloPiData data = new MonteCarloPiData(circle);

        for (int i=0;i < N;i++) {
            if (i % outputInterval == 0) {
                System.out.println(data.estimatePi());
            }
            int x = (int)(Math.random() * squareSide);
            int y = (int)(Math.random() * squareSide);
            data.addPoint(new Point(x,y));
        }
    }
    public static void main(String[] args) {

        int squareSide = 800;
        int N = 10000000;

        MonteCarloExeperiment exp = new MonteCarloExeperiment(squareSide,N);
        exp.setOutputInterval(100000);
        exp.run();
    }
}

这里设置了每次产生点的数量,其它逻辑差不多,去掉了视图的绘制, 这次用产生1千万随机数的方法来测试。
run一下,可以看到


捕获.PNG

这次的结果,基本能精确到小数点后面4位、5位了,至此,计算过程就到这里了。

总结

这里使用蒙特卡洛算法,也就是产生更大的数据量是估算的值达到更精确。并且整个过程使用的是MVC的设计,再一次见识了计算机的魅力。

相关文章

  • 随机模拟算法求解圆周率

    圆周率(π)这个东西是从小学开始一直陪伴我们的,这里使用使用蒙特卡洛算法来产生大量的随机数求解π的近似值。 计算方...

  • Scipy 积分

    数值积分,求解圆周率 求解圆周率 integrate对函数(1 - x2)0.5进行积分 首先画一个圆 使用Sci...

  • 数学建模最常用的数学算法

    1、蒙特卡罗算法(该算法又称随机性模拟算法,是通过计算机仿真来解决问题的算法,同时可以通过模拟可以来检验自己模型的...

  • 模拟退火算法

    1.概念 介绍模拟退火前,请先了解爬山算法。因为模拟退火算法是爬山算法的改进版,它在爬山算法的基础上引入了随机化。...

  • 遗传算法在自动组卷中的应用

    遗传算法 遗传算法(Genetic Algorithm)是一种模拟自然界的进化规律-优胜劣汰演化来的随机搜索算法,...

  • 蒙特·卡罗算法

    蒙特·卡罗算法的应用:在资产配置求解“有效前沿”时,由于我们很难解出函数表达式,就可以用蒙特·卡罗算法来进行模拟,...

  • EPI_PrimitiveTypes_No.004_Unifor

    问题简述 给一个2面的硬币,要求设计一个算法来模拟6面骰子。Follow Up:如何模拟任意[a, b]的均匀随机...

  • 模拟退火算法

    说模拟退火算法之前,先介绍爬山算法。爬山算法是优化算法的一种,它的思想是:对于复杂的问题,先给出一个随机的解s1,...

  • 随机模拟

    我们为什么要进行模拟? 一般情况下样本都是从真实世界中采样得到的,虽然我们事先不知道真实世界的概率分布是多少,但是...

  • HMM中的维特比算法

    1. 算法 维特比算法实际上常常被用来求解HMM模型的预测问题。即用动态规划求解概率最大(最优路径)。最后求解出来...

网友评论

      本文标题:随机模拟算法求解圆周率

      本文链接:https://www.haomeiwen.com/subject/tvxeaxtx.html