• 検索結果がありません。

π ( 円周率 ) を小数以下 10,000 桁まで計算 しなさい。

N/A
N/A
Protected

Academic year: 2021

シェア "π ( 円周率 ) を小数以下 10,000 桁まで計算 しなさい。"

Copied!
7
0
0

読み込み中.... (全文を見る)

全文

(1)

プログラミング言語 I 第 7 回 πの計算 2

埼玉大学工学部 電気電子システム工学科 伊藤 和人

Copyright ©2008 Kazuhito Ito

課題

„

π ( 円周率 ) を小数以下 10,000 桁まで計算 しなさい。

„

マーチンの公式を利用

„

計算の高速化

„無駄な計算を省く

„プログラムを見直し

⎟ ⎠

⎜ ⎞

− ⎛

⎟ ⎠

⎜ ⎞

= ⎛

239 arctan 1 5 4

arctan 1 π 16

Copyright ©2008 Kazuhito Ito

πの計算

„

arctan の計算 ⇒ テイラー展開を利用 + L

− +

− =

=

=

( 2 1 ) 1

+

3 5 7

arctan

7 5 3 1

2 1

1

x x x

x i x

x

i

i i

= S

⋅ +

⋅ −

⋅ +

⎟ =

⎜ ⎞

3 5

7 5

7

L

16 5 5

16 5 3

16 5 16 5 arctan 1 16

の場合

⎜ ⎞

⎛ 5 arctan 1 16

Copyright ©2008 Kazuhito Ito

πの計算プログラム (1)

void CalcPi( void )

{ long s[N+2], s5[N+2], s239[N+2];

long t[N+2] = {0}, q[N2], r[N2];

long d2[N+2] = {0}, d[N+2] = {0};

int pm;

/* 16*arctan(1/5) を計算 */

t[N] = 16;

d2[0] = 5;

LVdiv( t, d2, s5, r ); // s5 = 16/5 LVcopy( s5, t ); // t = 16/5

⎟ ⎠

⎜ ⎞

⎛ 5 arctan 1

16

を求める

Copyright ©2008 Kazuhito Ito

πの計算プログラム(2)

d2[0] = 25;

d[0] = 3;

pm = 0; // 減算 while(1){

LVdiv( t, d2, t, r ); // t = t/25 if( GetMSD(t) < 1 ) break;

LVdiv( t, d, s, r ); // s = t/d if( pm ){ // 加算

LVadd( s5, s ); pm = 0;

}else{ // 減算

LVsub( s5, s ); pm = 1;

} LVaddS( d, 2 );

}

tが0に

なれば 終了 ここでは

t=16/5

s5 ← s5 + s s5 ← s5 - s d ← d+2

Copyright ©2008 Kazuhito Ito

多倍長除算

long LVdiv( long a0[], long b0[], long q[] ) { …

nk = Normalize( b0, b );

LVmulS( a0, nk, a );

t = GetMSD( b );

for( k=0 ; k<N2 ; k++ ) q[k] = 0;

while(1){

if( Compare( a, b ) < 0 ) break;

s = GetMSD( a );

if( CompareOffset( a, b, s-t ) >= 0 ){

LVmulS( b, q[u], m );

while( CompareOffset( a, m, u ) < 0 ){

q[u]--;

LVmulS( b, q[u], m );

} }

これらすべて 多倍長数の 最上位桁位置を 必要とする

(2)

Copyright ©2008 Kazuhito Ito

最上位桁を求める処理

long GetMSD( long a[] ) { int k;

for( k=N2-1 ; k>=0 ; k-- ){

if( a[k] ) break;

} return k;

}

上位桁から順に調べ 非ゼロならば そこが最上位桁

„

値が小さい ( 最上位桁が添え字 0 に近い ) と 時間がかかる

„

同じ値に対して何度も繰り返し実行している 最上位桁を求める処理を節約する

Copyright ©2008 Kazuhito Ito

最上位桁を得る

„

最上位桁を毎回求めるのではなく、変数に記 憶しておく

„

1 つの多倍長数に対して

„多倍長数を記憶する配列v[N2]

„最上位桁位置を記憶する変数nMSD

„

意味の異なるデータをまとめて扱いたい 構造体を利用

Copyright ©2008 Kazuhito Ito

構造体の宣言

„

上に示したのは名前付き構造体

„

他に匿名(anonymous)の構造体もあり

⇒ ここでは省略

struct

名前

{

メンバ宣言

};

書式

1. 1 つの構造体としてまとめるデータ項目 ( メンバ ) の名前とデータ型を宣言 2. 構造体の型の名前を宣言

Copyright ©2008 Kazuhito Ito

構造体型宣言の例

„

多倍長数の場合の例 struct LongValue_data {

long v[N2];

int nMSD;

};

名前LongValue_data の構造体の型を宣言

2つのメンバの

 データ型と名前 を宣言

通常の変数宣言と 同じ書式

構造体名の規則は変数や関数と同じ

(アルファベット、数字、‘_’の組合せ)

変数/関数/配列と名前が重なっても問題ない

構造体型の変数宣言

„

通常のデータ型と同様に変数や配列を 宣言可能

struct LongValue_data x, y;

pは構造体personal_dataを指すポインタ struct LongValue_data A[100];

struct LongValue_data

*p;

2つの構造体変数xとyを宣言

要素数100の構造体配列Aを宣言 例

(後述)

構造体の型宣言と変数宣言

„

同時に宣言可能

„

構造体の型は1度だけ宣言すればよい struct st {

int n;

double f;

} x, y;

例 名前stの構造体の型を宣言 同時に構造体stの

変数xとyを宣言

struct st { int n;

double f;

} x, y;

struct st z;

宣言済みの構造体stの 変数zを宣言

名前stの構造体の型と 構造体stの変数xとyを宣言

(3)

Copyright ©2008 Kazuhito Ito

構造体の代入

„

同じ型の構造体は代入が可能

„

たとえメンバが全く同じでも、名前が異なる 構造体は代入できない

struct LongValue_data x, y;

x = y;

struct st1 { int a,b; } x;

struct st2 { int a,b; } y;

x = y;

異なる名前の構造体の代入

⇒コンパイルエラー 例

Copyright ©2008 Kazuhito Ito

メンバの参照

„

メンバのデータを読み書きするには struct st1 { int a,b; } x, y[10];

x.a = 10;

y[1].b += x.a;

メンバは通常の変数とまったく同じように使用できる 構造体変数名とメンバ名を ピリオドでつなぐ

Copyright ©2008 Kazuhito Ito

構造体の初期化

„

配列の初期化と同じ文法で行う struct st1 {

int a,b;

char s[10];

};

struct st1 x = {1, 10, “Hello” };

メンバaの初期化データ

メンバbの初期化データ

メンバsの初期化データ 初期化

メンバより初期化データが少ない場合は、

初期化データとして0やNULLを補う

Copyright ©2008 Kazuhito Ito

型別名の宣言

„

typedef 宣言子を利用 typedef

型 別名

;

書式 型に別名を与える

typedef struct LongValue_data { long v[N2];

int nMSD;

} LongValue;

LongValue a, x[5];

構造体の型を宣言 するとともに、別名

LongValueを与える

別名を使って

変数や配列を宣言 変数a, 配列xはLongValue型として宣言される

(= struct LongValue_data型)

Copyright ©2008 Kazuhito Ito

多倍長加算

„

加数bの最上位桁まで加算

void LVadd( LongValue *pa, LongValue *pb ) { long i, c=0;

for( i=0 ; i<=pb->nMSD ; i++ ){

pa->v[i] += (pb->v[i]+c);

if( pa->v[i] >= D ){

pa->v[i] -= D;

c = 1;

}else{

c = 0;

} }

#define D 10000

として、

加算結果がD以上 ならば桁上げ

a ← a+b

変数の前の*や->は何?

多倍長数の構造体を引数に

Copyright ©2008 Kazuhito Ito

関数の呼び出しについて

„

定義した関数を呼び出して、処理を行う

#include <stdio.h>

#include <math.h>

double

solve2p( double a, double b, double c ) {

double

bunshi, bunbo;

bunshi = -b+sqrt(b*b-4*a*c);

bunbo = 2*a;

return

bunshi / bunbo;

}

int

main()

{

double

xp = solve2p( 1, 4, 3 );

printf( “xp=%f¥n”, xp );

}

関数の 定義

関数の 呼び出し

(4)

Copyright ©2008 Kazuhito Ito

関数の呼び出し ( その 2)

„ 実行例

„ プログラム中で関数を呼び出す部分

„ 引数に実際の値を指定して関数を呼び出し

⇒実引数という

„ 引数aに1、bに4、cに3が対応付けられる

„ 実引数に対して、関数定義側の引数を仮引数という

xp=-1.000000

xp = solve2p( 1, 4, 3 );

2次方程式 x

2

+ 4 x + 3 = 0

の解-1, -3

doublesolve2p( doublea, doubleb, doublec )

xp = solve2p( 1, 4, 3 );

並び順による対応

Copyright ©2008 Kazuhito Ito

関数呼び出しと引数

„

仮引数は変数

実引数の値が初期値として代入される int n=1, m=5, y;

y = func( n, m );

... int func( int a, int b ) { int x;

a = a*2;

... return x;

}

実引数(1, 5) 仮引数(a, b) 初期値として

aに1, bに5が与えら

れて関数が開始する

仮引数の値を変更しても実引数は変化しない 以降a, bは変数として使用

Copyright ©2008 Kazuhito Ito

C 言語の引数は ‘ 値渡し ’

„

値渡し : 実引数の値が仮引数にコピーされる

„

問題点が 2 つ

„ 関数側で仮引数を書き換えても、実引数は変化 しない

„ 引数のデータ量が多い場合に、コピーのための 時間とメモリの両方が無駄遣い

int n=1, m=5, y;

y = func( n, m );

... void func( int a, int b ) { a = a*2;

} ...

仮引数aを変更しても 実引数nは変化しない

Copyright ©2008 Kazuhito Ito

ポインタ

„

計算機のメモリ上に記憶された変数の位 置 ( アドレス ) をポインタという

„

変数を名前(変数名)で指し示すだけでなく、

ポインタで指し示すこともできる

ポインタ 変数

ここにデータを記憶

変数xの

‘ポインタ’

変数x

変数名

‘x’

変数a 変数w

ポインタに関する操作

„

変数のポインタを取得 ⇒ 単項演算子&

„

ポインタを記憶する変数: ポインタ変数

„

ポインタが指す変数を読み書き

⇒単項演算子 * int i=10, j, *p;

p = &i;

j = *p;

*p = 100;

int i;

&i

変数iのポインタ(アドレス)

int *p;

int型変数のポインタを記憶する

ポインタ変数pを宣言

pに変数iのポインタを代入 pが指す値(i=10)を読み出し pが指す変数(i)に代入

ポインタを引数に用いる

„

ポインタも関数引数に使用可能

„

実引数として与えたポインタが関数の仮引 数に代入される

int m=5, y;

/*ここではm=5*/

func( &m );

/*ここではm=10*/

...

void func( int

*p )

{

*p = 10;

}

実引き数として 変数mのポインタ

仮引き数pに変数mの ポインタが代入される

*pは関数の外の 変数mを参照

(5)

Copyright ©2008 Kazuhito Ito

ポインタを引数に用いると

„

ポインタを通して、関数外の変数を変更可 能

„ 大域変数だけでなく局所変数も変更可能

„

ポインタは位置情報 ( アドレス ) であり、実引 数から仮引数へのコピーは簡単

„ 元のデータがどんなに巨大でもポインタは高々

32ビットないし64ビット

Copyright ©2008 Kazuhito Ito

(pが指す(->)構造体のメンバa)

構造体とポインタ

„

構造体もポインタを利用可能

„

ポインタを通してメンバを読み書き struct st1 { int a,b; } x, y, *p;

p = &x;

y = *p;

struct st1 { int a,b; } x, *p;

p = &x;

(*p).a = 10;

ポインタの定義どおりの方法

p->a = 10;

等価な

記述 演算子‘->’を利用 構造体変数xのポインタを ポインタ変数pに代入

Copyright ©2008 Kazuhito Ito

多倍長加算

„

加数 b の最上位桁まで加算

void LVadd( LongValue *pa, LongValue *pb ) { long i, c=0;

for( i=0 ; i<=pb->nMSD ; i++ ){

pa->v[i] += (pb->v[i]+c);

if( pa->v[i] >= D ){

pa->v[i] -= D;

c = 1;

}else{

c = 0;

} }

ポインタと‘->’を 用いて構造体の メンバを読み書き

a ← a+b

多倍長数構造体のポインタを引数

typedef struct LongValue_data { long v[N2]; /*

多倍長数データ

*/

int nMSD; /*

最上位桁位置

*/

} LongValue;

Copyright ©2008 Kazuhito Ito

多倍長加算 ( 続き )

„

上位桁への桁上げを考慮

void LVadd( LongValue *pa, LongValue *pb ) {

… if( c ){

while( 1 ){

pa->v[i]++;

if( pa->v[i] >= D ) pa->v[i++] = 0;

else break;

} }

if( pa->nMSD < i ) pa->nMSD = i;

}

bの最上位桁まで加算(前ページ)

残りの上位桁への 桁上げ処理

a ← a+b

上位桁への桁上げが なくなれば処理終了

加算後にaの最上位桁位置修正

Copyright ©2008 Kazuhito Ito

多倍長減算

„

加数bの最上位桁まで減算

void LVsub( LongValue *pa, LongValue *pb ) { long i, c=0;

for( i=0 ; i<=pb->nMSD ; i++ ){

pa->v[i] -= (pb->v[i]+c);

if( pa->v[i] < 0 ){

pa->v[i] += D;

c = 1;

}else{

c = 0;

} }

引算結果が負ならば 上位から桁借りして

Dを加えて正にする

a ← a-b

Copyright ©2008 Kazuhito Ito

多倍長減算(続き1)

„

上位桁への桁借りを考慮

void LVsub( LongValue *pa, LongValue *pb ) {

… if( c ){

while( 1 ){

pa->v[i]--;

if( pa->v[i] < 0 ) pa->v[i++] = D-1;

else break;

} } } …

bの最上位桁まで減算(前ページ)

残りの上位桁への 桁借り処理

a ← a-b

上位桁への桁借りが なくなれば処理終了

aの最上位桁位置修正(次ページ)

(6)

Copyright ©2008 Kazuhito Ito

多倍長減算 ( 続き 2)

„

減算の結果、最上位桁が 0 になる場合あり void LVsub( LongValue *pa, LongValue *pb ) { …

if( pa->nMSD < i ) pa->nMSD = i;

while( pa->v[pa->nMSD] == 0 ){

pa->nMSD--;

} }

桁借り処理完了(前ページ)

a ← a-b

最上位桁が0ならば 最上位桁位置を下げる 最も上位にある非ゼロの桁が最上位桁

Copyright ©2008 Kazuhito Ito

多倍長除算 (1)

void LVdiv(LongValue *pa0, LongValue *pb0, LongValue *pq, LongValue *pr)

{ long s, t, k, u, nk;

LongValue a, b;

nk = Normalize( pb0, &b );

LVmulS( pa0, nk, &a );

t = b.nMSD;

for( k=0 ; k<N2 ; k++ ) pq->v[k] = 0;

pq->nMSD = 0;

while(1){

if( Compare( &a, &b ) < 0 ) break;

// a < b ならば除算終了

q ← a0/b0

bを正規化(nk倍) aもnk倍

商を0に 初期化

Copyright ©2008 Kazuhito Ito

多倍長除算 (2)

s = a.nMSD;

if( a.v[s] >= b.v[t] ){

if( CompareOffset( &a, &b, s-t ) >= 0 ){

u = s-t; pq->v[u] = 1;

}else{

u = s-t-1; pq->v[u] = D-1;

}else{ }

u = s-t-1; pq->v[u] = (a.v[s]*D+a.v[s-1])/b.v[t];

} LVmulS( &b, pq->v[u], &m );

while( CompareOffset( &a, &m, u ) < 0 ){

pq->v[u]--;

LVmulS( &b, pq->v[u], &m );

}

商の

最上位桁位置u および値q[u]の 候補を求める

a≧m=b×q[u]

を満たす最大の

q[u]

を求める

tを検査

bD

s

a

Copyright ©2008 Kazuhito Ito

多倍長除算 (3)

while( 1 ){

if( Compare( &a, &b ) < 0 ) break;

// a < b ならば除算終了

LVmulS( &b, pq->v[u], &m );

while( CompareOffset( &a, &m, u ) < 0 ){

pq->v[u]--;

LVmulS( &b, pq->v[u], &m );

} LVsubOffset( &a, &m, u );

}

pq->nMSD = GetMSD( pq );

a ←a – b×q[u]*D

u

u, q[u]の候補を求める(省略)

商qの最上位桁 位置を求める

πの計算 (1)

void CalcPi( void )

{ LongValue s, t, s5, s239, q, r, d, d2;

int pm, k;

/* 定数初期化 */

for( k=0 ; k<N2 ; k++ ) t.v[k] = 0;

t.nMSD = 0;

LVcopy( &t, &d );

LVcopy( &t, &d2 );

/* 16*arctan(1/5) を計算 */

t.v[N] = 16; t.nMSD = N;

d2.v[0] = 5; d2.nMSD = 0;

LVdiv( &t, &d2, &s5, &r ); // s5 = 16/5;

LVcopy( &s5, &t ); // t = 16/5;

t=d=d2=0に初期化

πの計算 (2)

d2.v[0] = 25; d2.nMSD = 0;

d.v[0] = 3; d.nMSD = 0;

pm = 0; // 減算 while(1){

LVdiv( &t, &d2, &t, &r ); // t = t/25 if( t.nMSD < 0 ) break;

LVdiv( &t, &d, &s, &r ); // s = t/d if( pm ){ // 加算

LVadd( &s5, &s ); pm = 0;

}else{ // 減算

LVsub( &s5, &s ); pm = 1;

} LVaddS( &d, 2 );

}

tが0に

なれば 終了 ここでは

t=16/5

s5 ← s5 + s

s5 ← s5 - s

d ← d+2

(7)

Copyright ©2008 Kazuhito Ito

πの計算 (3)

… /* 4*arctan(1/239) を計算 */

t.v[N] = 4; t.nMSD = N;

d2.v[0] = 239; d2.nMSD = 0;

LVdiv( &t, &d2, &s239, &r );

LVcopy( &s239, &t );

d2.v[1] = 5; d2.v[0] = 7121;

d2.nMSD = 1; // 239*239

d.v[2] = 0; d.v[1] = 0; d.v[0] = 3;

d.nMSD = 0;

⎟ ⎠

⎜ ⎞

⎛ 239 arctan 1

4

を求める

Copyright ©2008 Kazuhito Ito

πの計算 (4)

pm = 0; // 減算 while(1){

LVdiv( &t, &d2, &t, &r ); // t = t/(239*239) if( t.nMSD < 0 ) break;

LVdiv( &t, &d, &s, &r ); // s = t/d if( pm ){ // 加算

LVadd( &s239, &s ); pm = 0;

}else{ // 減算

LVsub( &s239, &s ); pm = 1;

} LVaddS( &d, 2 );

}

tが0になれば

終了

ここでは

t=4/239

s239 += s s239 -= s d ← d+2

Copyright ©2008 Kazuhito Ito

πの計算 (5)

void CalcPi( void )

{ /* 16*arctan(1/5) を計算 */

… /* 4*arctan(1/239) を計算 */

LVsub( &s5, &s239 );

} s5-s239 = π

Copyright ©2008 Kazuhito Ito

実行結果

3.

1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679

2162484391 4035998953 5394590944 0704691209 1409387001 2645600162 3742880210 9276457931 0657922955 2498872758 4610126483 6999892256 9596881592 0560010165 5256375633

正しくは78

N=2500 ⇒ 小数点以下10000桁まで計算

正しく求めるには、N=2501 とする

3.

1415926535 8979323846 2643383279 5028841971 6939937510

4610126483 6999892256 9596881592 0560010165 5256375678 5628

Copyright ©2008 Kazuhito Ito

プログラムの実行時間

„

桁数Nとプログラム実行時間の関係

0.01 0.10 1.00 10.00 100.00 1000.00

100 1000 10000

N

PentiumIV 2.4GHz

オリジナル

最上位桁 位置記録

Copyright ©2008 Kazuhito Ito

まとめ

„

プログラム中の無駄な処理を節約して実 行時間短縮

„

構造体

„

ポインタ

„

関数の引数とポインタ

参照

関連したドキュメント

l 「指定したスキャン速度以下でデータを要求」 : このモード では、 最大スキャン速度として設定されている値を指 定します。 有効な範囲は 10 から 99999990

WAV/AIFF ファイルから BR シリーズのデータへの変換(Import)において、サンプリング周波 数が 44.1kHz 以外の WAV ファイルが選択されました。.

⑥ニューマチックケーソン 職種 設計計画 設計計算 設計図 数量計算 照査 報告書作成 合計.. 設計計画 設計計算 設計図 数量計算

国の5カ年計画である「第11次交通安全基本計画」の目標値は、令和7年までに死者数を2千人以下、重傷者数を2万2千人

製造業その他の業界 「資本金3億円を超える」 かつ 「従業員数300人を超える」 「資本金3億円以下」 または 「従業員300人以下」

ある周波数帯域を時間軸方向で複数に分割し,各時分割された周波数帯域をタイムスロット

項   目  単 位  桁   数  底辺及び垂線長 m 小数点以下3桁 境界辺長 m  小数点以下3桁

、肩 かた 深 ふかさ を掛け合わせて、ある定数で 割り、積石数を算出する近似計算法が 使われるようになりました。この定数は船