1:对"pi"的处理
1:int pi = 3.1415926
2 : const double pi = 4.0 * atan(1.0)
\ tan45度=1
所以 45度换成弧度就是 pi/4
所以反三角函数就是就atan(1.0) 再乘以4就是 pi\
求π的近似值,一般有两个公式,如下所示:
公式①:
![](http://see.xidian.edu.cn/cpp/uploads/allimg/141102/1-1411021Q249C2.gif)
题目1) 利用公式①计求π的近似值,要求累加到最后一项小于10^(-6)为止。
#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);
}
运行结果:
pi的值为:3.141594
上面的代码,先计算π/4的值,然后再乘以4,s=-s; 用的很巧妙,每次循环,取反,结果就是,这次是正号,下次就是负号,以此类推。
②
#include <stdio.h>
#include <math.h>
int main(){
int r;
double PI,s;
scanf("%d",&r);
PI= atan(1.0)*4;
s = PI*r*r;
printf("%.7f",s);
return 0;
}
这里的atan()是反正切函数,正切函数tan(1)=π/4,所以pi=4.0*atan(1.0)=3.14159......
③
“外星人计算圆周率的程序”原理及性能分析
#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);
![](https://i-blog.csdnimg.cn/blog_migrate/187f4a89fbe30dda11fb89f1db7c3517.png)
-
-
long a= 10000,b,c= 2800,d,e,f[ 2801],g;
-
main()
-
{
-
for( int i= 0;i<c;i++) //将数组中的变量都赋值为2000
-
{
-
f[i]=a/ 5;
-
}
-
while(c)
-
{
-
b=c; //做分子
-
d= 0;
-
g= 2*c; //做分母
-
while(b)
-
{
-
d=d+f[b]*a;
-
g--;
-
f[b]=d%g; //保留余数
-
d=d/g;
-
g--;
-
b--;
-
if(b)
-
d*=b;
-
}
-
c-= 14;
-
printf( "%.4d",e+d/a);
-
e=d%a;
-
}
-
}
![](https://i-blog.csdnimg.cn/blog_migrate/41ecf960642252e19187fd837cce0983.png)
可以看到,在程序开始运行的时候,b被赋值为2800,g被赋值为2*2800=5600,所以在g第一次参与运算时,g的值为5600-1=2*2799+1,b第一次参与运算时的值为2799,所以第一次循环恰如上面公式中最小的括号所示,是2799/(2*2799+1),当进行下一次循环时,就用之前得出来的值加上2000再乘上2798/(2*2798+1)。即while(b)中的每一循环都是之前的结果加上2000再向外拓展一层括号。
由于只需要精确到前800位,所以2800次迭代就可以满足所需要的精确度。
当然,在C语言中是不存在能够精确到小数点后800位的数据类型,所以在计算的时候需要用高精度计算的方法。while(c)中的每次循环都将数组中剩余的数据(即之前除以分母时的余数)前移四位,这样可以得到接下来的四位小数。当然,在运算的时候可能会出现进位的现象,即d/a是大于0的情况,所以要将这个部分加到之前保留的四位数据中,即每次输出的是e+d/a.
在输出那里%.4d的意思是输出4位整数,即若整数少于四位时,从高位开始将缺少的位补全为0。这个的作用在那篇帖子和很多博文中解释的都是输出的整数不止4位的时候取前四位输出,并把后四位保留到e中。这是错误的。%.4d没有截取最高四位的功能,如果整数的位数大于等于4,输出的会是整数本身,而当整数不足4位时它才会起作用。这里之所以要用%.4d,是因为e是d对a的取余,但这个余数可能小于1000,如果直接输出%d就会导致小数中一些0的缺失。 所以要用0进行占位,即%.4d是每次都输出4位整数的保障。
至于c的作用,帖子里面和其他的博文基本上都忽略掉了。在这里c的步长为14,即每次运算都舍弃掉最后的14项,这样可以保证整个运算过程的精确。若c的步长过小,则会因为出现冗余导致误差;若c的步长过大,则会因为忽略掉不该忽略的项而导致误差。在原帖中说这个对精确度没有影响,这也是不对的。不是对精确度没有影响,而是它的影响不足以影响到前800位小数。如果步长为1,最多只能精确到九百多位;步长为14的时候,则可以保证前16275位小数的准确程度,这也是仅通过c的改变能达到的最高的精确度。一般来讲,步长过大比步长过小的精确度会高一些。具体会在后面的性能分析中给出详细的分析。
程序实现的过程大体上就是这样,下面说一下帖子和博文普遍回避的两个问题:
1.那个计算圆周率的公式是怎样得来的。
2.这个程序的性能如何?究竟能达到多高的精确度。
一、公式推导
先把这个公式做一下变形:
可以发现右边变成了级数的形式,由于本人数学水平有限,想不到特别酷炫的方法。只能用两种比较常规的方法去解决它
1.和函数法
我做级数求和的题目时,一般会习惯性地想到和函数。这个我一开始想着直接乘上一个x的幂,结果怎么也凑不出来。联想到arcsinx的麦克劳林展开后x的次幂都是奇数,所以我把次幂改成n+1/2试了一下,于是就有了下面的步骤:
2.欧拉级数变换
在求完和函数之后,我又想着利用欧拉级数变换解决这个问题。
这个需要利用Leibniz公式:
这个公式的推导很容易,对arctanx泰勒展开然后令x=1就可以了
![](https://i-blog.csdnimg.cn/blog_migrate/37ce1a18c4fa82c22beab496a7755e3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a50839124b6587ca5fa63f7211ebccab.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1f7edb5469f51e8bf4f93833637e3e26.png)