看得见的算法蒙特卡洛问题——使用蒙特卡洛算法求PI值

看得见的算法蒙特卡洛问题——使用蒙特卡洛算法求PI值

1.什么是蒙特卡洛问题

蒙特卡洛方法(Monte Carlo method),也称统计模拟方法,是二十世纪四十年代中期由于科学技术的发展和电子计算机的发明,而被提出的一种以概率统计理论为指导的一类非常重要的数值计算方法。是指使用随机数(或更常见的伪随机数)来解决很多计算问题的方法。与它对应的是确定性算法。蒙特·卡罗方法在金融工程学,宏观经济学,计算物理学(如粒子输运计算、量子热力学计算、空气动力学计算)等领域应用广泛。

蒙特卡罗方法于20世纪40年代美国在第二次世界大战中研制原子弹的“曼哈顿计划”计划的成员S.M.乌拉姆和J.冯·诺伊曼首先提出。数学家冯·诺伊曼用驰名世界的赌城—摩纳哥的Monte Carlo—来命名这种方法,为它蒙上了一层神秘色彩。在这之前,蒙特卡罗方法就已经存在。1777年,法国数学家布丰(Georges Louis Leclere de Buffon,1707—1788)提出用投针实验的方法求圆周率π。这被认为是蒙特卡罗方法的起源。

2.蒙特卡洛问题求PI值的原理

正方形的面积为4r^2

正方形内接圆的面积为:Πr^2

所以正方形与内接圆的面积的比值为4/Π
所以Π的值为4*(圆的面积/正方形的面积)
由此我们可以在一个正方形和它的接圆内画点,然后一定密度的圆和正方形的点数量的比值就近似为Π值
3.使用算法可视化实现
1.圆对象的数据域(我们把窗体作为正方形)
package com.lipengge.montecarlo.data;

import java.awt.Point;
import java.util.LinkedList;
import java.util.List;

import com.lipengge.montecarlo.model.Circle;

public class MonteCarloData {
	//圆对象
	private Circle circle;
	//用来存储正方形总的点的个数
	public List<Point> list;
	//用来统计在圆内的点的数量
	private int insideCircle;
	public MonteCarloData(Circle circle) {
		this.circle = circle;
		list=new LinkedList<>();
	}
	//获取圆
	public Circle getCircle(){
		return circle;
	}
    //获取点
	public Point getPoint(int index){
		return list.get(index);
	}
	//统计在圆内的点
	public void countInsideCircle(int index){
		if(circle.contains(list.get(index))){
			insideCircle++;
		}
	}
	//计算pi的值
	public double questPI(){
		if(list.size()==0){
			return 0.0;
		}
		return 4*((double)insideCircle/list.size());
	}
	//添加点
	public void addPoint(Point p){
		list.add(p);
	}
}

. 视图层
package com.lipengge.montecarlo.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.lipengge.montecarlo.data.MonteCarloData;
import com.lipengge.randomquestion.util.AlgoVisHelper;

public class AlgoFrame extends JFrame{
	/**
	 * 
	 */
	private static final long serialVersionUID = -3035088527551930125L;
	private int canvasWidth;//画布的宽
	private int canvasHeight;//画布的长
	private MonteCarloData data;//圆的数据域
   public int getCanvasWidth() {
		return canvasWidth;
	}
	public int getCanvasHeight() {
		return canvasHeight;
	}
	public void render(MonteCarloData data){
		this.data=data;
		repaint();
	}
   public AlgoFrame(String title,int  canvasWidth, int canvasHeight){
	  super(title);
	  this.canvasWidth=canvasWidth;
	  this.canvasHeight=canvasHeight;
	  setDefaultCloseOperation(EXIT_ON_CLOSE);
	  setResizable(false);//关闭可拖动
	  setVisible(true);
	  AlgoCanvas algoCanvas = new AlgoCanvas();
	  setContentPane(algoCanvas);
	  pack();
   }
   //设置面板JPanel
   class AlgoCanvas extends JPanel{
	/**
	 * 
	 */
	private static final long serialVersionUID = -6056196800598676936L;
    private  AlgoCanvas(){
    	//开启双缓存
    	super(true);
    }
	@Override
	//在面板上绘制内容
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d=(Graphics2D) g;
		AlgoVisHelper.setRenderingHints(g2d);//设置抗锯齿
		AlgoVisHelper.setStrokeWidth(g2d, 1);//设置边的宽度为5
		AlgoVisHelper.setColor(g2d, Color.green);
		//画圆
		AlgoVisHelper.strokeCircle(g2d,data.getCircle().x,data.getCircle().y,data.getCircle().getR());
		for(int i=0;i<data.list.size();i++){
			//如果在园内点为红色
		  if(data.getCircle().contains(data.list.get(i))){
			  AlgoVisHelper.setColor(g2d, Color.RED);
			  AlgoVisHelper.fillCircle(g2d,data.getPoint(i).x,data.getPoint(i).y, 3);
		  }else{
			  //如果点在圆外为蓝色
			  AlgoVisHelper.setColor(g2d, Color.BLUE);
			  AlgoVisHelper.fillCircle(g2d,data.getPoint(i).x,data.getPoint(i).y, 3);
		  }
		}
	}
	@Override
	public Dimension getPreferredSize() {
		return new Dimension(canvasWidth,canvasHeight);
	}
   }
}

3.圆对象
package com.lipengge.montecarlo.model;

import java.awt.Point;

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

     public int getR() {
		return r;
	}

	public void setR(int r) {
		this.r = r;
	}

	//求点是否在圆内
	public boolean contains(Point p){
		return Math.pow(p.x-x, 2)+Math.pow(p.y-y,2)<=Math.pow(r, 2);
	}
}

4.控制层
package com.lipengge.montecarlo.controller;

import java.awt.EventQueue;
import java.awt.Point;

import com.lipengge.montecarlo.data.MonteCarloData;
import com.lipengge.montecarlo.model.Circle;
import com.lipengge.montecarlo.view.AlgoFrame;
import com.lipengge.randomquestion.util.AlgoVisHelper;

public class AlgoVisualizer {
	private AlgoFrame frame;
	private MonteCarloData data;
	private int Number;
	private int screenWidth;
	public AlgoVisualizer(int screenWidth,int screenHeight,int Number){
		if( screenWidth!=screenHeight){
			throw new IllegalArgumentException("输入不符合要求");
		}
		//实例化圆
		Circle circle=new Circle(screenWidth/2, screenWidth/2,screenWidth/2);
		data=new MonteCarloData(circle);
		this.Number=Number;
		this.screenWidth=screenWidth;
		//初始化视图
		EventQueue.invokeLater(()->{
			frame=new AlgoFrame("蒙特卡罗算法求PI",screenWidth,screenHeight);
			new Thread(()->run()).start();
		});
	}
	//播放动画
	public void run(){
			for(int i=0;i<Number;i++){
				//每生成100个点渲染一次
				if(i%100==0){
					//绘制数据
					frame.render(data);
					//线程休眠20ms
					AlgoVisHelper.pause(10);
					System.out.println(data.questPI());
				}
				    //随机生成点的坐标
					int x=(int)(Math.random()*screenWidth);
					int y=(int)(Math.random()*screenWidth);
					data.addPoint(new Point(x,y));
					//如果点在圆内则圆内点数量++
					data.countInsideCircle(i);
				
			}
	}
 public static void main(String[] args) {
  new AlgoVisualizer(1000,1000,10000);
  }
}

5.帮助类
package com.lipengge.montecarlo.util;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

public class AlgoVisHelper {
	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 strokeReactangle(Graphics2D g,int x,int y,int width,int height){
		Rectangle2D reactangle = new Rectangle2D.Double(x,y,width,height);
		g.draw(reactangle);
	}
	//填充矩形的辅助类
	public static void fillReactangle(Graphics2D g,int x,int y,int width,int height){
		Rectangle2D reactangle = new Rectangle2D.Double(x,y,width,height);
		g.fill(reactangle);
	}
	//设置画笔颜色
    public static void setColor(Graphics2D g,Color color){
    	g.setColor(color);
    }
    //设置边的宽度和平滑性
    public static void setStrokeWidth(Graphics2D g,int w){
    	//BasicStroke.CAP_ROUND把定点变为圆弧半径为画笔一半
    	//BasicStroke.JOIN_ROUND把线条连接点变为弧形
    	g.setStroke(new BasicStroke(w,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
    }
    //设置抗锯齿
    public static void setRenderingHints(Graphics2D g){
    	RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    	hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    	g.addRenderingHints(hints);
    }
    public static void pause(long millsecond){
    	try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    }
    
}

运行效果及结果分析

在这里插入图片描述

每次绘制变化的结果

0.0
3.12
3.02
3.0933333333333333
3.08
3.12
3.1333333333333333
3.12
3.15
3.137777777777778
3.152
3.1345454545454547
3.1366666666666667
3.129230769230769
3.1314285714285712
3.1493333333333333
3.14
3.1482352941176472
3.151111111111111
3.134736842105263
3.13
3.1352380952380954
3.130909090909091
3.1426086956521737
3.135
3.1344
3.129230769230769
3.1244444444444444
3.1228571428571428
3.1241379310344826
3.1293333333333333
3.1329032258064515
3.13625
3.1454545454545455
3.1411764705882352
3.1485714285714286
3.152222222222222
3.145945945945946
3.1526315789473682
3.154871794871795
3.153
3.153170731707317
3.1485714285714286
3.142325581395349
3.140909090909091
3.137777777777778
3.130434782608696
3.1336170212765957
3.135
3.1330612244897957
3.1328
3.1341176470588237
3.140769230769231
3.1456603773584906
3.1444444444444444
3.1432727272727274
3.1435714285714287
3.1473684210526316
3.147586206896552
3.145084745762712
3.1453333333333333
3.142950819672131
3.1432258064516128
3.1453968253968254
3.145
3.144
3.1466666666666665
3.146865671641791
3.1511764705882355
3.1518840579710146
3.1525714285714286
3.1554929577464788
3.156111111111111
3.1501369863013697
3.1497297297297298
3.152
3.1526315789473682
3.153246753246753
3.1523076923076925
3.150886075949367
3.1495
3.14962962962963
3.1502439024390245
3.1513253012048192
3.15
3.146352941176471
3.147906976744186
3.148045977011494
3.1486363636363635
3.146067415730337
3.1462222222222223
3.145054945054945
3.1491304347826086
3.149677419354839
3.1514893617021276
3.152
3.15125
3.1525773195876288
3.15265306122449
3.1515151515151514

我们可以看到10000个点我们最精确的值可以达到小数点后第四位那么是不是点的数量越多我们的值就越准确呢答案是否定的因为在点到一定数量后准确性将不会升高反而会有降低的风险下一节我们由于性能问题我们不用可视化看看结果https://blog.csdn.net/qq_40163148/article/details/95040917
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值