コンピュータの最近のブログ記事

法4の素数のレースに続いて、3より大きい素数を3で割ったときの余りの1組と2組のレース、すなわち法3のレースについても、みてみる。

そこで、以下のようなプログラムを書いてみた。

// 3を法としたレース

#include <stdio.h>

int SGN(int n); // 符号を返す

int main()     {
	
FILE *fi;
int i = 0;
int p; // 素数
int N1 = 0, N2 = 0;

int b_sgn, sgn = 1;  // 符号変数

if (( fi = fopen("sieve.txt","rt")) == NULL ) return -1;  // 予めエラトステネスの篩で生成した10億までの素数

 while ( fscanf(fi,"%d",&p) != EOF )     {
	
  if ( p > 3 )    {
   if ((p%3) == 1 ) N1++;
   else if ((p%3) == 2 ) N2++;
   i++;  // 素数の順番
   b_sgn = sgn;  // N3 - N1の符号の変化をみる
   sgn = SGN(N2 - N1);
   if (( b_sgn * sgn ) == -1 ) printf("%d,%2d,%d,%d,%d\n",i,sgn,p,N1,N2);  // 符号の入れ替わり
}
  }
  

printf("(p mod 3) == 1: %d\n",N1);
printf("(p mod 3) == 2: %d\n",N2);
return 1;
}

int SGN(int n)  { // 符号を返す
  if ( n < 0 ) return -1;
  return 1;
}

これの結果は、以下のように、単に最終結果が出るだけ、そして最終結果が2組の勝ちということは、2組が最初から50847534連勝で一度も負けなかったことを意味する。

(p mod 3) == 1: 25422713
(p mod 3) == 2: 25424819

そこで、以下に示すプログラムで、10000000毎に途中の差を見てみると、こちらのとおりである。ここから見てとれるように、単調増加でないにしても、じりっじりっと差が広がりつつあるのが分かる。それなら永遠に2組が勝ち続けるのかというと、谷口氏の「素数のレース」によると、608981813029のところで、1組が3組を逆転するというのが、コンピュータを駆使して、1976年に分かったのだという。ええっ、なら私もそれを検証しようとしたが、そのためにはそもそも、そのためには2から608981813029までのすべての素数を計算しなくてはならず、気が遠くなるほど時間がかかるので、諦めた次第である。なんとも不思議な世界なのであった。

// 3を法としたレースの10000000毎の集計

#include <stdio.h>

int main()     {
	
FILE *fi;
int p; // 素数
int N1 = 0, N2 = 0;

int Border = 10000000;

if (( fi = fopen("sieve.txt","rt")) == NULL ) return -1;  // 予めエラトステネスの篩で生成した10億までの素数

 while ( fscanf(fi,"%d",&p) != EOF )     {
	
  if ( p > 3 )    {
     if ((p%3) == 1 ) N1++;
     else if ((p%3) == 2 ) N2++;
     if ( p > Border )            {
        printf("%d,%d,%d,%d\n",p,N1,N2,N2-N1);
        Border += 10000000;
     }
  }
 }
 
        printf("%d,%d,%d,%d\n",p,N1,N2,N2-N1);

  
return 1;

}

冗長化

user-pic
0

私は基本的にとてもそそっかしくて粗忽な性格だと自分で認識しているので、少しでもトラブルを避けようと、冗長化を欠かさない。例えば、道を歩いていて、ふと何かアイデアを思いついたら、すぐに書き留めないと忘れてしまうので、いつもボールペンを鞄に入れているのだが、一本だけだとインクが切れたり、目詰まりしたりして書けなくなることがあるので、ボールペンは必ず少なくとも二本はもっている。

しかし、日常的に一番冗長化で注意しているのは、パソコンのHDDのバックアップである。昔から、USB接続などのポータブルHDDを用意して、ほとんど毎日パソコンのHDDのバックアップを行っていた。BUNBACKUPというフリーウェアのツールは、差分コピーが可能なので、HDD全体でもそんなに時間はかからなかったりする。そうすると、パソコンのHDDがたまたまクラッシュしたときも、データの被害が最小限で、私の冗長化のスキームは妥当なのだと意を強くした。

そんな私なので、外資系にいたとき、会社で使っているパソコンのHDDのバックアップを取っていない同僚に、それでは危険なので、ポータブルHDDを買って、バックアップすべきだと強く勧めた。すると、ポータブルHDDはいくらだ、と聞くので、当時は今より少し高くて、1万円くらいだったと思う。それでそう答えたら、馬鹿な、そんなもんに1万円も払えるか、というので、私は呆れて、もう相手にしないことにした。というのは、別にお金に困っているわけではないどころか、数百万円の高級車を乗り回しているのである。優先順位がおかしすぎる。案の定、その後パソコンのHDDがたまたまクラッシュしてデータを失い、相当困ったことがあったらしい。他でもない、その外資系はコンピュータ会社ではあった。

さて、そんな私のバックアップ好きを知ってか、外資系のときのある友人が、LANディスクをくれた。要するに、LANのネットワーク経由でバックアップする。私はそれを自宅のLANに接続し、ポータブルHDDよりやや低い頻度でバックアップするようにした。二重化よりも三重化の方が心強い。

そのLANディスクをもらったのは確か、2011年の暮れだったと思うが、平成から令和に切り替わる今に至るまでもちゃんと動いているのだからなかなか堅牢である。ただ、仕方ないのだが、ネットワークの調子が悪いと、切断されてしまう。そんなときBUNBACKUPを走らせても、ドライブが見つかりません、とあっさりエラー終了する。

ところが最近のあるとき、BUNBACKUPでLANディスクに所定のフォルダの内容をバックアップしようとすると、差分コピーのはずなのに、そのフォルダ全体をコピーしようとする。要するに差分コピーになっていないので、あれっ、LANディスク側のフォルダの内容が、何かの理由で消えてしまったのか、と思った。

ともかくコピーが終わったので、LANディスクのフォルダを開こうとしたら、なんとドライブが繋がっていないとエラーが出る。そんな馬鹿な、今のバックアップは何だったんだと、LANディスクが置いてある場所に行ってみると、なぜかコンセントがずれて、半挿しになっている。で、LANディスクを触ってみると、結構熱い。おっと、これはいよいよLANディスクが死んだかと、とりあえずLANディスクの電源を抜いて、コンセントもきちんと入れて、電源に繋いだら、なんとかLANディスクは正常動作し、熱も平熱に戻った。

いやあでもこれは、冗長化の精神では、LANディスクもそろそろ買い替えかな、と思った次第である。そもそも、コンセント半挿しで、LANディスクが暴走するだろうか、発熱するだろうか。意味不明である。

アマゾンからよく本の案内が来て、最近は私の好みに合う本が多いのだが、いろんな意味で、いちいち買う訳にはいかないので、選択に苦労している。

それでも、これは是非買わなくては、と思ったのは、古澤明著「光の量子コンピューター」インターナショナル新書である。私が特に興味をもっている分野の一つであるばかりでなく、あまり類書がない。それで、即注文して、届いたら読み始めた。

すると、いきなりおおっ、と思ったのは、この本は、光による論理ゲート型量子コンピューターについて、記述したものらしい。

とはいえ、最初は、量子コンピューターの一般論から始まる。で、なるほど、と思ったのは、量子コンピューターのメリットは、計算速度よりもむしろ、発熱量や消費電力が少ないことである、という記述で、そうだったのか、と感心した。

最近では、量子アニーリング型のコンピューターがあるが、古澤氏は、量子アニーリング型は、組み合わせ最適問題を解くのに特化している点で汎用的でないが、論理ゲート型量子コンピューターは、汎用を目指している、という。

量子アニーリングのファンでもある私は、組み合わせ最適化問題だって、世間の相当な応用があるし、確か、一般的なアルゴリズムを、組み合わせ最適化に落し込む技法があったんじゃないっけ、と思ったのだが、それに対する拘りから直ぐに離れた。なぜなら、古澤氏が、論理型量子コンピューターの困難についても、きちんと言及し始めたからである。

すなわち、量子の重ね合わせと、量子もつれの状態がきわめてデリケートで、壊れやすい。そのため、極低温の超伝導状態に保つ必要がある。

また、どんなコンピューターでも、ノイズなどでビットの情報が変わってしまうことがあるので、誤り訂正の仕組みが必須であるが、量子コンピューターでは、そういう仕組みが不可能視されていた時期があり、量子コンピューターは現実的には使えないと思われていた。

ところが、世の中には天才がいるもので、量子コンピューターに誤り訂正の仕組みが考案された。それでも、現実のアルゴリズムを量子コンピューターに落し込むのは無理だと思われていたが、そこにショアが、素因数分解を量子コンピューターで解く仕組みを考え出して、世の中に衝撃を与えた。すなわち、これでもう、素因数分解の困難に依存する公開鍵暗号は使えないと。

しかし、量子コンピューターの量子もつれの不安定さなどの問題で、ショアの技法は、現実的に問題を解くに至っていない。その後、ショアのアルゴリズム以外にも、特定のアルゴリズムを量子コンピューターの問題に帰着させる技法が何十か案出されたが、到底、汎用になっていない。なので、現時点で、汎用を喧伝する量子コンピューターは、相当に眉唾であるといえる。

さて、著者の古澤氏の経歴が記述される。古澤氏は、東大工学部物理工学科とその大学院を卒業すると、ニコンで光デバイスの研究を担当した。それから、カリフォルニア工科大に留学したのだが、そこはなんと、量子コンピューター研究のメッカで、古澤氏は、大いに勉強し、刺激を受けた。そしてなんと、古澤氏は、カルテックで、1998年、世界で初めて量子テレポテーションの実験に成功した。なんとそれは、ノーベル賞級の業績ではないだろうか。ともかく、この時点で、古澤氏が世界のフロント・エンドに立ったことは確かだろう。

日本に戻ってきて、東大の助教授に就いた古澤氏は、光を使った三者間の量子もつれ、九者間の量子もつれ、さらにはシュレーディンガーの猫状態の量子テレポテーションの実現と、光量子の研究で世界をリードしてきた。

さらには、時間領域多重一方向計算方式の実現で、光パルスが飛んでくる方向に向かって、多数の量子ビットを生成でき、これはプログラミングもできるので、まさに光量子コンピューターの道を拓くものである。さらには、量子テレポテーションの心臓部を光チップ化したり、武田俊太郎助教によるループ型光量子コンピューターの発明と、一歩ずつ、実用的な光量子コンピューターの実現の方向に進んでいる。

ということで、量子コンピューターや、光量子理論を学ぶとともに、この分野の発展を興味をもって眺めていくことにする。

会社勤務なので、当然毎日の勤務時間を記録する必要があるが、いつもは既存の、総務部の人が作成したフォームを使っているので、時間表示について悩むことはなかった。しかし、ある特別な仕事で、それに費やした時間を集計する必要があったので、例えば、下記に示すようなエクセルの表を作成して、時間を入力してみた。

すなわち、D2からD5までは、その行のC列からB列を引くという式を入れ、D6には、=SUM(D2:D5)と入れた。すると上記画像にあるように、D6の値が明らかに変である。小さすぎる。そこで、隣の席の人に聞いたら、自分も分からないので、グーグルで検索してみるという。

その結果、セルの書式設定をしなくてはならないのだという。すなわち、D6のセルを右クリックし、セルの書式設定でユーザー定義を選び、h:mmになっているのを、[h]:mmに書き直す。すると、以下のように、D6に正しい値が表示される。

要するに、時間のところは、24でのMODになっている。日付も入っていたら、繰り上がるのだろう。では、と試しに分の方を72にしてみたら、12になって、時間に1繰り上げになった。ならば、セルの書式設定でユーザー定義をh:[mm]にしたら、エラーになった。分の繰り上げを防ぐのは無理だった。

それにしても、大抵の機能はPerlで実現しているので、私は驚くほど、エクセルの機能に無知である。関数もほとんど知らない。一方、私が今いる会社は、エクセル文化で、マクロの達人も結構いる。なので、私はほとんど相手にされていないが、それでも私の仕事のスキルとPerlのスキルがあるので、渋々なんとか一目置いてくれている。こういう開き直りが私らしいといえる。

充電スキームでも述べたように、スマホを使い始めて七ヶ月くらいたって、最近妙にバッテリーの減り具合が速いように感じた。すなわち、朝の時点でフル充電しても、そんなに使っていないはずなのにバッテリーの残量が30%を切ったりする。

将棋アプリで棋譜をある程度長い時間観戦したときは、バッテリーの残量がゼロになりそうで、電車の中だったので、慌てて、バッテリー・チャージャーで充電した。出番がないと思っていたバッテリー・チャージャーが役にたって、よかったと思いつつ、最近のスマホのバッテリーの減り方は速すぎるので、もしかして早くもバッテリーが劣化しているのか、自分の使い方が悪いのか、とかいろいろ考えた。

それで、以前にたまたま、ウェブでスマホの最適化ツールを見かけたので、改めて調べたら、ヤフーのバッテリー最適化ツールが見つかった。おっ、これだ、と思って導入しようとしたら、Power Batteryというのも見つかって、こちらの方が微妙に評価が高いようなので、これを導入した。

すると、Power Batteryは、アンドロイドのチャージ管理プロセスをインターセプトして、バッテリーに充電している間、独自のチャージ進捗を表示する。それが見やすいので、とりあえずPower Batteryに好感をもった。

と思ったら、何かのタイミングで、画面の中心に回転する輪っかのようなものが現れ、アプリのアイコンをどんどん取り込んでいく。私は、ブラックホールか、と突っ込みたくなったが、その輪っかはどうみても、以前に見たインフルエンザ・ウイルスである。

で、あっけに取られていたら、最適化処理が終わり、9分最適化しました、とかメッセージが出る。なんかよく分からないなあ、と怪訝だったが、観察していると確かに、バッテリーのもちがよくなっている。

すなわち、今までだと、朝フル充電で、普通に使っていて夕方には30%を切ったりしていたが、Power Batteryを導入してからは、これがわずかに50%を切る程度で、この差は大きい。

その仕組みであるが、おそらく、バッテリー最適化しないと、ある特定のアプリだけ使っていても、背景でいろいろなアプリが通信しているのだろう。Power. Batteryは、こういう背景アプリの活動を制限しているのだろう。

これでまた、バッテリー・チャージャーの出番が減るが、それは警察のようなものなので、暇な方が望ましいのだろう。

去年の7月に、ガラケーをスマホに買い替えてから、ちょっと困ったのは、バッテリーが急速に減ることである。ガラケーだと、単にメールを使うだけだからかもしれないが、一回充電したら、少なくとも3日くらい平気であった。

ところが、スマホだと、画像を眺めたり、将棋の棋譜を見たりしていると、急速にバッテリーが減って、下手したら半日くらいで30%くらいまで残量が下がって、そろそろ充電しなくてはならないと、なんかうっとおしい。

それで、外出先では簡単には充電できないので、スマホを使うのを控えたり、どうしても充電さなくてはならないときのために、バッテリー・チャージャーを買ったりした。

それで、つい昔を思い出したのは、昔のノート型パソコンでも、結構速く放電されたが、そのころよく言われたのは、バッテリーにはメモリー効果というのがあって、充分使いきってから充電しないと、だんだん充電可能量が減ってきて、最後は10分くらいしか充電できなくなると言われた。実際、ノート型パソコンのバッテリは、経年劣化で、ほとんどバッテリとしての意味がなさないものが多かった。

その性質は、今のスマホのバッテリーにも継承されているのかと思ったら、スマホのバッテリーは、昔のパソコンのバッテリーとは違うらしい。すなわち、昔のパソコンのバッテリーは、ニッケル・カドミウム電池で、今のスマホのバッテリーは、リチウム・イオン電池である。

そのリチウム・イオン電池は、ニッケル・カドミウム電池と違って、メモリー効果はなく、よって使いきってから充電する、という配慮は無用なだけでなく、むしろ、あまり使いきったり、過充電しないで、ほどほど継ぎ足し充電するのがいい、という。そうやって使っていると、500回の充電くらいまて性能が低下しないというのだが、見ていると、実用上、もっとも持ちそうである。なんにしても、リチウム・イオン電池が、かなりの技術革新なのはよく理解できる。ただ、一時話題になった、バッテリーの発火問題はもう大丈夫なのだろうか。まあ一応、PSEマークというのを目安にすることにする。

あと、バッテリー・チャージャーであるが、せっかく充電して鞄に入れていても、二週間くらい全く使わないことがある。それでそのまま自然放電に任せるというのも何か変なので、ときどき、意図的にスマホに充電することで、適宜放電させている。これも、果たして最適な使い方なのかよく分からず、まだ暗中模索なのだった。

ともかく、バッテリー技術は、今の時代、極めて重要な技術なので、研究が盛んで、そのうち画期的なバッテリーが出でてくるだろう。そうすれば、下手な配慮も不要になるだろう。

さて、分数や二項係数の計算を補題として、いよいよベルヌーイ数の計算を行う。そのために、「ベルヌーイ数とゼータ関数」牧野書店の4ページにある以下の式を使う。

このままでは分かりにくいので、次のように変形して、B0,B1,...,Bn-1からBnを漸化式的に求めていくようにする。

こうやって式を書き下すと、これに従って、コーディングするだけでよい。以下のとおりである。frac64.hを使うと、以下のようにかなりシンプルにコーディングできた。なお、単なる便宜上、B0=1とB1=1/2を所与として計算した。


#include <stdio.h>
#include "frac64.h"

frac bc(int n, int i);

int main()                {

 int i,j,n;
 frac B[100];  // ベルヌーイ数の列
 frac obj, obj1;  // 計算用の変数
 
 B[0].setA(1); B[0].setB(1);
 B[1].setA(2); B[1].setB(1);
 
for ( n = 2; n < 39; n++ )        {
 obj.setA(1); obj.setB(0);
 for ( i = 0; i <= n-1; i++ )  {
  obj += (bc(n+1,i)*B[i]);
 }
  obj1.setA(n+1); obj1.setB(1);
  obj *= obj1;
  B[n].setA(1); B[n].setB(1);
  B[n] -= obj;
 
 printf("B<sub>%d</sub> = ",n);
 B[n].Show();
 printf("\n");
}
 
 return 1;

}

frac bc(int n, int i) {
 int j;
 __int64 a,b;
 frac obj(1,1), temp;
 
 if ( i == 0 ) return obj;
 
 if ( i > n/2 )   {
    i = n - i;
 }
 
 for ( j = 1; j <= i; j++ )  {
  b = (__int64) (n - j + 1);
  a = (__int64) j;
  
  temp.setA(a); temp.setB(b);
  obj *= temp;
 }
 return obj;
}

計算結果は、以下のとおりである。ウェブ上では、B28くらいまでしか計算した結果が見つけられなかったが、たまたま最近買った、青木美穂「p進ゼータ関数」日本評論社には、B50までリストされていた。比較してみると、B35までは合っているが、B36から違っている。ここで、64ビット整数の限界を超えたようである。これ以上だと、多倍長分数ルーチンを用意するよりない。

B2 = 1/6
B3 = 0/1
B4 = -1/30
B5 = 0/1
B6 = 1/42
B7 = 0/1
B8 = -1/30
B9 = 0/1
B10 = 5/66
B11 = 0/1
B12 = -691/2730
B13 = 0/1
B14 = 7/6
B15 = 0/1
B16 = -3617/510
B17 = 0/1
B18 = 43867/798
B19 = 0/1
B20 = -174611/330
B21 = 0/1
B22 = 854513/138
B23 = 0/1
B24 = -236364091/2730
B25 = 0/1
B26 = 8553103/6
B27 = 0/1
B28 = -23749461029/870
B29 = 0/1
B30 = 8615841276005/14322
B31 = 0/1
B32 = -7709321041217/510
B33 = 0/1
B34 = 2577687858367/6
B35 = 0/1
B36 = 252116306927798389/755127450
B37 = 0/1
B38 = 3187113749954969/94114272101850

二項係数

user-pic
0

分数計算(その3)で作成したfrac64.hのライブラリを利用して、二項係数を計算してみた。今更なんで二項係数を計算してみるのかということになるが、実は最近、ずっと前から買ってあった、「ベルヌーイ数とゼータ関数」荒川恒雄・伊吹山知義・金子昌信著、牧野書店を眺めていて、とりあえずベルヌーイ数をパソコンのプログラムで計算させてみようと思い立ったからである。そのためには二項係数は必須である。

実は分数計算も、このための準備なのだが、分数計算はベルヌーイ数だけでなく、楕円曲線の有理数解を計算するためにも使えると思っている。それで、二項係数は英語でbinomial coefficientなので、frac bc(int n, int i)という関数を書いてみた。これは数学では記号では縦長の括弧に、n iと縦に並べて書く形式である。fracとあるのは、分数のクラスのインスタンスを返すことを示す。

そこで、サンプルのソースコードを示す。どうってことないプログラムであるが、二項係数の計算で、整数の掛け算を続けると、あっという間に64ビット整数の限界を超えるので、個別の分数の掛け算に分けて、その都度通分することで、分子の増加を抑えた点くらいが工夫である。また、ソースコードの後に、実行結果の例も示す。実行結果の各行の左端の数字は、(x+1)nのnを示す。ここでも、64ビット整数の限界との戦いで、n = 54以上だと限界なのかもしれない。

#include <stdio.h>
#include "frac64.h"

frac bc(int n, int i);

int main()                {

 int i,j,n;
 frac X;
 
 for (n = 1; n <= 9; n++ )  {
  printf("%d: ",n);
   for ( i = 0; i <= n; i++ )  {
     X = bc(n,i);
     X.Show();
     printf("; ");
   }
   printf("\n\n");
 }
 
 n = 54;
 printf("%d: ",n);
 for ( i = 0; i <= n; i++ )  {
     X = bc(n,i);
     X.Show();
     printf("; ");
 }
 printf("\n");
 
 return 1;

}

frac bc(int n, int i) {
 int j;
 __int64 a,b;
 frac obj(1,1), temp;
 
 if ( i == 0 ) return obj;
 
 if ( i > n/2 )   {
    i = n - i;
 }
 
 for ( j = 1; j <= i; j++ )  {
  b = (__int64) (n - j + 1);
  a = (__int64) j;
  
  temp.setA(a); temp.setB(b);
  obj *= temp;
 }
 return obj;
}

1: 1/1; 1/1;

2: 1/1; 2/1; 1/1;

3: 1/1; 3/1; 3/1; 1/1;

4: 1/1; 4/1; 6/1; 4/1; 1/1;

5: 1/1; 5/1; 10/1; 10/1; 5/1; 1/1;

6: 1/1; 6/1; 15/1; 20/1; 15/1; 6/1; 1/1;

7: 1/1; 7/1; 21/1; 35/1; 35/1; 21/1; 7/1; 1/1;

8: 1/1; 8/1; 28/1; 56/1; 70/1; 56/1; 28/1; 8/1; 1/1;

9: 1/1; 9/1; 36/1; 84/1; 126/1; 126/1; 84/1; 36/1; 9/1; 1/1;
(中略)
54: 1/1; 54/1; 1431/1; 24804/1; 316251/1; 3162510/1; 25827165/1; 177100560/1; 1040465790/1; 5317936260/1; 23930713170/1; 95722852680/1; 343006888770/1; 1108176102180/1; 3245372870670/1; 8654327655120/1; 21094923659355/1; 47153358767970/1; 96926348578605/1; 183649923622620/1; 321387366339585/1; 520341450264090/1; 780512175396135/1; 1085929983159840/1; 1402659561581460/1; 1683191473897752/1; 1877405874732108/1; 1946939425648112/1; 1877405874732108/1; 1683191473897752/1; 1402659561581460/1; 1085929983159840/1; 780512175396135/1; 520341450264090/1; 321387366339585/1; 183649923622620/1; 96926348578605/1; 47153358767970/1; 21094923659355/1; 8654327655120/1; 3245372870670/1; 1108176102180/1; 343006888770/1; 95722852680/1; 23930713170/1; 5317936260/1; 1040465790/1; 177100560/1; 25827165/1; 3162510/1; 316251/1; 24804/1; 1431/1; 54/1; 1/1;

分数計算の(その2)の続きで、32ビット整数では極めて速く計算の限界に達するので、64ビット整数にしてみた。それは単に、intを、__int64にし、printfにおける%dを、%I64dにするだけでよかった。frac64.hとしてヘッダ・ファイルを保存した。前回のプログラムのヘッダファイルを、このfrac64.hに置き換えるだけで、そのまま使えた。折角なので、22項まで計算してみた。64ビット整数でも、ここらあたりがほぼ限界で、これ以上だと多倍長分数にするよりない。

一応、以下にソースコードを示す。

#include <stdio.h>
#include "frac64.h"


int main()                {

 int i = 1, n = 1;
 frac r,u;
 r.setA(1);
 r.setB(0);

 u.setB(1); 
 while ( n < 23 )   {
   u.setA(i);
   r += u;
   printf("%d項までの和:",n++); r.Show(); printf(" = %10.8f\n",r.ShowVal());
   
   i += 2;
   u.setA(i);
   r -= u;
   printf("%d項までの和:",n++); r.Show(); printf(" = %10.8f\n",r.ShowVal());
   
   i += 2;
 }
 
 
 return 1;

}

また、計算結果は、以下のとおりである。

1項までの和:1/1 = 1.00000000
2項までの和:2/3 = 0.66666667
3項までの和:13/15 = 0.86666667
4項までの和:76/105 = 0.72380952
5項までの和:263/315 = 0.83492063
6項までの和:2578/3465 = 0.74401154
7項までの和:36979/45045 = 0.82093462
8項までの和:33976/45045 = 0.75426795
9項までの和:622637/765765 = 0.81309148
10項までの和:11064338/14549535 = 0.76045990
11項までの和:11757173/14549535 = 0.80807895
12項までの和:255865444/334639305 = 0.76460069
13項までの和:1346255081/1673196525 = 0.80460069
14項までの和:3852854518/5019589575 = 0.76756365
15項までの和:116752370597/145568097675 = 0.80204641
16項までの和:3473755390832/4512611027925 = 0.76978835
17項までの和:3610501179557/4512611027925 = 0.80009138
18項までの和:3481569435902/4512611027925 = 0.77151995
19項までの和:133330680156299/166966608033225 = 0.79854698
20項までの和:129049485078524/166966608033225 = 0.77290595
21項までの和:5457995496252709/6845630929362225 = 0.79729620
22項までの和:227848175409504262/294362129962575675 = 0.77404038

一応、π/4 = 0.785372...に、緩慢ながら、近づきつつあることが見て取れる。

分数の計算の続きで、クラスのメンバ関数を追加して、細かい不具合を修正し、frac.hとしてヘッダ・ファイルを作成してみた。そこで、このクラスを使って、ある級数の分数計算をしてみることにした。

ある級数とは、以下に示す級数である。

これは、歴史的にはライプニッツの級数と呼ばれているが、黒川重信氏によると、これを世界で最初に発見したのは、南インドのケララ派で、マーダヴァ級数と呼ばれるべきだそうである。

これの分数計算は、frac.hを使うと、以下のような極めて短いプログラムで計算できる。

#include <stdio.h>
#include "frac.h"


int main()                {

 int i = 1, n = 1;
 frac r,u;
 r.setA(1);
 r.setB(0);

 u.setB(1); 
 while ( n < 12 )   {
   u.setA(i);
   r += u;
   printf("%d項までの和:",n++); r.Show(); printf(" = %10.8f\n",r.ShowVal());
   
   i += 2;
   u.setA(i);
   r -= u;
   printf("%d項までの和:",n++); r.Show(); printf(" = %10.8f\n",r.ShowVal());
   
   i += 2;
 }
 
 
 return 1;

}

また、計算結果は、以下のとおりである。

1項までの和:1/1 = 1.00000000
2項までの和:2/3 = 0.66666667
3項までの和:13/15 = 0.86666667
4項までの和:76/105 = 0.72380952
5項までの和:263/315 = 0.83492063
6項までの和:2578/3465 = 0.74401154
7項までの和:36979/45045 = 0.82093462
8項までの和:33976/45045 = 0.75426795
9項までの和:622637/765765 = 0.81309148
10項までの和:11064338/14549535 = 0.76045990
11項までの和:11757173/14549535 = 0.80807895
12項までの和:255865444/334639305 = 0.76460069

なお、π/4 = 0.785372...なので、かなり粗いが、一応π/4に収束しつつあることは窺われる。また、12項で打ち切ったのは、これ以上だと分母または分子が、32ビット整数で表記できる範囲を超えるので、仕方ない。これ以上を計算するには、64ビット整数を用いるよりない。もともとπ/4を計算することが目的なら、こんなまだるっこしく分数は計算しない。そうなら、1億項まででも簡単に計算できる。

月別 アーカイブ

このアーカイブについて

このページには、過去に書かれたブログ記事のうちコンピュータカテゴリに属しているものが含まれています。

次のカテゴリはスピリチュアルです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。