数学の最近のブログ記事

ブログで何度も採り上げている、黒川重信の「ラマヌジャンζの衝撃」であるが、私の本の読み方という意味でもこの本は、静かな、そして確かな衝撃だった。

だいたい私は、数学が好きといいつつ、具体的な数学の計算が苦手である。それでも、ファンが芸能人を遠くから(別に近くでもよいのだが)から眺めているように、ぼんやりと数学のことを想っている数楽者にすぎない。

すなわち、ただでさえ計算が苦手なのに、ラマヌジャンの数式ときたら、この世のものとも思えない複雑怪奇なもので、これはいかん、とあっさりページを閉じて、他の本に移った。

普通ならそれが、長いお別れ、というか、永遠の別れになるのだが、数日後に、またそのラマヌジャンの本をおそるおそる開いたのは、もしかしてシバ神?のはからいなのかもしれない。

ところが、あるページを眺めていたら、黒川氏の丁寧なヒント付きで、ある式変形が解説されていたので、ノートに元の式を書き写して、私なりに計算してみた。すると、その計算結果が本にあるとおりだったので、感激した。ラマヌジャンが象だとすると、象のしっぽの毛一本くらいは掴んだ気がした。

そんなことがあってから、他の本を読んでいて、気分転換に、このラマヌジャンの本を眺めるのが 一つの習慣になった。たいていは、しばらくただ眺めて、何も進展がなく空しくページを閉じるのだが、主観的には別に空しいとは思っておらず、眺めていろいろ妄想?しただけでも意味があったと考えた。

意味があった、というのは別に負け惜しみではなく、時間を隔てて、同じ箇所を何度も眺めていると、解らないなりにもある概念が耕されてくるのを感じる。

そして、自分でも計算できそうな箇所の式変形を演習問題してノートに書いたり、プログラミングできそうな箇所は、実際にC++で、コーティングしたりした。メビウス関数のコーティングがその一例であるが、数学そのものの理解ほどではないにしても、かなり苦労した。

そうやって、ラマヌジャンの本を読むのが習慣になると、本を読み終えることを目的にしない、読み進めることさえ意図しない、ただ眺めて何か考えることが意味があるのだ、という幻想をつくった。

この原則に従い、このラマヌジャンの本は読み終えないことにした。

これと同じような原則は、数学セミナーにも適用した。そして、気に入った号は保存して、折に触れて何度でも読み返すことにした。その文脈で一番気になっているバックナンバーは、整数の分割の号だったりする。その値を計算する漸化式をプログラミングしようと試みたりするのだが、なかなかうまくいかない。まあ気長に対応することにする。

こういう努力を続けると、思考の粒度が少し上がったような気がする。それは、数学の勉強を通じて得られたものであるので、数学の顕微鏡と呼ぶことにした。

数学の顕微鏡は、日常生活にはあまり関係ないのだが、仕事で特許請求の範囲を解析したところ、その緻密さが感心され、褒めてもらえた。普段から、仕事の要領のよさを誉められることはあっても、緻密さを誉められることはあまりないので、数学の顕微鏡路線も悪くないと自信をもった。

ただ、もう1ランク上の解像度はほしいところである。

高次合成数

user-pic
0

例によって、黒川重信氏の「ラマヌジャン ζの衝撃」現代数学社を眺めていたら、その第10章「深リーマン予想」のところに、高次合成数(highly composite number)のことが言及されていたので、私で分かる範囲で述べてみる。

最初は、高次合成数というけど、要するに自然数は、素数と、それ以外の合成数に二分されるわけで、高次合成数って何よ、と思ったのだが、よく読んだら高次合成数の定義があった。

すなわち、数nの約数の数をあらわす関数をd(n)とすると、d(1) = 1, d(2) = 2, d(3) = 2, d(4) = 3などとなる。すなわち、n = 1を除き、nは、1≦k≦nのような数kを考えたとき、k=1とk=nでは必ず割れるので、素数あってもそうでなくても、d(n)≧2となる。n = 4なら、これ以外にk = 2でも割れるので、d(4) = 3となる。

そこで、高次合成数とは、n = 1, 2, ..., N-1に対して、d(n) < d(N)となるNのことである。ここまで定義が与えられたら、高次合成数を計算するプログラムを書くのは難しくない。それを以下に示す。また、その計算結果とその素因数分解も示す。ここから、高次合成数が極めて少ないことも見て取れる。

ラマヌジャンの研究によれば、N = 2a(2)3a(3)...pa(p)とすると、a(2)≧a(3)≧...≧a(p)が成立し、N =4または36の場合を除き、a(p)=1となるという。確かに、下記の例では、それがすべて成立していることが見て取れるが、ラマヌジャンはそれをきちんと証明したわけで、相当凄い。

しかし、ラマヌジャンにとってこれは序の口で、d(n)を拡張して、リーマン予想に関係するσa(n)について研究していたというのだが、ここまでくると全く理解できないので、説明は省く。

#include <stdio.h>

int d(int n);
int HCN(int n);
int IsPrime(int n);
void PF(int n);  // 素因数分解
int *Prime;

int main(void)      {
 int i,n, P_max;
 i = 0;
  Prime = new int[9600];
  for ( n = 2; n < 100000; n++ )  {
	if ( IsPrime(n) == 1 )
	  Prime[i++] = n;
 }

 P_max = i;
 
printf("<html><body><ol>");

for ( i = 2; i < 100000; i++ )
 if (HCN(i) == 1 )  {
   printf("<li>%d = ",i);
   PF(i);
 }
printf("</ol></body></html>\n");

delete[] Prime;
return 1;
}

int d(int n)    {
int i;
int lim = n/2 + 1;
int r = 0;
if ( n == 1 ) return 1;
if ( n == 2 ) return 2;

for ( i = 1; i <= lim; i++ )
 if ((n%i) == 0 ) r++;

return r + 1;
}

int HCN(int n)  {
int N = d(n);

for (int i = 1; i <= n-1; i++ )
 if ( d(i) >= N ) return 0;
	
return 1;
}

int IsPrime(int n)  {
 int i = 2;
 
 if ( n == 2 ) return 1;
 
 while ( i*i <= n )  {
  if ( (n%i) == 0 )
   return 0;
  i++;
 }
 return 1;
}

void PF(int n)       {
int N = n;
int i = 0;
int j;
 while ( N > 1 )      {
  if (( N%Prime[i]) == 0 )  {
    j = 1;
    N /= Prime[i];
    while ( ( N%Prime[i]) == 0 ) {
	  N /= Prime[i];
      j++;
    }
    printf("%d<sup>%d</sup> ",Prime[i],j);
  }
  i++;
 }
 printf("<br>\n");
 return;
}

  1. 2 = 21
  2. 4 = 22
  3. 6 = 21 31
  4. 12 = 22 31
  5. 24 = 23 31
  6. 36 = 22 32
  7. 48 = 24 31
  8. 60 = 22 31 51
  9. 120 = 23 31 51
  10. 180 = 22 32 51
  11. 240 = 24 31 51
  12. 360 = 23 32 51
  13. 720 = 24 32 51
  14. 840 = 23 31 51 71
  15. 1260 = 22 32 51 71
  16. 1680 = 24 31 51 71
  17. 2520 = 23 32 51 71
  18. 5040 = 24 32 51 71
  19. 7560 = 23 33 51 71
  20. 10080 = 25 32 51 71
  21. 15120 = 24 33 51 71
  22. 20160 = 26 32 51 71
  23. 25200 = 24 32 52 71
  24. 27720 = 23 32 51 71 111
  25. 45360 = 24 34 51 71
  26. 50400 = 25 32 52 71
  27. 55440 = 24 32 51 71 111
  28. 83160 = 23 33 51 71 111

数学セミナーの2018年8月号は、フィボナッチ数の特集号で、数論好きの私は興味をもったが、暑さで思考力がまとまらず、しばらく放置していた。

それで少し涼しくなってやっとその特集号を読み始めたのであるが、その前提として、とりあえずフィボナッチ数を、以前作成した多倍長ルーチンを使って計算するプログラムを書いてみることにした。

すると、別の自分が囁くには、そんなの単なる足し算だけで、コラッツの問題と比べても単純すぎないか、というのである。そんなの分かっているけど、とりあえず書くのだ、どうせ鎧袖一触だ、と応えたのだが、やってみると私のスキルでは案外大変だった。まったくもう、いつもそうである。

そういえば、私はフィボナッチ数は要するに、Fn+2 = Fn+1 + Fnのことだけだと思っていたら、特集号を読んで改めて知ったのだが、F0 = 0, F1 = 1もその要件だという。ええっ、とそんなことさえ知らない自分の無知を恥じた。

そこで書いたプログラムは以下のとおりである。下記のように、ライブラリを使用しているせいもあるが、もともとアルゴリズムがシンプルなので、ソースコードはコンパクトにまとまっている。実は鎧袖一触でなかったのは、下記の変数Cの必要性と、その使い方に多少悩んだからである。

また、MLA5.hというライブラリは、以前書いた多倍長計算ライブラリMLA2.hの基数を100000にして、多少DISPの変形関数を追加した程度のものである。なお、基数を100000をすると、実質的に多倍長同士の掛け算が使えないが、この場合、足し算しか使わないので問題ない。

// フィボナッチ数の計算、多倍長版
#include "MLA5.h"  // 基数を100000とした

int n = 2000; // 10000桁くらいまでをカバー

int main(void)           {
 int i,k;
 int *A, *B, *C;
 FILE *fo;
 
 if ((fo = fopen("Fibonacci.txt","wt")) == NULL ) return -1;
 
 A = new int[n];
 B = new int[n];
 C = new int[n];
 
 k = 0; // フィボナッチ数のインデックス
 // 初期値の設定
 CLEAR(n,A,0);
 CLEAR(n,B,1);
 
 fprintf(fo,"%d:",k++);
 DISPF(fo,n,A);
 
 fprintf(fo,"%d:",k++);
 DISPF(fo,n,B);
 
 for ( i = 0; i < 2000; i++ )   {
	COPY(n,C,A);
	MADD(n,C,B);  // C = A + B
    fprintf(fo,"%d:(桁数=%d) ",k++,DGN(n,C));
    DISPF(fo,n,C);
    COPY(n,A,B); // A ← B
    COPY(n,B,C); // B ← C
 }
 
 delete[] A;
 delete[] B;
 delete[] C;

 fclose(fo);
 return 1;
}

これの結果は、Fibonacci.txtに書き出されているが、その内容は、こちらに示すとおりである。この計算は、2000項までの計算なのに、あっという間に終わるので、なんか異常終了したと勘違いしたほどである。

その内容からみて取れるように、フィボナッチ数の桁数の増加は緩慢で、2nよりもかなり遅い。

フィボナッチ数の陽の計算式には、((1+√5)/2)nのような式が現れて、(1+√5)/2 = 1.618... なので、フィボナッチ数は1.618n程度の増え方だと思われる。

とりあえず、フィボナッチ数を計算するベースを作ったので、次は数学セミナーの特集記事に基づき、個別にみていくことにする。

解析接続

user-pic
0

初等関数に書いたように、数学セミナーの2018年6月号で「複素関数の質問箱」今更のように複素関数の初歩を勉強しているのであるが、その勉強のとりあえずの目標は、解析接続の意味を少しでも理解することである。

そこで、「複素関数の質問箱」の最後の小山信也氏の「解析接続の意味と意義について」の記事を私が分かる範囲でまとめてみる。

すると、1-s+s2-s3+... = 1/(1+s)という式が言及される。ここでsを複素数とし、この左辺をf(s)と置き、右辺をg(s)と置くと、あら不思議、f(s)は|s| < 1でしか定義されないのに、g(s)はs = -1を除く全複素平面で定義されてしまうのである。

そこで、f(s)の定義域をD、g(s)の定義域をEとすると、明らかにD ⊂ Eで、Dではf(s)とg(s)が一致する。そこで次のような「一致の定理」が、複素関数論にある。

領域Dで値が等しい2つの正則関数f,gがあり、gがより広い領域Eで定義されているとする。もしfがE上で正則関数として定義されるなら、E上でもgとfは一致する。

一致の定理は、例えば私がもっている「複素解析入門」阪井章著、新曜社によれば、2つの正則関数f,gが領域Dの加算無限の点列で一致するとき、f,gは領域Dで一致する、とある。これって、上の定理と等価なのだろうか。

あと、上の定理で、領域Eの|s|≧1の領域で、f(s)はどうするの、という疑問がある。もし、そこは拙者が参ります、と急にg(s)が代理で出てくるとなると、そんなのEでfとgが一致するといえるのか、それとも領域Eの|s|≧1の領域ではf(s)は最早正則ではないのかとか、素人質問が沢山でてきて困る。

しかし、小山氏の説明によると、やはりg(s)が出っ張るらしい。納得できないが、一応納得したことにしておく。ともかく、このようなgをfの解析接続という。実はEでgが出っ張るのでなく、gこそが真の姿で、Dでのfが劣化したものとみるのが妥当なようである。

さて、リーマンのゼータ関数は、以下のように定義される。

リーマンの主要な業績の一つは、これの解析接続を与えたことであり、それは例えば以下のようなものである。

リーマンはこれ以外にあと二つ、ゼータ関数の解析接続を与えた。ところが、リーマンのゼータ関数の分母が、1,2,3,...でなく、全素数に亘る、まがい物のゼータ関数をつくってみると、これには解析接続が存在しないのだという。

よって、解析接続が存在するということは、何かしら数学的な深淵につながるものらしい。このところは追々、勉強してみることにする。

数値積分

user-pic
0

発散級数の和(その2)のところで、


の積分の数値計算を、台形公式により粗く、0.596364028767647ととりあえず計算してみたのだが、その後、黒川信重「ラマヌジャン ζの衝撃」を眺めていて、その88ページで以下の記載をみつけて唖然とした。私が計算した値と、誤差を考慮してもなにか本質的に違う。
オイラーは、"1-1!+2!-3!+4!-5!+..." = 0.59637164と求めています(全集の編者は0.59637255と修正)

それで、Wikiで、ボレルの総和法の記事を調べて、上記の値が0.59634736232319407...とあって、何が正しい値に近いのかいよいよわからなくなった。

像てい

そこで、手許にある、数値解析入門[増訂版]、山本哲朗著、サイエンス社を参考にして、台形公式より精度がよいとされるシンプソンの3/8公式を用いて上記積分の数値計算を再度行うことにした。

シンプソンの3/8公式では、以下のようにして、関数f(x)の数値計算を行う。
S = 3h(f(x0) + 3f(x1) + 3f(x2) + f(x3)/8 + 3h(f(x3) + 3f(x4) + 3f(x5) + f(x6)/8 +
...+ 3h(f(xN-3) + 3f(xN-2) + 3f(xN-1) + f(xN)/8

これのソースコードは、以下のとおりであり、また計算結果は、0.59634721063974127642で、0.596347までしか正しくない。ただ、「ラマヌジャン ζの衝撃」にあった上記数字は、おそらく正しくないとはいえる。それにしても、シンプソンの公式といいつつ、台形公式よりも必ずしも精度が上がらず、仕方なく刻み目hを小さくすると異様に計算時間がかかってしまうのだった。また、蛇足であるがいくつかの注意点も記す:

  • 積分範囲Wを仮にセットしたものの、実際は0から∞までの積分であり、計算ループの脱出条件を、一回前のループの計算値との差が、10-23以内とした。これを小さくするほど精度は上がる。
  • 精度により効いてくるのは、積分範囲の刻み目hの幅であるが、これをあまりに小さくすると計算時間が膨大になってしまう。そこでとりあえず、h = 10-10とした。計算時間は、だいたい30分くらいだった。
  • 上記のような小さい数のセットのため、TH_set(int n)という関数を用意した。これは、10-nの実数値を返す。
// シンプソンの3/8公式による積分計算

#include <stdio.h>
#include <math.h>

double f(double x);
double TH_set(int n);

int main()        {

int i = 0;
double W = 100.0;  // 積分範囲
double S = 0.0; // 積分値
double b = 0.0;  // 変数の値
double Sx; // 1つ前の値
double Threshold  = TH_set(23);  // 差分の閾値 10のマイナス23乗にセット
double h =  TH_set(10);  // 刻み幅10のマイナス10乗にセット


 S += 3.0*h*(f(b) + 3.0*f(b+h) + 3.0*f(b + 2.0*h) + f(b + 3.0*h))/8.0;
 b += 3.0*h;

do                      {
 Sx = S;
 S += 3.0*h*(f(b) + 3.0*f(b+h) + 3.0*f(b + 2.0*h) + f(b + 3.0*h))/8.0;
 b += 3.0*h;
 
  i++;
//  if ( i%100000 == 0 ) { printf("%6.20f\t%5.20f\n",S, S - Sx); i = 0; }
 
  if ( S - Sx < Threshold ) break;

} while ( b + 2*h < W );


 printf("%6.20f\n",S);
 return 1;
}

double f(double x)  {
 return exp(-x)/(1.0 + x);
}

double TH_set(int n) {
 int i;
 double TH = 1.0;

 for (i = 0; i < n; i++)  {
  TH /= 10.0;
 }
 
 return TH;

}

黒川信重「ラマヌジャン ζの衝撃」の発散級数の和の続きで、次のような式があった。

今更驚かないが、これもトリッキーで、左辺は普通に考えたら左辺は-∞になるはずであるが、右辺は意味不明な小数になっている。そこで、「ラマヌジャン ζの衝撃」に従い、説明を述べる。

まず、次のような関数Fnを考える。

そして、F0は、下記の計算で1になる。

また、F1も、下記の計算で1になる。

以上から帰納的に、下記の式が成立する。

よって、最初の式は、以下のように、Fnを使って書きあらわされる。このような和の式は、ボレルの総和法と呼ばれている。ラマヌジャンが大変好んだ式でもある。

そこで問題は、最後の積分の式であるが、これは台形公式により、数値積分してみる。すなわち、積分範囲は、[0,∞)であるが、exp(-x)/(1.0 + x)の分子が極めて急速に減衰するので、[0,100)で考え、これを10000刻みする。
これは直ちに結果が得られて、0.596364028767647となるので、一応所望の値である。

#include <stdio.h>
#include <math.h>

double f(double x);

int main()        {

double W = 100.0;  // 積分範囲
double h = W/10000.0;  // 刻み幅
double S = 0.0; // 積分値
double b = 0.0;  // 変数の値

// 普通に台形公式を使う

 S = h*f(b)/2.0;
 b += h;

while ( b < W - h )   {
  S += h*f(b);
  b += h;
}

 S += h*f(b)/2.0;
 
 printf("%6.15f\n",S);
 
 return 1;
}

double f(double x)  {
 return exp(-x)/(1.0 + x);
}

初等関数

user-pic
0

数学セミナーの2018年6月号は、「複素関数の質問箱」という特集で、最近リーマン予想のごく初歩のところから勉強し始めた私には願ったりの特集である。なぜなら、リーマン予想は、リーマンのゼータ関数の解析接続を前提とした理論なので、複素関数論を理解していることが必須であるところ、私は複素関数論なんてほとんどまともに勉強したことがないからである。

その「複素関数の質問箱」は、以下の七編からなる。

  1. 複素数ってそもそも何なの?
  2. 複素関数論中の実関数
  3. 複素微分とはなんですか、ふつうの微分とはどう違うのですか
  4. 複素積分とはなんですか、ふつうの積分とはどう違うのですか
  5. 正則関数の"綺麗"な性質はなぜ
  6. 複素対数とは何か/パラドックスをこえて
  7. 解析接続の意味と意義について

その「複素関数論中の実関数」と題する、中村弥生氏の記事を読んでいるうちに、以下のような初等関数の定義が書かれているのをみて、新鮮な驚きを感じた。

さて、代数関数、指数関数、三角関数、対数関数、およぴそれらの線形結合、積、商、合成関数、逆関数をとる操作によって得られる関数を初等関数といいます。初等関数のうち、特に代数関数でないものを初等超越関数といいます。

私は初等関数というのは、高校までで習う関数くらいに曖昧に理解していたので、この明快な定義に感動さえした。すなわち、代数関数、指数関数、三角関数、対数関数に積、商、合成関数、逆関数をとる操作を繰り返していくと、見かけ上相当に複雑な関数が得られる。それでも、それらは初等関数と呼ばれるのだな、と思った。

で、そこからさらに気づいたことがある。微分操作可能性である。それは、解析的な意味での微分可能性とは違う。要するに、決まった公式により式変形できるのである。なので、別の言い方をすると、微分操作的式変形を許すなら、初等関数なのである。

ところが初等関数を含む、常微分方程式、偏微分方程式、積分となると急に様子が変わってくる。それらの解は最早、一般的には初等関数とはならない。例えばアーベルは、代数関数の積分を考察して、楕円関数論を切り開いた。

厳密な定義は知らないが、初等関数でない関数の一部、すなわち理工学に応用される関数は特殊関数と呼ばれるのだと思う。例えば手許にある、井上正雄著「応用関数論」共立全書、昭和29年刊には、楕円関数、Γ関数、ルジャンドル関数、ベッセル関数が論じられており、これらが特殊関数の典型なのだろう。

この「応用関数論」で、最初の方に複素関数論の基礎の章がある。すなわち、関数というものは複素関数論で基礎づけられなくてはならず、なので「複素関数論中の実関数」に初等関数の定義も出てくるのだな、と思った。

黒川信重「ラマヌジャン ζの衝撃」現代数学社の発散級数の和のところを眺めていて、これは決して初めてみたわけではないが、何度みてもトリッキーである。典型的には、以下の式である。

普通に考えたら、左辺は-∞になるので、ありえないのだが、ともかくこの級数は、アーベルの総和法という技法を使って、以下のような多項式の級数に変換される。それにしても、アーベル大先生が、こんなまやかし?に加担していたとは(笑)

プロの数学者には、この式変形は自明なのだろうが、私のような弱小アマチュアには理解できないので、自分にもわかるように段階を追って行間を埋めてみた。まず、以下のように、1+xの逆数の級数展開を得る。

すると、(1+x)2の逆数の級数展開は、以下のとおりとなり、さらに変形される。

最後の式変形は、xkの係数に着目すると、(左側の括弧からのn=0の項)×(右側の括弧からのn=kの項)+(左側の括弧からのn=1の項)×(右側の括弧からのn=k-1の項)+・・・+(左側の括弧からのn=kの項)×(右側の括弧からのn=0の項)となって、そのような項の各々はどれも(-1)kであって、この項の数が(k+1)個あることに基づく。

よって、以下の式変形が成り立つ。この式の左辺は確かに、x → 1-0の極限で、1/4になる。

と、一応式は追えたものの、トリックの種明かしは分からないままである。

ところが、上記の式の応用で、以下の、よりトリッキーな式が導き出される。

これの導出は、まずc= 1 + 2 + 3 + 4 + ・・・とおく。 そして、c - 4cを以下のようにつくる。4cを、ずらして一つ飛ばしで引くのがミソである。

c = 1 + 2 + 3 + 4 + ・・・
4c =    4     + 8 + ・・・

すると、c - 4c = -3c = 1 - 2 + 3 - 4 + ・・・
なので、c = 1 + 2 + 3 + 4 + ・・・は、1 - 2 + 3 - 4 + ・・・の結果を-3で割ったものになるという次第である。

これを単なる数学的トリックと嗤ってはいけない。なぜなら、例えばこれらの式は、超弦理論の空間次元にも関与してくるのである。もしかしてゼータ関数の解析接続などに関係があるのかもしれないが、私は不勉強でそのあたりの意味が全くわからない。他のより複雑な発散級数の和と併せて、勉強してみる。

数学の本

user-pic
0

数学の本というのはなんともベタなタイトルだが、それは数学セミナー2018年5月号が「数学の本」特集だからである。それが代数や解析などの専門書の特集なら、なにを今更なのだが、今回は数学の一般書、啓蒙書、雑誌の特集なのだった。それで私は、例によって本ノートに、気に入った本をメモしていくことにした。

すると、いきなりガーンとショックを受けたことがあった。それは、フェルマーの定理に関するベストセラー、サイモン・シン、青木薫訳「フェルマーの最終定理」新潮文庫をみて、私がまだこの本を読んでいないことを確認したからである。

私はそもそも数学者でなく、せいせい数楽者なのだが、いかなる意味でも数学の一般書を軽視する立場にはない。また私は、ここにもリストされていて、リーマン予想に関する一般書の名著とされるマーカス・デュ・ソートイ、富永星訳「素数の音楽」新潮文庫をかなり前に読んで感動したことがあるので、サイモン・シン「フェルマーの最終定理」を読まない理由を見つけることは困難である。

にも拘わらず「フェルマーの最終定理」を読まないできたのは、そのことについて書かれた本を何冊も読んできたので、その背景についてはもうだいたい知っているだろうという思い込みがあったようである。しかし、冷静に顧みて私は、ワイルズの私生活をどれくらい知っているだろうか。ほとんど知りはしないのである。それで、なるべく早めにサイモン・シン「フェルマーの最終定理」を読むことにした。

あとは、リストされている本でそのうち読みたいと思ったものをリストしてみる。

  • G.H.ハーディ、 C.P.スノー「ある数学者の生涯と弁明 」(シュプリンガー数学クラブ) 柳生 孝昭 (訳)
    ハーディは、ラマヌジャンにも関与していて、興味深い。
  • マーカス・デュ・ソートイ、富永星訳「シンメトリーの地図帳」新潮文庫
    19世紀のパリ、決闘の果てに夭折した天才ガロアは新しい数学の言語を生み出していた。古代ギリシャから続く対称性探求の旅に挑む数学界の探検家たちは、その言葉を駆使して "シンメトリーの素数"を網羅した「地図帳」を完成させる。最後に発見されたのは19万6883次元の空間に潜む巨大結晶「モンスター」だった――。『素数の音楽』の著者と旅する、美しくも奇妙な数学の世界。
    という内容紹介をみても私には想像がつかない。面白そうである。
  • エドワード・フレンケル、青木薫訳「数学の大統一に挑む」文藝春秋
    これは確か、ラングランズ・プログラムの話だったか。そのうち読もうと思っていて、忘れてしまっていた。
  • 森口 繁一、応用数学夜話 (ちくま学芸文庫)
    私は、コンピュータの計算が好きで、なので森口 繁一のファンである。なので、これも私には必読なのだった。

最大の素数

user-pic
0

素数が無数にあるということは太古の昔から既知であるので、最大の素数なんて、ない。それで、ここでいう最大の素数というのは、現時点で素数であることが知られている最大の数という意味である。

それは、GIMPSによれば、277232917-1だということである。私は、Lucas-Lehmer判定法を自分のパソコン上で実装してみるというささやかな努力を通じて、それがいかに大変かを認識し、自分でも新たな大きい素数を探究してみようという努力を諦めた。

そこでせめて、277232917-1を計算してみようという、相当に妥協した考えに至った。しかし、ともう一つの自分がつぶやく。そんなの、単なる力業で、なんの工夫もいらないのではないか、と。

で、とりあえず去年作成した多倍長ライブラリを参照してまず考えたのは、int MUL(int n, int *a_num, int a)をナイーブに使用して、2の77232917乗を計算するものである。これの前準備として用意する多倍長計算のための整数配列の長さを決める必要がある。2の77232917乗は桁数としては、23249425桁であり、Radix = 100000とすると、必要な配列数は、これを5で割って、4649885なので、余裕をみて4650000個の配列とする。そして、2を1000回掛けるというナイーブな計算をさせてみると、処理時間は約48秒だった。すると、2を77232917回掛けると、見積の処理時間は、1029時間、すなわちなんと約43日もかかってしまう。

これはいくらなんでも、と思いしばし思考して、そうだ、2でなく2の14乗すなわち、16384ずつ掛ければよいのだと気付いた。これでもなんとか100000進法の枠組みに収まっている。しかも、2でなく2の14乗を1000回掛けても、処理時間はほとんど同じで、これにより処理時間を43日の1/14すなわち、3日程度に短縮できる。なお、欲を出して1000000進法にし、2の15乗を掛けようとすると、4バイト整数の32ビットの枠組みを壊してしまうので、無理である。

一方、77232917 = 5516636×14 + 13なので、2の14乗を5516636回掛け、次に2の13乗を掛け、最後に1を引くことで求めるメルセンヌ素数が得られる。それをプログラミングしたのが以下のとおりである。

といっても、まだ実行していないので、計算結果は得られていない。

#include <stdio.h>
#include <time.h>

int Radix = 100000;
int n = 4650000;
int *a_num;
int CLEAR(int n, int *a_num, int a);
int DISP(FILE *fo, int n, int *a_num);
int MUL(int n, int *a_num, int a);
int SUB(int n, int *a_num, int a);

int main()       {
int i;
clock_t t_begin, t_end;
FILE *fo;

if ((fo = fopen("Max_Prime.txt","wt")) == NULL )
 return -1;

t_begin = clock();  // 計測開始

a_num = new int[n];

// 277232917 - 1の計算
// 77232917 = 5516636×14 + 13

CLEAR(n,a_num,1);
for ( i = 1; i <= 5516636; i++ )
 MUL(n, a_num, 16384); // 2の14乗を5516636回掛ける
 
MUL(n, a_num, 8192); // 2の13乗を掛ける
 
SUB(n,a_num,1);   // 1を引く
 
DISP(fo, n, a_num);

t_end = clock();  // 計測終了

printf("Elapsed time : %7.3f秒\n",(double) (t_end - t_begin)/1000);

delete[] a_num;
return 1;
}

int CLEAR(int n, int *a_num, int a)   {
int i;
for ( i = 0; i < n; i++ )  {
    a_num[i] = 0;	
}
a_num[0] = a;
return 1;
}

int DISP(FILE *fo, int n, int *a_num)   {

int i;
int disp = 0;
int first = 1;

for ( i = n - 1; i >= 0; i-- )     {
 if ( a_num[i] != 0 )  disp = 1;
 if ( disp == 1 )     {
	if ( first == 1 )  {
		fprintf(fo,"%d",a_num[i]);
		first = 0;
    } else             {
		fprintf(fo,"%05d",a_num[i]);
	}
  }
}

if ( disp == 0 ) fprintf(fo,"0");

fprintf(fo,"\n");	

fclose(fo);

return 1;
}

int MUL(int n, int *a_num, int a)     {
int i,j;
	
  for ( i = 0; i < n; i++ )
   a_num[i] = (a * a_num[i]);  // a倍する
  // 桁揃え
  for ( j = 0; j < n - 1; j++ )  {
	a_num[j+1] += (a_num[j] / Radix); // 桁上がり
	a_num[j] = (a_num[j] % Radix);  // 10万進法	
  }	

return 1;

}

int SUB(int n, int *a_num, int a)      {
int i,j;

a_num[0] -= a;

 for (i = 0; i < n-1; i++ )  {
   if ( a_num[i] < 0 )        {
	a_num[i+1]--;
	a_num[i] += Radix;
   }
 }

  // 桁揃え
  for ( j = 0; j < n - 1; j++ )  {
	a_num[j+1] += (a_num[j] / Radix); // 桁上がり
	a_num[j] = (a_num[j] % Radix);  // 10万進法	
  }

return 1;
}

月別 アーカイブ

このアーカイブについて

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

前のカテゴリは教育です。

次のカテゴリは日常です。

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