一、理論篇
1. 數學公式
圓面積公式:π*r*r,其中π為圓周率,r為圓半徑;
正方形面積公式:s*s,其中s為邊長;
勾股定理:a*a + b*b = c*c,其中a/b分別為直角三角形的兩個直角邊,c為斜邊。
2. 計算方法
考慮下圖,邊長為r的正方形內嵌了一個以r為半徑的1/4圓。
InsideCircle面積 = 以r為半徑的圓面積 / 4 = π*r*r/4
正方形面積 = r*r
所以, InsideCircle面積 / 正方形面積 = (π*r*r/4) / (r*r) = π/4
面由線組成,線由點組成。因此,如果有若干點均勻落入到正方形中,那麼落入InsideCircle的點的個數占總數的比率也將會是π/4,由此將會統計出π的值。
二、實戰篇
1. 散彈槍計算
具體方式為:制作一個如上圖一樣正方形木板,用散彈槍對著它一頓亂掃,最後統計彈孔個數和落點,從而得出π的值。
事實上,真的有研究人員做過這個腦洞大過黑洞的實驗,他們在 30857 個樣本中得到了 3.13 這個還算不錯的結果。
詳見:http://www.linuxidc.com/Linux/2015-01/111775.htm
2. Java隨機數計算
作為一個碼農,當然玩不起散彈槍這種高級玩具,那麼接下來就以代碼來玩一把。
具體思路是這樣的:r直接取值為1.0,還需要定義一個落在正方形中的所有點的個數PointNumber,每一個點都有一個坐標(x,y),x,y取值為0.0-1.0,利用Java隨機數生成每個點,然後用勾股定理判斷該點是落在圓內還是圓外,並統計落在圓內的點的個數InsideCircleNumber,那麼π=InsideCircleNumber/PointNumber*4。當然,如果只計算一次的話,可能誤差會較大,可以再增加一個計算次數CalcTimes,然後求平均值。
按照這樣的思路的計算結果如下:
PointNumber
CalcTimes
π
最接近π的值
10000
10000
3.141617279999959
3.1416
100000
10000
3.1415569599999684
3.1416
1000000
10000
3.1415845499999953
3.141592
10000000
10000
3.1415924761886806
3.1415928
附源代碼:
package com.test.pai;
import org.apache.commons.lang.math.RandomUtils;
public class CalcPai
{
public static boolean inCircle(double x, double y)
{
return (y <= Math.sqrt(1 - x * x));
}
public static double CalcPaiByPointNumber(long num)
{
double inCircleNum = 0.0;
for (long i = 0; i < num; i++)
{
if (CalcPai.inCircle(RandomUtils.nextDouble(), RandomUtils.nextDouble()))
{
inCircleNum++;
}
}
double pai = inCircleNum * 4 / num;
return pai;
}
public static void main(String[] args)
{
double realPai = 3.14159265;
CurrResult currResult = new CurrResult(0.0, realPai, 0.0);
long times = 10000;
long num = 1000000;
for (long i = 1; i <= times; i++)
{
double pai = CalcPai.CalcPaiByPointNumber(num);
currResult.setTotalPai(currResult.getTotalPai() + pai);
double diff = Math.abs(realPai - pai);
if (diff < currResult.getDifference())
{
currResult.setCurrPai(pai);
currResult.setDifference(diff);
}
System.out.println("No." + i + "/" + times + "\t" + pai + "\t" + currResult.getCurrPai() + "\t"
+ currResult.getTotalPai() / i);
}
}
}