很久以前,在我刚刚开始学习编程的时候,突然想到了…
那时才刚刚开始学习编程,不知道改写些什么。在学习了非常基础的内容之后,突然想到可以写一个程序来算出圆周率。

准备工作/思路

怎么做的的呢?其实相当简单。
我们只需要用到编程中最基础的东西,四则运算、Math.sqrt()函数、随机器、还有循环和判断。

假想有一个正方形吧。
现在我们需要计算出这个正方形的内切圆的面积 (注意,不能使用 S = pai * (r ^ 2) 来计算,因为此时pai是需要计算的数。)
pai 可以这样计算

pai = S圆 / (r ^ 2)

即只要求出圆的面积就可以求出pai。

不使用pai求出圆的面积,可以使用蒙特·卡罗方法(Monte Carlo method)来进行模拟计算,实际上这是高中时期的教学内容。
在正方形的范围内随机出大量坐标,有一些坐标会在圆内。
而蒙特·卡罗方法告诉我们

圆的面积 / 正方形的面积 ≈ 落在圆内的坐标数量 / 落在正方形内的坐标数量

假如这个正方形的边长为2(也就是圆的半径为1,方便计算),我们可以精确的算出此正方形的面积(也就是4)。
所以此时等式变为 圆的面积 / 4 ≈ 落在圆内的坐标数量 / 落在正方形内的坐标数量
即 圆的面积 ≈ (4 * 落在圆内的坐标数量) / 落在正方形内的坐标数量

将这个结论代入最上面那个方程中可以得到
pai ≈ ((4 * 落在圆内的坐标数量) / 落在正方形内的坐标数量) / (r ^ 2)
又因为 r = 1
所以我们的终极结论就是

pai ≈ (4 * 落在圆内的坐标数量) / 落在正方形内的坐标数量

显然,模拟的次数越多,得到的结果就越精确。


动手吧

根据以上的推导,不难写出程序。
以下是核心算法的源码(刚刚学编程的时候写的,好幼稚啊哈哈哈哈哈哈)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
System.out.print("请输入模拟次数:");
Scanner input = new Scanner(System.in);
try {
int JD = input.nextInt();
int c = 0; /* 第 c+1 次模拟 */
int m = 0; /* 满足条件(在圆内)的次数 */
while (JD > c) {
c++;
/* 随机出两个小数,范围[-1, 1],代表坐标(a, b) */
double a = (double) (Math.random() * 2);
a--;
double b = (double) (Math.random() * 2);
b--;
/* 平方和开根号 */
double p1 = (a * a) + (b * b);
double p2 = Math.sqrt(p1);
/* 小于等于1即在圆内 */
if (p2 <= 1) {
m++;
}
}
double m2 = 4 * m;
double pai = m2 / JD;
System.out.println("模拟" + JD + "次后,圆周率约为:" + pai);
} catch (Exception e) {
e.printStackTrace();
}

这种方法的缺点还是有很多,运行缓慢,而且还不够精确。


番外/累加法求圆周率

求圆周率的方法还有很多,随便举个例子

pai ≈ (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - …

不难写出

1
2
3
4
5
6
7
8
9
10
11
12
Scanner in = new Scanner(System.in);
System.out.println("请输入累加次数:");
int addmany = in.nextInt();
double pai = 0;
for (int i = 1; i <= addmany; i++) {
if (i % 2 == 0) {
pai -= 4.0 / (2 * i - 1);
} else {
pai += 4.0 / (2 * i - 1);
}
}
System.out.println("经过" + addmany + "次累加\n圆周率约为:" + pai);

这种方法计算速度很快,而且结果比较精确,但是趣味性却完全不如模拟法。


END…