蒙特卡洛算法并不是一种算法的名称,而是是一类随机方法的统称。这类方法的特点是在随机采样上计算得到近似结果。随着采样的增多,得到的结果是正确结果的概率逐渐加大,但在获得真正的结果之前(放弃随机采样,而采用类似全采样这样的确定性方法),无法知道目前得到的结果是不是正确的结果
从特性来说,既然是随机算法,在采样不全时,通常不能保证找到最优解,只能尽量找。因此可将随机算法分成以下两类:
(1)蒙特卡洛算法:采样越多,越近似最优解(尽量找好的,但不保证是最好的)
对一百个数进行比较,首先随机取一个数A,再随机取一个数B与A进行比较,留下较大值,抛弃掉较小值,然后再随机取一个数C与较大值作比较,如此循环,保证留下的值都比舍弃掉的值大,比较的次数越多,值就越大。但在一百个数全部取出前,无法确认目前留下的值是否是最大值
(2)拉斯维加斯算法:采样越多,越有机会找到最优解(尽量找最好的,但不保证能找到)
假设有一把锁和一百把钥匙,其中只有一把钥匙能开锁,每次随机拿一把钥匙开锁,失败则舍弃,如此循环,尝试的次数越多,则打开(找到最优解)的机会越大。当然,也有可能这一百把钥匙都打不开锁(无法找到最优解)
因为随机算法的特性,所以算法本身可能复杂,也可能简单
下面就编写一个Java程序来实现蒙特卡洛法求π的近似值:
这个方法的理论基础是概率论中的伯努利大数定律。
在一个边长为1的正方形中有一个内切圆,根据圆的面积公式S = πr2,可得该圆的面积为π/4。假设有大量的随机点等概率均匀地落入此正方形中,若正方形的面积为S,内切圆的面积为S',落入正方形中的点数为N',落入内切圆的的点数为N,这时便有 S'/S = N'/N,所以可据此求得 π = 4N'/N
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class MonteCarloPi {
public static void main(String[] args) throws Exception {
double radius = 1.0;
int pointNumber = 100000000
Circular c = new Circular(radius);
for(int i = 1; i <= numberOfDarts; i++) {
Point p = Point.getRandom(radius);
c.strike(p);
}
double fractionIn = c.getFractionIn();
double pi = 4.0 * fractionIn;
System.out.println("Pi is approximately " + pi);
}
}
class Circular {
private double radius;
private int insideCircle, outsideCircle;
public Circular(double radius) {
this.radius = radius;
insideCircle = 0;
outsideCircle = 0;
}
public void strike(Point point) {
double x = point.getX();
double y = point.getY();
if (Math.sqrt(x*x + y*y) < radius)
insideCircle++;
else
outsideCircle++;
}
public double getFractionIn() {
double total = (double) (insideCircle + outsideCircle);
return (double) insideCircle / total;
}
}
class Point {
private double x,y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public static Point getRandom(double radius) {
double x, y;
double size = Math.random();
double sign = Math.random();
size = size * radius;
if(sign > 0.5)
x = size;
else
x = -size;
size = Math.random();
sign = Math.random();
size = size * radius;
if (sign > 0.5)
y = size;
else
y = -size;
return new Point(x, y);
}
}
执行上述代码可得:Pi is approximately 3.14170216
由此可见,所得到的结果近似于圆周率π