2014_01_03_円周率の計算
円周率を1000桁計算するプログラムです。
簡単にアルゴリズムを紹介すると、円周率を求めるのにint,long,doubleなどの型ではすぐにオーバーフローしてしまいます。
そこで、多倍長演算というアルゴリズムを使用します。
多倍長演算は配列の中に任意の数をいれて、オーバーフローせずに計算させるというものです。
今回は1要素に4桁の数を入れています。これを250要素用意すれば、1000桁表示できるということです。
以下、ソースコードです。
ソースコード(クリックで展開します。)
#include/* ** 関数一覧 CLEAR3 ADD SUBTRACT PRINT DIVIDE */ void CLEAR3(long PI[], long A[], long B[]) { /* * 配列をクリアする。 */ for(int i=0;i<=250;i++) { PI[i] = NULL; A[i] = NULL; B[i] = NULL; } }; void ADD(long sum[], long A[], long B[]) { /* * 上位の桁の繰り上げ。 * 足し算 */ int carry = 0; for(int i = 250; i>=0;i--) { sum[i] = A[i] + B[i] + carry; if(sum[i] >= 10000) { carry = sum[i] / 10000; sum[i] = sum[i] - (10000 * carry); } else { carry=0; } } }; void SUBTRACT(long A[],long B[]) { /** * 上位の桁から借りてくる * 引き算 */ long borrow = 0; for(int i=250;i>=0;i--) { if(A[i] < B[i]) { A[i]=A[i]+10000; A[i]=A[i] - (B[i]+borrow); borrow = 1; } else { A[i]=A[i] - (B[i]+borrow); borrow=0; } } }; void DIVIDE2(long A[],long B[],long C){ //A保存配列 //B割られる配列 //c割る数 int R=0;//あまり for(int i=0;i<=250;i++) { R = B[i]%C;//余りを計算 A[i]=B[i]/C;//商を代入 if(i==250) break; B[i+1]=B[i+1]+(R*10000);//Rに10^4をかける。(次の割られる数を計算。) } } ; void DIVIDE3(long A[],long B,long C){ //A保存配列 //B割られる配列 //c割る数 int R=0;//あまり for(int i=0;i<=250;i++) { R = B%C;//余りを計算 A[i]=B/C;//商を代入 B=R*10000;//Rに10^4をかける。(次の割られる数を計算。) } }; void MULTIPLE(long A[],int B) { int carry = 0; for(int i = 250; i>=0;i--) { A[i] = A[i] * B + carry; if(A[i] >= 10000) { carry = A[i] / 10000; A[i] = A[i] - (10000 * carry); } else { carry=0; } } }; void COPY(long main[],long copy[]) { for(int i=0;i<=250;i++) { copy[i]=main[i]; } }; //完成 void PRINT(long pi[]) { for(int i=0;i<=250;i++) { if(i==0) { printf("%d.",pi[i]); } else { if(pi[i]>=1000) { printf("%d",pi[i]); } else if(pi[i]<1000 && pi[i]>=100) { printf("0%d",pi[i]); } else if(pi[i] < 100 && pi[i] >=10) { printf("00%d",pi[i]); } else if(pi[i] < 10 && pi[i] >=1) { printf("000%d",pi[i]); } else { printf("000%d",pi[i]); } } if(i% 5==0) { printf(" "); } if(i%15==0) { printf("\n"); } } }; int main() { //変数宣言 long PI[251];//部分和piの値 long A[251]; //M1項 long B[251]; //M2項 long pn[251]; //pnを入れる箱 int TERM1=714;//M1に必要な項数 int TERM2=210;//M2に必要な項数 long arc5Pn=5;//pn long arc239Pn=239;//pn long cn=1;//Cn long arctan5 = 5;//arctan long arctan239 = 239;//arctan //初項を求める //クリアをする CLEAR3(PI,A,B); CLEAR3(PI,pn,A); //pn初項 DIVIDE3(A,arc5Pn,(arctan5*arctan5)); //pnを配列pnに保存する COPY(A,pn); //A(p0)に16をかける MULTIPLE(A,16); //pn/cnをする DIVIDE2(A,A,cn); //部分和を保存 ADD(PI,A,B); //Aにn+1項の高精度計算が入っている //M1項の計算 for(int i=1;i<=TERM1;i++) { //Aをクリアする。 CLEAR3(A,A,A); //pn配列が入っているのでAにn項目の結果をいれる。 DIVIDE2(A,pn,(arctan5*arctan5));//Aにpn+1として保存 //Aのものをpnにコピー(pn+1)として保存する COPY(A,pn); //16をかける MULTIPLE(A,16); //cn+1を求める。 cn=cn+2; DIVIDE2(A,A,cn);// (pn+1/cn+1)をする //Aにn+1項の高精度計算が入っている if(i%2==0) { ADD(PI,PI,A);//部分和をPIに保存 } else { SUBTRACT(PI,A);//部分差をPIに保存 } } //クリア CLEAR3(A,B,A); //cnを1にする cn=1; //M2項を求める //初項を求める //pn初項をBに保存 DIVIDE3(B,arc239Pn,(arctan239*arctan239)); //コピーする COPY(B,pn); //4をかける MULTIPLE(B,4); //pn/cnをする DIVIDE2(B,B,cn); //部分和を保存 SUBTRACT(PI,B); //M2項の計算 for(int i=1;i<=TERM2;i++) { //Bをクリアする。 CLEAR3(B,B,B); //pn配列が入っているのでBにn項目の結果をいれる。 DIVIDE2(B,pn,(arctan239*arctan239));//Bにpn+1として保存 //Bのものをpnにコピー(pn+1)として保存する COPY(B,pn); //4をかける MULTIPLE(B,4); //cn+1を求める。 cn=cn+2; DIVIDE2(B,B,cn);//(pn+1/cn+1)をする if(i%2==0) { SUBTRACT(PI,B);//部分差をPIに保存 } else { ADD(PI,PI,B);//部分和をPIに保存 } } PRINT(PI); getchar(); }