数学の最近のブログ記事

体の定義

user-pic
0

最近は、数学セミナーを読むのが遅れがちで、知らないうちに、最新号に対して何周遅れかになっている。それは、ある程度理解しながら読んでいるから、というと格好をつけているのであって、単に数学セミナーを読むのがハードなので、避けているだけだったりする。

そういう訳で、やっと数学セミナーの2018年10月号をやっと読んでいる次第なのだが、これは「体とはなにか」というタイトルで、要するに、代数的なシステムの一つである、体の特集号なのだった。

そこで、最初の三宅克哉氏の記事を辿って、体の定義を復習することにした。

すなわち、体とは、数学的な対象を集めた集合に対して、加法と乗法が与えられ、結合律、可換律、2種の単位元の存在、2種の逆元の存在、分配律を満たすもの、とあって、ああそうだよね、と思い出した。

ところが、可換律、と聞いて、そんなのあったっけ?と怪訝になった。すなわち、加法について可換群になるのはまあいいとして、乗法については単に群であればよく、別に可換群である必要はないのでは、と思ってしまった。

私は適当なことを言っているのではなく、例えば、有名な数学者の永田雅宣氏に、裳華房の「可換体論」という名著があって、これは要するに、乗法が可換な体のことを可換体と読んでいると思うのだが、あらゆる体が可換体なら、単に「体論」でよくないだろうか。

それとも、永田氏の「可換体論」は、代数方程式論の発展に関与するので、そういう場合特に、体論を可換体論と呼ぶのだろうか、といろいろ考えてみた。

で、考えた挙句、考えてても仕方ないではないか、永田氏の「可換体論」に直接当たってみようということになった。考えるまでもなく当然である。すると、その18ページに、次のような記載がある。引用してみる。

さて、体の定義をしよう。環Rにおいて、その乗法について、0以外の元がいずれも乗法について逆元をもつとき(従って、このとき、Rの0以外の元全体は群をなす)、Rは体であるという。群R-{0}を体Rの乗法群と呼ぶことがある。乗法が可換であれば可換体というのはもちろんである。可換でない体を非可換体、または斜体という。しかし本書では可換体を扱うのを主眼しするから、非可換体は登場しない。

なんだ、やはり非可換体もありえるんだ。なんとなく安心した次第である。

数学セミナーだったか、他の雑誌だったか忘れたが、今年の春頃に、小山信也著、黒川陽子構成「リーマン教授にインタビューする」青土社という本を見かけて、既にリーマン予想関係の本はいろいろ買ってあるのだが、多分屋上屋にはならないだろう、と買って読み始めたら、実に面白い。

それで、かなり間欠的であるが、半年近くかけてようやく読み終えたのだが、いやあ実に有意義な読書だったと、感慨に耽っているところである。

小山信也氏といえば、解析的数論やリーマン予想の研究で、日本を代表する数学者の一人だと認識しているが、構成の黒川陽子氏は誰なのだろう、と見てみると、早稲田大学文学部大学院卒の新進の劇作家で、実はかの黒川信重博士の娘さんらしい。

黒川信重氏といえば、小山氏の師匠格と私は思っているのだが、いくら親しい人の娘さんとはいえ、劇作家が数学書の構成に関与するのか、と怪訝に思ったところ、読んでみてその理由が分かった。

すなわち、ファウストではないが、小山氏がリーマンという、過去の偉大な数学者に、まるで精霊のように会いにいく、という設定で、まさに劇仕立てなのであった。その巧みな構成により、中身は本格的な数学書なのに、肩肘張らないで、読める。ただ、数学的な記述のところは、立ち返って、また考え直したりする必要があったりするが。

さて、この本は、オイラーの研究をベースにする、リーマンのオリジナルの研究に、小山氏が、その後の発展を考慮して、コメントする、という形式を採る。

最初は、背景として、オイラーのζ(2)の計算、すなわちそれが、πの二乗を6で割った値になるという、いわゆるバーゼル問題の解決から始まる。

次にいよいよ、リーマン教授の業績、すなわち、ゼータ関数の解析接続としての、2通りの積分表示が言及される。

その説明の中で、小山氏が、リーマン没後のカントールの対角線論法やp進体などの業績に言及し、リーマン教授が感心するシーンが面白い。

そこから、ゼータ関数の非自明零点の実部がすべて二分の一であろう、という、いわゆるリーマン予想の佳境に入る。

読み終えて、リーマン教授もさぞかし満足して、冥界に帰ることだろうと思った。というよりも、リーマン予想はあの世で、小山氏の示唆をヒントに研究を深め、それを思念として世の数学者に送ってくるであろう。

それにしても、黒川陽子さんの構成は実に巧みで、楽しく読めた。ウェブで黒川さんへのインタビューに応えて、劇作に関して、父が数学者ですから論理的に、みたいに言っている記事があり、妙に面白かった。

先日、とりあえず高次合成数を計算するプログラムを書いたのだが、あまりにナイーブというか計算に時間がかかるので、いろいろ調べたり、自分でも工夫してみて、計算速度を向上を図ってみた。以下にその工夫を列挙する。

  1. まず、ウェブで調べていて、なるほどと分かったのは、今の約数の数を計算する関数d(n)が問題ということである。それは、自然数Nを素因数分解を計算して、2a3b5cのようになったとすると、約数の数は、(a+1)(b+1)(c+1)になることを利用した。もともと素因数分解を計算するルーチンは使っていたので、これを流用するだけでよかった。これによる計算速度向上が最大であった。
  2. HCN(n)という関数で、nが奇数のときは、それが最大の約数の数を与えることはないので、nが偶数のときのみ、高次合成数のためのd(n)の比較を行うようにした。
  3. iという数が高次合成数かどうかを調べるループで、iがある値以上のとき、予め分かっている素因数分解の要素の値を利用して、例えば22×3×5×7で割り切れる数だけHCN(i)を適用することにした。これもかなり計算速度の向上に貢献するが、これはちょっとフェアでないというか後知恵をプログラムに組み込んでいるので、自分でもあまり感心できない。
ともかく以上の工夫を入れたプログラムのソースコードは、以下のとおりとなった。
#include <stdio.h>

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

int main(void)      {
	
 FILE *fi;
 int i,n;
 int DIV = 4*3*5*7;
 int c = 0;
 i = 0;
 int Max_Prime = 100000000;
 int P_size = 5800000; // 一億までの素数をカバー
 
  if ( (fi = fopen("sieve_n.txt","rt")) == NULL ) return -1;
  Prime = new int[P_size];
   while ( c < Max_Prime + 10 )   {  // 余分に取っておく
   fscanf(fi,"%d",&c);
   if ( c >= 2 )  Prime[i++] = c;
 }

 fclose(fi);
 
printf("<html><body><ol>");

for ( i = 2; i < 1000; i++ )
 if (HCN(i) == 1 )  {
   printf("<li>%d = ",i);
   PF(i);
 }
 
 for ( i = 1000; i < 60000; i++ )  {
  if ((i%DIV) != 0 ) continue;
   if (HCN(i) == 1 )  {
     printf("<li>%d = ",i);
      PF(i);
   }
 }

DIV *= 11;
 
 for ( i = 60000; i < 800000; i++ )  {
  if ((i%DIV) != 0 ) continue;
   if (HCN(i) == 1 )  {
     printf("<li>%d = ",i);
      PF(i);
   }
 }
 
 DIV *= 13;
 
 for ( i = 800000; i < 10000000; i++ )  {
  if ((i%DIV) != 0 ) continue;
   if (HCN(i) == 1 )  {
     printf("<li>%d = ",i);
      PF(i);
   }
 }
printf("</ol></body></html>\n");

delete[] Prime;
return 1;
}

int d(int n)       {
int N = n;
int ret = 1;
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++;
    }
    ret *= (j+1);
  }
  i++;
 }
 
 return ret;
}

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

for (int i = 1; i <= n-1; i++ )  {
  if ((i%2) == 1 ) continue;  // 奇数をスキップ
  if ( d(i) >= N ) return 0;
}
	
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;
}
また、計算結果は、以下のとおりである。なお、ナイーブな実装だと、1000000までの高次合成数の計算も、丸一日かけても終わらないのに、下記の実装だと10000000までの計算で、1時間くらいで終わる。まあまあのチューニングである。
  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
  29. 110880 = 25 32 51 71 111
  30. 166320 = 24 33 51 71 111
  31. 221760 = 26 32 51 71 111
  32. 277200 = 24 32 52 71 111
  33. 332640 = 25 33 51 71 111
  34. 498960 = 24 34 51 71 111
  35. 554400 = 25 32 52 71 111
  36. 665280 = 26 33 51 71 111
  37. 720720 = 24 32 51 71 111 131
  38. 1081080 = 23 33 51 71 111 131
  39. 1441440 = 25 32 51 71 111 131
  40. 2162160 = 24 33 51 71 111 131
  41. 2882880 = 26 32 51 71 111 131
  42. 3603600 = 24 32 52 71 111 131
  43. 4324320 = 25 33 51 71 111 131
  44. 6486480 = 24 34 51 71 111 131
  45. 7207200 = 25 32 52 71 111 131
  46. 8648640 = 26 33 51 71 111 131

フィボナッチ数の続きであるが、バックナンバーで数学セミナーの2018年8月号のフィボナッチ数の特集号を眺めていたら、青木美穂さんの「フィボナッチ数の素因数」という記事で、フィボナッチ数を素数pで法をとると、周期解になるというので、へぇ、そうなのかと興味をもった。

ちなみに、青木美穂さんは、日本評論社の「ゼータの現在」というシリーズの「p進ゼータ関数」の著者となっている人で、この本はまだ出ていなくて、出たら買うつもりだが、この本もまた、とてつもなく難しそうである。「フィボナッチ数の素因数」はそこまでは難解ではないが、それでも青木さんの、フィボナッチ数を素数pで法をとると、周期解になるという証明が何度読んでもどうしても理解できなくて、仕方ないので例によって、コンピュータに計算させて、お茶を濁すことにした。すなわち、素数pの法でフィボナッチ数が0になる周期をフィボナッチのpのランクと読んで、r(p)と表記する。

この計算自体は比較的簡単で、以下のようなプログラムを書いてみた。これは10000までの素数pの各々のランクを計算してみたものである。その結果は、こちらに示すとおりである。

青木さんの記事には、p ≡ ±1(mod 5)のとき、r(p)はp-1の約数、p ≡ ±2(mod 5)のとき、r(p)はp+1の約数、とあり、そのことは上記リストから確認できる。それにしても証明はどうやるんだろう。

また、特異的にr(p)が小さい値になる場合が気になる。例えば、
r(419) = 418
r(421) = 21
r(431) = 430とか、
r(9887) = 9888
r(9901) = 66
r(9907) = 9908とかである。p ≡ ±1(mod 5)とか、p ≡ ±2(mod 5)が続くあたりで出てくるのか気になる。

// フィボナッチ数の計算、多倍長版 - そのランク
#include "MLA5.h"  // 基数を100000とした
int IsPrime(int n);

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

int main(void)           {
 int i,j,k, bk;
 int *A, *B, *C;
 FILE *fo;
 int p;  // modをとるための素数
 int r;
 
 if ((fo = fopen("Fibonacci_r.txt","wt")) == NULL ) return -1;
 
 A = new int[n];
 B = new int[n];
 C = new int[n];
 
 for ( i = 2; i < 10000; i++ )    {
  if ( IsPrime(i) == 1 ) p = i;
  else continue;
  // 初期値の設定
   k = 0; // フィボナッチ数のインデックス
  CLEAR(n,A,0);
  CLEAR(n,B,1);
  
  k++;
  if ( MOD(n,A,p) == 0) bk = k;

  k++;
  if ( MOD(n,B,p) == 0) bk = k;
 
  do {
     COPY(n,C,A);
     MADD(n,C,B);  // C = A + B
     k++;
     r = MOD(n,C,p);
     COPY(n,A,B); // A ← B
     COPY(n,B,C); // B ← C
  } while ( r != 0 );
  fprintf(fo,"r(%d) = %d\n",p,k - bk);
 }
 
 delete[] A;
 delete[] B;
 delete[] C;

 fclose(fo);
 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;
}

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

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

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

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

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

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

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

そして、自分でも計算できそうな箇所の式変形を演習問題してノートに書いたり、プログラミングできそうな箇所は、実際に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);
}

月別 アーカイブ

このアーカイブについて

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

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

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

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