You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于球体积法实现1e-6精度π值估算的方案及代码咨询

基于球体积计算π(精度≥1e-6)实现方案

核心原理

基于三维单位球体积公式 $V=\frac{4}{3}\pi R^3$($R=1$ 时 $V=\frac{4}{3}\pi$),通过数值积分计算单位球体积后反推 $\pi=\frac{3V}{4}$,该方法收敛速度远高于蒙特卡洛算法,可轻松达到1e-6及更高精度。
我们仅计算第一卦限的单位球体积($x,y,z \in [0,1]$ 且满足 $x2+y2+z^2 \leq 1$),乘以8即可得到整个单位球的体积,无需处理负数逻辑。

推荐实现方案:复合辛普森积分法

复合辛普森积分收敛阶为 $O(h^4)$,仅需每维200个采样点即可将误差控制在1e-9级别,完全满足精度要求:

  • 内层积分:固定x值,计算 $f(y)=\sqrt{1-x2-y2}$ 从 $y=0$ 到 $y=\sqrt{1-x^2}$ 的积分
  • 外层积分:对x从0到1做辛普森积分,得到第一卦限体积
  • 最终计算:总球体积=第一卦限体积8,$\pi=\frac{3总球体积}{4}$

纯C++实现代码

#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include <iomanip>

// 计算固定x时y方向的辛普森积分
double simpson_y(double x, int sample_cnt) {
    double y_max = sqrt(1.0 - x * x);
    double step = y_max / sample_cnt;
    double sum = sqrt(1.0 - x * x); // y=0时的函数值,y=y_max时函数值为0无需加
    for (int i = 1; i < sample_cnt; ++i) {
        double y = i * step;
        double f_val = sqrt(1.0 - x * x - y * y);
        sum += (i % 2 == 1) ? 4 * f_val : 2 * f_val;
    }
    return sum * step / 3.0;
}

double calc_pi(int sample_cnt = 200) {
    double step = 1.0 / sample_cnt;
    double sum = simpson_y(0.0, sample_cnt) + simpson_y(1.0, sample_cnt);
    for (int i = 1; i < sample_cnt; ++i) {
        double x = i * step;
        double inner_val = simpson_y(x, sample_cnt);
        sum += (i % 2 == 1) ? 4 * inner_val : 2 * inner_val;
    }
    double total_volume = 8 * sum * step / 3.0;
    return 3 * total_volume / 4.0;
}

int main() {
    double pi = calc_pi(200);
    std::cout << std::fixed << std::setprecision(10) << "计算得到的π值:" << pi << std::endl;
    std::cout << "与标准π的误差:" << std::abs(pi - M_PI) << std::endl;
    return 0;
}

CUDA并行实现代码

当采样点数量较大时,可通过CUDA并行计算每个x对应的内层积分,大幅提升计算速度:

#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include <iomanip>
#include <cuda_runtime.h>

__device__ double dev_sqrt(double x) {
    return __dsqrt_rn(x);
}

// 每个线程计算一个x对应的内层y方向积分
__global__ void calc_inner_integral(double* d_out, int sample_cnt) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= sample_cnt) return;
    double x = double(idx) / sample_cnt;
    double y_max = dev_sqrt(1.0 - x * x);
    double step = y_max / sample_cnt;
    double sum = dev_sqrt(1.0 - x * x);
    for (int j = 1; j < sample_cnt; ++j) {
        double y = j * step;
        double f_val = dev_sqrt(1.0 - x * x - y * y);
        sum += (j % 2 == 1) ? 4 * f_val : 2 * f_val;
    }
    d_out[idx] = sum * step / 3.0;
}

int main() {
    const int sample_cnt = 2000;
    double* h_vals = new double[sample_cnt];
    double* d_vals;
    cudaMalloc(&d_vals, sample_cnt * sizeof(double));

    dim3 block_size(256);
    dim3 grid_size((sample_cnt + block_size.x - 1) / block_size.x);
    calc_inner_integral<<<grid_size, block_size>>>(d_vals, sample_cnt);
    cudaMemcpy(h_vals, d_vals, sample_cnt * sizeof(double), cudaMemcpyDeviceToHost);

    // 外层辛普森积分在CPU端计算
    double step = 1.0 / sample_cnt;
    double sum = h_vals[0] + h_vals[sample_cnt - 1];
    for (int i = 1; i < sample_cnt - 1; ++i) {
        sum += (i % 2 == 1) ? 4 * h_vals[i] : 2 * h_vals[i];
    }
    double total_volume = 8 * sum * step / 3.0;
    double pi = 3 * total_volume / 4.0;

    std::cout << std::fixed << std::setprecision(10) << "CUDA计算得到的π值:" << pi << std::endl;
    std::cout << "与标准π的误差:" << std::abs(pi - M_PI) << std::endl;

    delete[] h_vals;
    cudaFree(d_vals);
    return 0;
}

可选低复杂度方案

若不需要太高计算效率,也可采用均匀网格采样法:

  • 将x/y/z三个轴各拆分为1000个均匀采样点,总采样量为1e9
  • 统计满足 $x2+y2+z^2 \leq 1$ 的点数量count
  • 总球体积 $V=8*count/(1000^3)$,计算得到的π误差约为5e-7,刚好满足1e-6的精度要求

内容的提问来源于stack exchange,提问作者Asan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 19:06:04