当前位置: 首页 > news >正文

「Java案例」求PI的值

案例解析

莱布尼茨级数法

π的近似值可以通过莱布尼茨级数公式计算:
π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - …
这个公式虽然简单,但收敛速度较慢,先用它来理解基本原理。

# 源文件保存为“CalculatePI.java”
public class CalculatePI {public static void main(String[] args) {int terms = 1000000; // 计算的项数double pi = 0.0;for (int i = 0; i < terms; i++) {double term = 1.0 / (2 * i + 1);if (i % 2 == 0) {pi += term;} else {pi -= term;}}pi *= 4; // 因为计算的是π/4,所以需要乘以4System.out.println("计算得到的π值: " + pi);System.out.println("Math.PI实际值: " + Math.PI);System.out.println("误差: " + (Math.PI - pi));}
}

运行结果

计算得到的π值: 3.1415916535897743
Math.PI实际值: 3.141592653589793
误差: 1.0000000187915248E-6

代码解析:

  • 设置计算项数为100万项,项数越多结果越精确
  • 使用for循环逐项计算
  • 根据项数的奇偶性决定加减
  • 循环结束后乘以4得到π的近似值
  • 最后输出计算结果并与Java内置的Math.PI比较

运行这个程序,可以发现虽然计算了100万项,但精度仍然只有小数点后5位左右,说明这个方法收敛确实很慢。

蒙特卡洛方法

现在尝试另一种有趣的方法——蒙特卡洛模拟。这个方法通过随机撒点来估算π值。

import java.util.Random;public class MonteCarloPI {public static void main(String[] args) {Random random = new Random();int totalPoints = 1000000; // 总点数int insideCircle = 0; // 落在圆内的点数for (int i = 0; i < totalPoints; i++) {double x = random.nextDouble(); // 0-1之间的随机x坐标double y = random.nextDouble(); // 0-1之间的随机y坐标// 判断点是否在单位圆内if (x * x + y * y <= 1) {insideCircle++;}}// 计算π值:面积比 = π/4double pi = 4.0 * insideCircle / totalPoints;System.out.println("蒙特卡洛法计算的π值: " + pi);}
}

运行结果

蒙特卡洛法计算的π值: 3.143272

代码解析:

  • 在边长为1的正方形内随机撒点
  • 计算落在半径为1的四分之一圆内的点数
  • 根据面积比(圆面积/正方形面积=π/4)估算π值
  • 点数越多,结果越精确

这个方法虽然直观有趣,但精度同样有限,适合理解概念而非高精度计算。

马青公式

为了提高计算效率,现在来看一个收敛更快的算法——马青公式。

# 源文件保存为“MachinPI.java”
import java.math.BigDecimal;
import java.math.RoundingMode;public class MachinPI {public static void main(String[] args) {int decimalPlaces = 15; // 小数位数BigDecimal pi = calculatePI(decimalPlaces);System.out.println("计算到小数点后" + decimalPlaces + "位的π值: " + pi);}private static BigDecimal calculatePI(int decimalPlaces) {BigDecimal sixteen = new BigDecimal(16);BigDecimal four = new BigDecimal(4);// 计算arctan(1/5)的级数展开BigDecimal arcTan1_5 = arctan(5, decimalPlaces);// 计算arctan(1/239)的级数展开BigDecimal arcTan1_239 = arctan(239, decimalPlaces);// 应用马青公式: π/4 = 4arctan(1/5) - arctan(1/239)BigDecimal pi = sixteen.multiply(arcTan1_5).subtract(four.multiply(arcTan1_239));return pi.setScale(decimalPlaces, RoundingMode.HALF_UP);}private static BigDecimal arctan(int denominator, int decimalPlaces) {BigDecimal result = BigDecimal.ZERO;BigDecimal term;int n = 0;// 设置计算精度int precision = decimalPlaces + 5;// 计算arctan(1/x)的泰勒级数展开BigDecimal x = new BigDecimal(denominator);BigDecimal xSquared = x.multiply(x);BigDecimal divisor;do {divisor = new BigDecimal(2 * n + 1);term = BigDecimal.ONE.divide(x.pow(2 * n + 1).multiply(divisor), precision, RoundingMode.HALF_UP);if (n % 2 == 0) {result = result.add(term);} else {result = result.subtract(term);}n++;} while (term.compareTo(new BigDecimal("1E-" + precision)) > 0);return result;}
}

运行结果

计算到小数点后15位的π值: 3.141592653589793

代码解析:

  • 使用BigDecimal保证高精度计算
  • 马青公式基于arctan函数的泰勒展开
  • 通过设置小数位数控制计算精度
  • 当新增项小于指定精度时停止计算
  • 采用高精度计算模式避免舍入误差

这个方法收敛速度快得多,计算15位小数只需很少的迭代次数。

实践练习题

改进莱布尼茨级数

观察莱布尼茨级数的收敛情况,修改程序使其在达到指定精度时自动停止计算。

参考代码:

# 源文件保存为“LeibnizImproved.java”
public class LeibnizImproved {public static void main(String[] args) {double precision = 1e-6; // 期望精度double pi = 0.0;int i = 0;double term;do {term = 1.0 / (2 * i + 1);if (i % 2 == 0) {pi += term;} else {pi -= term;}i++;} while (Math.abs(term) > precision);pi *= 4;System.out.println("计算到精度" + precision + "的π值: " + pi);System.out.println("实际迭代次数: " + i);}
}

运行结果

计算到精度1.0E-6的π值: 3.1415946535856922
实际迭代次数: 500001

比较不同算法的效率

编写程序比较上述三种方法在相同精度下的计算效率,统计各自的迭代次数和耗时。

参考代码:

# 源文件保存为“CompareMethods.java”
import java.util.Random;
import java.math.BigDecimal;
import java.math.RoundingMode;class LeibnizImproved {public static void leibniz(double targetPrecision) {double precision = 1e-6; // 期望精度double pi = 0.0;int i = 0;double term;do {term = 1.0 / (2 * i + 1);if (i % 2 == 0) {pi += term;} else {pi -= term;}i++;} while (Math.abs(term) > precision);pi *= 4;System.out.println("计算到精度" + precision + "的π值: " + pi);System.out.println("实际迭代次数: " + i);}
}class MonteCarloPI {public static void monteCarlo(int num) {Random random = new Random();int totalPoints = 1000000; // 总点数int insideCircle = 0; // 落在圆内的点数for (int i = 0; i < totalPoints; i++) {double x = random.nextDouble(); // 0-1之间的随机x坐标double y = random.nextDouble(); // 0-1之间的随机y坐标// 判断点是否在单位圆内if (x * x + y * y <= 1) {insideCircle++;}}// 计算π值:面积比 = π/4double pi = 4.0 * insideCircle / totalPoints;System.out.println("蒙特卡洛法计算的π值: " + pi);}
}class MachinPI {public static BigDecimal calculatePI(int decimalPlaces) {BigDecimal sixteen = new BigDecimal(16);BigDecimal four = new BigDecimal(4);// 计算arctan(1/5)的级数展开BigDecimal arcTan1_5 = arctan(5, decimalPlaces);// 计算arctan(1/239)的级数展开BigDecimal arcTan1_239 = arctan(239, decimalPlaces);// 应用马青公式: π/4 = 4arctan(1/5) - arctan(1/239)BigDecimal pi = sixteen.multiply(arcTan1_5).subtract(four.multiply(arcTan1_239));return pi.setScale(decimalPlaces, RoundingMode.HALF_UP);}private static BigDecimal arctan(int denominator, int decimalPlaces) {BigDecimal result = BigDecimal.ZERO;BigDecimal term;int n = 0;// 设置计算精度int precision = decimalPlaces + 5;// 计算arctan(1/x)的泰勒级数展开BigDecimal x = new BigDecimal(denominator);BigDecimal xSquared = x.multiply(x);BigDecimal divisor;do {divisor = new BigDecimal(2 * n + 1);term = BigDecimal.ONE.divide(x.pow(2 * n + 1).multiply(divisor),precision,RoundingMode.HALF_UP);if (n % 2 == 0) {result = result.add(term);} else {result = result.subtract(term);}n++;} while (term.compareTo(new BigDecimal("1E-" + precision)) > 0);return result;}
}public class CompareMethods {public static void main(String[] args) {double targetPrecision = 1e-6;// 测试莱布尼茨级数法long start = System.nanoTime();LeibnizImproved.leibniz(targetPrecision);long end = System.nanoTime();System.out.println("莱布尼茨法耗时: " + (end - start) / 1e6 + "ms");// 测试蒙特卡洛法start = System.nanoTime();MonteCarloPI.monteCarlo((int) 1e6);end = System.nanoTime();System.out.println("蒙特卡洛法耗时: " + (end - start) / 1e6 + "ms");// 测试马青公式start = System.nanoTime();MachinPI.calculatePI(6);end = System.nanoTime();System.out.println("马青公式耗时: " + (end - start) / 1e6 + "ms");}
}

运行结果

计算到精度1.0E-6的π值: 3.1415946535856922
实际迭代次数: 500001
莱布尼茨法耗时: 32.9133ms
蒙特卡洛法计算的π值: 3.14054
蒙特卡洛法耗时: 64.2519ms
马青公式耗时: 1.8079ms
http://www.lryc.cn/news/584586.html

相关文章:

  • 告别卡顿与慢响应!现代 Web 应用性能优化:从前端渲染到后端算法的全面提速指南
  • 快速搭建服务器,fetch请求从服务器获取数据
  • 搭建自动化工作流:探寻解放双手的有效方案(1)
  • RK3568项目(八)--linux驱动开发之基础外设(上)
  • Linux驱动开发(platform 设备驱动)
  • ARM单片机滴答定时器理解与应用(二)(详细解析)(完)
  • 多线程交替打印
  • 技术学习_检索增强生成(RAG)
  • 【个人笔记】负载均衡
  • 微服务项目远程调用时的负载均衡是如何实现的?
  • Prompt提示词的主要类型和核心原则
  • 【WEB】Polar靶场 Day8 详细笔记
  • Docker 镜像加速站汇总与使用指南
  • SpringBoot系列—MyBatis(xml使用)
  • Flink自定义函数
  • 一个编辑功能所引发的一场知识探索学习之旅(JavaScript、HTML)
  • Android 插件化实现原理详解
  • 虚拟储能与分布式光伏协同优化:新型电力系统的灵活性解决方案
  • Datawhale AI 夏令营:基于带货视频评论的用户洞察挑战赛 Notebook(下篇)
  • Chromium 引擎启用 Skia Graphite后性能飙升
  • 【TGRS 2025】新型:残差Haar离散小波变换下采样,即插即用!
  • 从零构建MVVM框架:深入解析前端数据绑定原理
  • 深入理解 Linux 中的 stat 函数与文件属性操作
  • NGINX系统基于PHP部署应用
  • 开发需要写单元测试吗?
  • Camera2API笔记
  • 记录一下openGauss自启动的设置
  • 《测试开发:从技术角度提升测试效率与质量》
  • io_helper说明
  • 使用Word/Excel管理需求的10个痛点及解决方案Perforce ALM