程序计算精确圆周率π的方法

圆周率π=3.14159265358……无穷多位[事实上是超越数], 自古以来,人们就对π的精确计算充满兴趣。从最早的几何方法到现代的计算机算法,计算π的精度逐步提高。本文将介绍快速计算圆周率的算法以及它们在计算机上的应用。

首先给一种王炸级别的获取方法:python+mpmath库 【pip install mpmath】

from mpmath import mp
mp.dps = 1000 
print( mp.pi)   ## 输出1000位圆周率

Python 运行效果
这里提前为大家轻松列举1000位圆周率,其实mpmath内部的算法也是用c语言实现的。

下面我们选一种关于π比较简单, 并且收敛非常快的级数。
π /2= 1 + 1/3 + (1/3)(2/5) + (1/3)(2/5)(3/7) +(1/3)(2/5)(3/7)(4/9) + …
调整下:
π = 2 + 2/3 + (2/3)(2/5)+(2/3)(2/5)
(3/7)+(2/3)(2/5)(3/7)(4/9) + …

void main()
{
	double x = 2, z = 2;
	int a = 1, b = 3;
	while (z > 0)
	{   //when b->max, z->0
		z = z * a / b;
		x += z;
		a++;
		b += 2;
	}
	printf("π=%.14f\n", x);
}

执行效果: π=3.1415926535898 ;这个程序如此简单它可以计算到小数点后14位,因受限于double的精度。【感兴趣的可以使用GMP大数库,MPFR高精度库 再做尝试】

把上面的程序改造下, 将double类型的x,z变成数组保存每位小数,让它精确到小数点后面 1000 位再测试试试!!


void main()
{
	const int ARRSIZE = 1010;
	const int DISPCNT = 1000;	 // 定义数组大小,显示位数
	char x[ARRSIZE], z[ARRSIZE]; // x[0] x[1] . x[2] x[3] x[4] .... x[ARRSIZE-1]
	int a = 1, b = 3, c, d, i;
	int Run = 1, Cnt = 0;
	memset(x, 0, ARRSIZE);
	memset(z, 0, ARRSIZE);
	x[1] = 2;
	z[1] = 2;

	while (Run && (++Cnt < 100000))
	{
		d = 0;
		i = ARRSIZE - 1;
		do
		{
			c = z[i] * a + d;
			z[i] = c % 10;
			d = c / 10;
		} while (i--);

		d = 0;
		i = 0;
		do
		{
			c = z[i] + d * 10;
			z[i] = c / b;
			d = c % b;
		} while (i++ < ARRSIZE);

		Run = 0;
		for (i = ARRSIZE - 1; i > 0; i--)
		{
			c = x[i] + z[i];
			x[i] = c % 10;
			x[i - 1] += c / 10;
			Run |= z[i];
		}
		a++;
		b += 2;
	}
	printf("calc %d times\r\n", Cnt);
	printf("Pai=%d.\r\n", x[1]);
	for (i = 0; i < DISPCNT; i++)
	{
		if (i && ((i % 100) == 0))
			printf("\r\n");
		printf("%d", (int)x[i + 2]);
	}
}

以下是运行的效果,可以看到经过3343轮计算后前面的999位数字都是对的!
在这里插入图片描述
这下心里就有底了, 是不是改变数组大小就可以计算更多位数呢?答案是肯定的。
如果把定义数组大小和显示位数改为:
const int ARRSIZE = 10100;
const int DISPCNT = 10000;
执行的结果表明计算33538轮后圆周率精度就可以达到惊人的10000位!
初步结论,这个算法可以通过计算大概3.3轮就能输出一位正确的数值。

趣味扩展
基于以上算法,其实我们就可以对π的一些属性展开研究,如统计π每个位数数字出现的概率,等等

算法提高精度的原理:
以上程序的原理是利用数组把计算结果保存起来, 其中数组每一项保存10进制数的一位,
小数点定位在数组第1个数和第2个数之间, 即小数点前面2位整数, 其余都是小数位。
利用电脑模拟四则运算的笔算方法来实现高精度的数据计算,没想到最原始的方法竟然是精度最高的。

利用这个原理我们就可以扩展到其他常量的计算如自然底数e=2.71828…任意位数

#include <stdio.h>
#include <string.h>

// e = 1+1/1!+1/2!+1/3!+…+1/n! = 1+1/2*(1+1/3*(1+…*(1+1/n))…)));
int main()
{
#define TIMES 100 /// 输出e有效位数
	int n, e, d, i, f[TIMES + 1];
	i = TIMES;
	while (i--)
		f[i] = 10;
	printf("e=2.");
	i = 0;
	e = 0;
	while (i++ < TIMES)
	{
		d = 0;
		for (n = TIMES; n > 0; n--)
		{
			d = d + f[n] * 10;
			f[n] = d % n; // 余数
			d = d / (n);  // 整数
		}
		printf("%d", (e + d / 10) % 10);
		e = d % 10;
	}
	sleep(1);
	printf("\n\n");
	return 0;
}

用python+mpmath打印下答案对比

import mpmath
mpmath.mp.dps = 1000
# 计算自然对数的底数e的值,1000位
print(mpmath.e)

作为文章结束,给出一个某年Obfuscated C Contest佳作选录: 800位精度圆周率

#include <stdio.h>
long a=10000, b, c=2800, d, e, f[2801], g; 
main(){
for(;b-c;)f[b++]=a/5; 
for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a) 
for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);
} 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值