某次碰到pi,想用编程打印出它的比较多的有效位(至少比背的要多)。
开始考虑到 pi/4 = arctan(1)
arctan(x)展成多项式 arctan(x) = (1/1!)x - (1/3)(x^3) + (1/5)(x^5) - ....
所以有 pi/4 = 1 - 1/3 + 1/5 - 1/7 + .....
但是上式后面的式子收敛太慢了,编程很难求到很多的有效位,
而后查到Machin公式 pi/4 = 4arctan(1/5) - arctan(1/239),这个公式可以自己证明一下(忘的差不多了,涂了半天才算对)
这样泰勒展开式收敛很快,我就是照着这个公式编程的,代码如下
#include <stdio.h> #include <string.h> #include <malloc.h> #define KS_MIN(a, b) ((a)<(b)?(a):(b)) #define KS_MAX(a, b) ((a)>(b)?(a):(b)) #define BITS 20000 typedef struct BigInt { int L; int *d; BigInt(){ L = 1; d = (int*)malloc((BITS + 16)*sizeof(int)); d[0] = 0; } ~BigInt(){ free(d); } }BigInt; // out = in / x; void division(BigInt &out, BigInt &in, int x) { out.L = in.L; memset(out.d, 0, out.L*sizeof(int)); int i, s = 0; for (i = in.L-1; i >= 0; i--) { s = s*10 + in.d[i]; out.d[i] = s/x; s %= x; } for (i = out.L-1; i >= 0; i--) { if (out.d[i]) break; } out.L = i+1; if (out.L == 0) out.L++; } // a += b; void addequal(BigInt &a, BigInt &b) { int len = KS_MAX(a.L, b.L)+1; memset(a.d+a.L, 0, (len-a.L)*sizeof(int)); a.L = len; int i; for (i = 0; i < b.L; i++) a.d[i] += b.d[i]; for (i = 0; i < a.L-1; i++) { if (a.d[i] > 9){ a.d[i+1]++; a.d[i] -= 10; } } for (i = a.L-1; i >= 0; i--) { if (a.d[i]) break; } a.L = i+1; if (a.L == 0) a.L++; } // a -= b; void subequal(BigInt &a, BigInt &b) { int i; for (i = 0; i < b.L; i++) a.d[i] -= b.d[i]; for (i = 0; i < a.L-1; i++) { if (a.d[i] < 0){ a.d[i+1]--; a.d[i] += 10; } } for (i = a.L-1; i >= 0; i--) { if (a.d[i]) break; } a.L = i+1; if (a.L == 0) a.L++; } // a *= k void mulequal(BigInt &a, int k) { int i; for (i = 0; i < a.L; i++) a.d[i] *= k; memset(a.d+a.L, 0, 12*sizeof(int)); a.L += 12; for (i = 0; i < a.L-1; i++) { if (a.d[i] > 9){ a.d[i+1] += a.d[i]/10; a.d[i] %= 10; } } for (i = a.L-1; i >= 0; i--) { if (a.d[i]) break; } a.L = i+1; if (a.L == 0) a.L++; } void BigIntCopy(BigInt &dst, BigInt &src) { dst.L = src.L; memcpy(dst.d, src.d, dst.L*sizeof(int)); } void argtanx(BigInt &out, int x, int digit) { BigInt a, b; a.L = digit+1; memset(a.d, 0, (a.L-1)*sizeof(int)); a.d[a.L-1] = 1; division(b, a, x); BigIntCopy(a, b); BigIntCopy(out, b); int i = 3, xx = x*x, p = -1; while (1) { division(b, a, xx); BigIntCopy(a, b); division(b, a, i); if (b.L == 1 && b.d[0] == 0) break; if (p < 0) subequal(out, b); else addequal(out, b); p *= -1; i += 2; } } void print(BigInt &a) { printf("%d.", a.d[a.L-1]); int i; int low = a.L > 200 ? a.L-200 : 0; // 不打算全部打印出来 for (i = a.L-2; i >= low; i--) printf("%d", a.d[i]); printf("\n"); } int main(int argc, char *argv[]) { BigInt a, b; argtanx(a, 5, BITS); argtanx(b, 239, BITS); mulequal(a, 16); mulequal(b, 4); subequal(a, b); print(a); return 0; } ///* pi/4 = 4*arctan(1/5) - arctan(1/239) */
设要算的有效位数为N,那么上面时间复杂度大概O(N^2),所有10000位在几秒钟内应该能算出来
然后某次网上看到别人写的代码(稍微修改过)
#include <stdlib.h> #include <stdio.h> #define BITS 2000 int a=10000,b,c=BITS*7/2,d,e,f[BITS*7/2+1],g; int 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); //getchar(); return 0; }
太简短了!!!然后结果和我上面的对照过,是一致。
从代码上看时间复杂度也是O(N^2),真实测时速度比我上面的代码要快一些。
还好时间复杂度一致,不至于让我过分自悲,然后看了半天也不明白它这个是用什么原理设计的。
而后又查了一些pi的公式
http://zhan.renren.com/52faming?gid=3602888498032257169&checked=true
拉马努金公式
丘德诺夫斯基简化版公式
等等,据说上面很多公式(如丘德诺夫斯基)计算pi很快,我其实不太理解怎么处理那个根号。。。
pi的有效位已经到 万亿位级别
还有一个bbp的关于pi的.....
好吧,我只能说我真的是过分渺小了。。。。。。