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

パソコンを使っていると、例えばメモリ・リークにより使えるメモリの領域が減ってきて、リブートしなくてはならない、という状況がよくある。

Windows 10に乗り換えてからは、流石にあまりメモリ・リークが起きなくなって、リブートの必要性は下がったが、それでも、昔からの習慣により、つい週に一度くらいはリブートしてしまう。

ところが、Windows 10になって、よくわからないことが起きた。すなわち、リブートする前は、C++ Builder XE4できちんとコンパイル&リンクできていたし、タスクバーにもきちんとピン止めされたアイコンが表示されていたのに、リブートするとC++ Builder XE4でコンパイル&リンクすると、LME288というエラー・コードのリンク・エラーが起きた。

仕方ないなあと、いったんパソコンをシャットダウンして完全に終わり、それからパソコンを起動するという、いわゆるコールド・スタートさせると、やはりLME288のエラーになる。

まったくもう、と普通にリブートしたら、今度はLME288のエラーは起きなくなったが、今度はタスクバーからピン止めされたアイコンが消えてしまった。

このように、再起動させるたびに異なる挙動をするパソコンは今まで経験したことがないので、流石Windows 10と感心した(なわけないだろ(怒))。

それで毎月一度のWindows Updateでこのエラーが修正されているかと毎回期待するのだが、なかなか修正されない。まあタスクバーからピン止めされたアイコンが消える、というのは明らかなバグだが、LME288のエラーというのは、マイクロソフトにしてみれば、そんなもん、エンバカデロさんのバグでっしゃろ、しらんがな、というところだろう。

ただ、こういうのを格好つけてステートフル・ブートというのは、私が思いついた言い方だが、明らかに妥当ではない。強いていうなら、モンテカルロ・ブートか。

惑星の軌道

user-pic
0

もう30年以上前、20台後半でまだ独身の頃、シャープのMZという8ビット・パソコンを買い、BASICでプログラムを書いて、夜な夜な楽しく遊んでいたのだが、そのとき書いていたプログラムの中に、惑星の軌道計算があった。

惑星の軌道計算といっても、なんのことはない、ニュートンの法則の逆二乗則で力学の運動方程式を記述し、それを例によってルンゲ・クッタで数値計算するという素朴なものである。

それでも、惑星2つが引き合うという連星の二体問題を数値計算してプロットした結果を知り合いに見せたら、辛口で滅多に他人を褒めないその知り合いも、あまりこういうのは見たことがないので面白いと褒めてくれたので、うれしくなったのを覚えている。

それで気を良くして、ポアンカレによれば積分が存在しない、すなわち解析的に解けないといわれている三体問題をプロットしようとしたら、プログラムにバグがあるようでどうもうまくいかない。それでその後諦めていたのだが、最近、常微分方程式の数値解法に再び馴染んできたのをきっかけに、また再チャレンジしてみたくなった。

といっても、いきなり三体問題は荷が重いので、一体問題⇒二体問題⇒三体問題と進めていくことにした。一体問題というのはあまり聞きなれないが、私が意図したのは、中心に太陽があって、その周りを単一の惑星が周回する、というものである。しかしウェブで調べてみると、一体問題とは、宇宙にたった一つの星がある場合のことらしくて、するとニュートン力学のパラダイムでは、静止しているか等速直線運動するだけなので、数値計算もなにもあったものではない。

で、当初予定していたこのブログのタイトルを、一体問題から惑星の軌道という曖昧な言い方に変えて、中心に太陽があって、その周りを単一の惑星が周回する、という場合の数値計算をしてみることにした。

さて、それは要するに、太陽の周りに惑星が楕円軌道をとるだけだから楽勝と思っていたら、なかなか綾があった。すなわち、惑星を太陽の引力を受けて運動するようにするには、惑星の太陽からの距離と、太陽に対して相対的な速度を与えるのだが、ヨハン・ベルヌーイは、その場合惑星がとりえる軌道は、楕円、放物線、双曲線のどれかであることを見出した。

要するに、注意深く初期速度と位置を与えないと、軌道は放物線または双曲線となって、無限遠に「いっちゃう」のである。それで最初、適当に初期速度と位置与えて、軌道が発散して何度も困ってしまった。

さらに、自明なのでヨハン・ベルヌーイが指摘しなかったことがある。それはあまりに太陽の近くに初期位置を配置するか、初期速度が小さすぎると、惑星が太陽に引っ張られすぎて激突してしまうのである。これはこれでゲームセットである。

そうやってなんとか楕円軌道を得たのが、下記のプログラムで、(x,y,u,v) = (3.0, 2.0, 0.43. 0.01)の場合である。ここで、太陽を原点としたとき、xは惑星の初期x座標、yは惑星の初期y座標、uは初期速度のx方向成分、uは初期速度のy方向成分である。

その軌道は、以下のとおりだった。なお、下手なマンガみたいだが、太陽を赤い小円で示した。

これだと近日点が近すぎて、もし惑星が地球だと人類が焼け死んで滅びるかもしれない。そこで、妥当な距離の近日点になるように、初期位置と初期速度を、(x,y,u,v) = (3.0, 0.0, -0.1. 0.5)に変更してみた。すると軌道は、以下のとおりとなった。

このプログラムのソースコードを例によって以下に示す。それにしても、惑星や太陽の質量や重力定数やその他物理単位の次元を全く無視して適当に微分方程式を解くってどうよ、とつい自己突っ込みするのだった。

//---------------------------------------------------------------------------

#include <vcl.h>
#include <math.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
double fx(double x, double y);
double fy(double x, double y);
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Close1Click(TObject *Sender)
{

 Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Run1Click(TObject *Sender)
{
const double H = 0.00001;  // 刻み
double x,y,u,v;     // x,yは座標値で、u,vはその速度のxとy成分
double a1,a2,a3,a4;  // dx
double b1,b2,b3,b4;  // dy
double c1,c2,c3,c4;  // du
double d1,d2,d3,d4;  // dv
double px, py;
int N = 0;           // 繰り返し回数

// 位置と速度の初期値
x = 3.0;  y = 0.0; u = -0.1; v = 0.5;

// 原点を描く
Canvas->Brush->Color = clRed;
Canvas->Ellipse(280,280,300,300);

while ( N++ < 10000000 )        {
 a1 = u;
 b1 = v;
 c1 = -fx(x,y);
 d1 = -fy(x,y);
 a2 = u + H*c1/2.0;
 b2 = v + H*d1/2.0;
 c2 = -fx(x+H*a1/2.0,y+H*b1/2.0);
 d2 = -fy(x+H*a1/2.0,y+H*b1/2.0);
 a3 = u + H*c2/2.0;
 b3 = v + H*d2/2.0;
 c3 = -fx(x+H*a2/2.0,y+H*b2/2.0);
 d3 = -fy(x+H*a2/2.0,y+H*b2/2.0);
 a4 = u + H*c3;
 b4 = v + H*d3;
 c4 = -fx(x+H*a3,y+H*b3);
 d4 = -fy(x+H*a3,y+H*b3);
 x += H*(a1 + 2.0*a2 + 2.0*a3 + a4)/6.0;
 y += H*(b1 + 2.0*b2 + 2.0*b3 + b4)/6.0;
 u += H*(c1 + 2.0*c2 + 2.0*c3 + c4)/6.0;
 v += H*(d1 + 2.0*d2 + 2.0*d3 + d4)/6.0;
 px = 290.0 + 70.0*x;
 py = 290.0 - 70.0*y;
 Canvas->Pixels[px][py] = clBlack;
}
}
//---------------------------------------------------------------------------
double fx(double x, double y) {
 double r,R;
 r = x*x + y*y;
 R = r * sqrt(r);
 return x/R;
}

double fy(double x, double y) {
 double r,R;
 r = x*x + y*y;
 R = r * sqrt(r);
 return y/R;
}

じつは、ストレンジ・アトラクタの微分方程式を数値的に描いてみたいとおもったモチベーションは、「カオスとフラクタル」講談社ブルーバックスでたまたま見かけた煙たなびくみたいな画像に一目ぼれしたからだが、いろいろ工夫してみてもどうしても意図する画像にならないでいろいろ調べているとき、ポアンカレ断面という用語を知った。

そこでポアンカレ断面とは何か調べてみると、どうも微分方程式の解の軌道内に超平面を配置し、その超平面と軌道の交点をプロットする、というものであるらしい。ストレンジ・アトラクタの場合、cos tというtに関する周期項があるので、t = 0, 2π, ... 2nπ,...を超平面として、軌道の交点をプロットしてみることにした。

ところがたまたま、以下のようなダフィンの方程式をウェブ上でみつけた。

ちなみにストレンジ・アトラクタの微分方程式を再掲すると、以下のとおりである。

この2つの微分方程式の最大の違いは、xという項の存在で、これは本質的である。ともかく、なんとなくであるが、ダフィンの方程式に関して、ポアンカレ断面を求めてみることにした。なおこのとき、ウェブ上情報の示唆により、δ=0.20、γ=0.30、ω=1.0ととった。

どうやってポアンカレ断面を求めるか、特に文献もウェブ上の情報もみつからなかったので、独自に考えてみた。

まず、角度θの象限を確認する。

  • 第1象限: cosθ > 0 且つsinθ > 0
  • 第2象限: cosθ < 0 且つsinθ > 0
  • 第3象限: cosθ < 0 且つsinθ < 0
  • 第4象限: cosθ > 0 且つsinθ < 0

そして、第4象限から第1象限に移るとき、あるいは第1象限から第4象限に移るとき、θ = 0の点を通るので、ポアンカレ断面としてプロットすることにした。その一例を示すと、以下のとおりである。

これは、関連の書籍でみるポアンカレ断面とだいたい似ているので、一応正しいとみなすことにした。そこで、ストレンジ・アトラクタの場合も同様にしてポアンカレ断面をプロットしてみると、以下のとおりである。ちなみに、このストレンジ・アトラクタでは、k = 1.0、B = 12.0としてみた。

最後にダフィンの方程式のためのプログラムのソースコードを以下に示す。ストレンジ・アトラクタの場合は、fny()として以下の関数を用い、

double fny(double x, double y, double t)  {
 return -0.1*y - x*x*x + 12.0*cos(t);
}
プロットのところを
 px = 300.0 + 28.0 * x;
 py = 320.0 - 28.0 * y;
とした。

ところで全く関係ないかもしれないが、ヒカシューの「二十世紀の終わりに」という曲を聞いていたら、以下のような歌詞があった。

二十世紀の終わりに恋をするなら
惑星のちからと死の魔術が必要
恋の断面図を透かしてみてごらん
そこは地獄かはたまた暗闇か

もしかしてポアンカレ断面は、恋の断面図なのか、なわけないよね、と意味不明のことをつい呟いてしまった。

//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdlib.h>
#include <math.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
double fny(double x, double y, double t);
double ModT(double t);
int MODE(double th);
const double pi = 3.14156;
double d = 0.0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Close1Click(TObject *Sender)
{
 Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Draw1Click(TObject *Sender)
{
const double H = 0.001;
int i,j;
double x = 1.074,y = 3.021,t = 0.0;
double a1,a2,a3,a4;
double b1,b2,b3,b4;
double px,py;
double th;
int N = 0;
int n = 0;
int mode0 = 0, mode1 = 0;

 while (n < 100000 )  {
  N++;
  th = ModT(t);
  mode0 = MODE(th);
  a1 = y; 
  b1 = fny(x,y,t);
  a2 = y + H*b1/2.0;
  b2 = fny(x+H*a1/2.0,y+H*b1/2.0,t+H/2.0);
  a3 = y+H*b2/2.0;
  b3 = fny(x+H*a2/2.0,y+H*b2/2.0,t+H/2.0);
  a4 = y+H*b3;
  b4 = fny(x+H*a3,y+H*b3,t+H);
  x += H*(a1+2.0*a2+2.0*a3+a4)/6.0;
  y += H*(b1+2.0*b2+2.0*b3+b4)/6.0;
  t += H;
  if ( N > 1000 && (( mode1 == 3 && mode0 == 0 ) || ( mode1 == 0 && mode0 == 3 )) )     {
	  px = 400.0 + 140.0 * x;
	  py = 320.0 - 140.0 * y;
     Canvas->Pixels[px][py] = clBlack;
     n++;
  }
  mode1 = mode0;
 }
}

double fny(double x, double y, double t)  {
 return -0.20*y + x - x*x*x + 0.30*cos(t);
}

double ModT(double t)                     {
 double s = floor(t / (2.0 * pi));
 return t - 2.0*s*pi;
}

int MODE(double th)      {
 if ( cos(th + d) >= 0 && sin(th + d) >= 0 ) return 0;
 if ( cos(th + d) < 0 && sin(th + d) >= 0 ) return 1;
 if ( cos(th + d) < 0 && sin(th + d) < 0 ) return 2;
 if ( cos(th + d) >= 0 && sin(th + d) < 0 ) return 3;
 return 3;
}
//---------------------------------------------------------------------------

山口昌哉、「カオスとフラクタル」講談社ブルーバックスを更に読んでいて、アトラクタの挙動をするダフィンの方程式という常微分方程式が示されていたので、久々にルンゲ・クッタ法で解いてみることにした。その常微分方程式は以下のとおりである。


すると、以下のように連立一次の常微分方程式であらわされ、ルンゲ・クッタ法で解ける。なお、この微分方程式にアトラクタを描かせるためには、定数kとBをある特定の範囲の値に選ばなくてはならないが、ここではそのような値の一例として、k = 0.25, B = 8.0と選んだ。

この方程式は、時間tの項cos(t)を陽にもつのが特徴である。そこで、x,yにcos(t)を第3の軸として加え、三次元として表示させることにした。いろいろな側面の描画を便宜上異なる色で示した。各図からは、不規則なアトラクタになっていることが見て取れる。

先ず、x-yの軌跡は次のとおりである。

x-y-tの3Dの軌跡は次のとおりである。

x-tの軌跡は次のとおりである。

y-tの軌跡は次のとおりである。

よく分からないが、x-tの軌跡が、「カオスとフラクタル」講談社ブルーバックスにあった図に近いかもしれない。

//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdlib.h>
#include <math.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
double fny(double x, double y, double t);
double ModT(double t);
const double pi = 3.14156;
int DT = 0;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Close1Click(TObject *Sender)
{
  Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Draw1Click(TObject *Sender)
{
const double H = 0.00001;
int i,j;
double x = 1.074,y = 3.021,t = 0.0;
double a1,a2,a3,a4;
double b1,b2,b3,b4;
double px,py;
double TH1, TH2, COS1, COS2, SIN1, SIN2, th;
int N = 0;
int n = 1;

TH1 = pi/4.0;
TH2 = pi/4.0;

COS1 = cos(TH1);
SIN1 = sin(TH1);
COS2 = cos(TH2);
SIN2 = sin(TH2);
for ( i = 1; i < 1000; i++ )
 for ( j = 1; j < 760; j++ )
 Canvas->Pixels[i][j] = clWhite;

 while (N++ < 30000000 )  {
  a1 = y; 
  b1 = fny(x,y,t);
  a2 = y + H*b1/2.0;
  b2 = fny(x+H*a1/2.0,y+H*b1/2.0,t+H/2.0);
  a3 = y+H*b2/2.0;
  b3 = fny(x+H*a2/2.0,y+H*b2/2.0,t+H/2.0);
  a4 = y+H*b3;
  b4 = fny(x+H*a3,y+H*b3,t+H);
  x += H*(a1+2.0*a2+2.0*a3+a4)/6.0;
  y += H*(b1+2.0*b2+2.0*b3+b4)/6.0;
  t += H;
  if ( N > 10000 )               {
	th = ModT(t);
	if ( DT == 1 )                            {
	 px = 300.0 + 40.0*(COS1*x + SIN1*COS2*y + 2.0*SIN1*SIN2*cos(th));
	 py = 320.0 - 40.0*(-SIN1*x + COS1*COS2*y + 2.0*COS1*SIN2*cos(th));
	 Canvas->Pixels[px][py] = clNavy;
	} else if ( DT == 3 )                     {
	 px = 380.0 + 60.0*y;
	 py = 320.0 - 100.0*cos(th);
	 Canvas->Pixels[px][py] = clGreen;
	}  else if ( DT == 2 )                     {
	 px = 350.0 + 60.0*x;
	 py = 320.0 - 100.0*cos(th);
	 Canvas->Pixels[px][py] = clRed;
	} else                                     {
	 px = 300.0 + 40.0*x;
	 py = 320.0 - 40.0*y;
	 Canvas->Pixels[px][py] = clBlack;
	}
	n++;
  }
 }

  return;
}
//---------------------------------------------------------------------------

double fny(double x, double y, double t)  {
 return -0.25*y - x*x*x + 8.0*cos(t);
}

double ModT(double t)                     {
 int M = (int) t / (2.0 * pi);
 return (double) t - M * 2.0 * pi;
}
void __fastcall TForm1::RadioGroup1Click(TObject *Sender)
{
   switch(RadioGroup1->ItemIndex)   {
	 case 0:
	 DT = 0;
	 break;
	 case 1:
	 DT = 1;
	 break;
	 case 2:
	 DT = 2;
	 break;
	 default:
	 DT = 3;
	 break;
   }
}
//---------------------------------------------------------------------------

高木関数

user-pic
0

山口昌哉、「カオスとフラクタル」講談社ブルーバックスを更に眺めていると、高木関数というものがと言及されていた。すなわち、ここで高木とは、類体論で有名な高木貞治のことである。

高木貞治は、ワイエルシュトラスとは独立に、以下のようなアイデアによって、[0,1]の区間の至るところで連続であり、且つ至るところで微分不可能な関数T(x)を構成した。その数式を示すと以下のとおりである。

実は、「カオスとフラクタル」講談社ブルーバックスには高木関数の級数表示のところで誤記があり、そのままプログラミングしたら全くそれらしい関数が構成されなかった。そこで、ウェブで調べるとともに、自分でも考えて、以下のようにした。すなわち、φ(22x)のようにすると、もともと[0,1]だった定義域が広がってしまう。そこで、xが1より大きい場合は、その小数部分のみで関数の値を計算することにより、φ(22x)という値域の拡張を達成した。これで計算すると、想定されていた高木関数の表示が得られた。

折角なので、φ(x) = 4x(1-x)というワイエルシュトラス関数の場合も同様に計算して、併せて表示した。高木関数を黒、ワイエルシュトラス関数を赤のラインで示したのが以下のとおりである。

中心点だけ高木関数とワイエルシュトラス関数の値が一致し、その他の場合は高木関数 < ワイエルシュトラス関数となっていることが見て取れる。

でも待てよ、と考えてみた。私の拙い知識では、至るところ連続且つ微分不可能というワイエルシュトラスの関数は、三角関数を駆使したもので、φ(x) = 4x(1-x)のような関数形ではなかったような気がする。これはワイエルシュトラス関数のalternativeなのだろうか。機会があったら調べてみることにする。

また、改めて関数の連続性と微分可能性の定義を考えてみたが、すっかり忘れているようで、それを改めて勉強しなおして、高木関数の微分不可能性の証明も考えてみたい。

ともかく、例によって以下、プログラムのソースコードを示す。

//---------------------------------------------------------------------------

#include <vcl.h>
#include <math.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
double phai(double x);
double W(double x);
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
 Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Draw1Click(TObject *Sender)
{
  double x,y,v,by,py,u,r;
  int i,j,bx,px;
  bx = 20;
  by = 660.0;
  Canvas->Pen->Color = clBlack;
  for ( i = 0; i < 700; i++ )               {
	x = (double) i / 700;
	u = x;
	r = 2.0;
	v = phai(u)/r;
	y = 0.0;
	for ( j = 0; j < 100; j++ )        {
	  y += v;
	  u *= 2.0;
	  r *= 2.0;
	  v = phai(u)/r;
	}
	px = i + 20;
	py = 660.0 - 700.0*y;
	Canvas->MoveTo(bx,by);
	Canvas->LineTo(px,py);
	bx = px; by = py;
  }
 // W-function 
    bx = 20;
  by = 660.0;
  Canvas->Pen->Color = clRed;
  for ( i = 0; i < 700; i++ )               {
	x = (double) i / 700;
	u = x;
	r = 2.0;
	v = W(u)/r;
	y = 0.0;
	for ( j = 0; j < 100; j++ )        {
	  y += v;
	  u *= 2.0;
	  r *= 2.0;
	  v = W(u)/r;
	}
	px = i + 20;
	py = 660.0 - 700.0*y;
	Canvas->MoveTo(bx,by);
	Canvas->LineTo(px,py);
	bx = px; by = py;
  }
}
//---------------------------------------------------------------------------

double phai(double x)   {
 double y;
 y = x - floor(x);
 if ( y >= 0 && y < 0.5 )  {
	return 2.0 * y;
 }	else if ( y >= 0.5 && y < 1.0 )  {
	return 2.0*(1.0 - y);
 }
 return 0.0;
}

double W(double x)      {
 double y;
 y = x - floor(x);
 return 4.0 * y * (1.0 - y);
}

ウェブで、ドラゴン曲線を見かけて、そういえばこういうのがあったよね、と描いてみることにした。

アルゴリズムはレヴィの図形とほぼ同じだが、数式が以下のとおり、よりシンプルである。

図形は以下のとおりであり、

プログラムのソースコードは、以下のとおりである。一応、レベル毎に色を変えてみたが、あまり反映されていない。

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
void fracta(double x, double y, int level);


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 fracta(0.0, 1.0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 Close();
}
//---------------------------------------------------------------------------

void fracta(double x, double y, int level)  {
 int Level = level;
 double px, py, tx, ty;
 if (level > 13 )  {
   px = 200.0 + 400.0 * x;
   py = 350.0 - 400.0 * y;
   if ( level == 13 )
	Form1->Canvas->Pixels[px][py] = clYellow;
   else if ( level == 14 )
	Form1->Canvas->Pixels[px][py] = clBlue;
   else
	Form1->Canvas->Pixels[px][py] = clRed;
   if (level > 16 ) return;
 }
 Level++;
 tx = (x - y)/2.0;
 ty = (x + y)/2.0;
 fracta(tx, ty, Level);
 tx = 1.0 - (x + y)/2.0;
 ty = (x - y)/2.0;
 fracta(tx, ty, Level);
 return;
}

Hidden Dimention Galleriesでは、以下のような一般化が与えられる。

すなわち、以下のような二元線形変換が定義される。

xn+1 = axn + byn + e
yn+1 = cxn + dyn + f

そして、これに確率pを組み合わせて、(a,b,c,d,e,f,p)という7つの実数の組で1つの変換が与えられる。

前記羊歯の場合、
(0.836,0.044,-0.044,0.836,0,0.169,1/2)
(-0.141,0.302,0.302,0.141,0,0.127,1/6)
(0.141,-0.302,0.302,0.141,0,0.169,1/6)
(0,0,0,0.175337,0,0,1/6)

であった。

上記ウェブサイトには、以下の例が与えられている。
(0.14 ,0.01 ,0.00 ,0.51 ,-0.08 ,-1.31 ,0.10)
(0.43 ,0.52 ,-0.45 ,0.50 ,1.49 ,-0.75 ,0.35)
(0.45 ,-0.49 ,0.47 ,0.47 ,-1.62 ,-0.74 ,0.35)
(0.49 ,0.00 ,0.00 ,0.51 ,0.02 ,1.62 ,0.20)

これで描いた図形が下記のとおりである。

よく分からないが、これはメープルの葉ということらしいが、現物をみたことがないので、ピンとこない。念のためソースコードも示す。

//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdlib.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Close1Click(TObject *Sender)
{
  Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Draw1Click(TObject *Sender)
{
int i = 0, n, c;
double x,y,px,py,tx,ty;

// x, yに適当な初期値を与える
x = 0.1;
y = 0.1;

while ( i++ < 5000000 )   {

 n = random(136512)%100 + 1;

  if ( n >= 1 && n <= 10 )      {
     tx = 0.14*x + 0.01*y - 0.08;
     ty = 0.51*y - 1.31;
  } else if ( n >= 11 && n <= 45 )  {
     tx = 0.43*x + 0.52*y + 1.49;
     ty = -0.45*x + 0.50*y - 0.75;
  } else if ( n >= 46 && n <= 80 )  {
     tx = 0.45*x - 0.49*y - 1.62;
     ty = 0.47*x + 0.47*y - 0.74;
  } else                            {
     tx = 0.49*x + 0.02;
     ty = 0.51*y + 1.62;
  }

  if ( i > 100 )    {
	px = 350.0 + 70.0*tx;
	py = 300.0 - 70.0*ty;
	if ( i < 4950000 )
	Canvas->Pixels[px][py] = clGreen;
	else
	Canvas->Pixels[px][py] = clOlive;
  }
  x = tx;
  y = ty;
}
  return;
}
//---------------------------------------------------------------------------

以前に、羊歯のグラフィックというのを描いてみたことがあるが、これは要するに本にあるソースコードを打ち込んでみただけなので、アルゴリズムの輪郭もうまく把握できなかった。

最近たまたま、合原一幸編著「カオス カオス理論の基礎と応用」サイエンス社に、IFSのアトラクタとして、上記羊歯のグラフィックの元となると思われるアルゴリズムが説明してあったので、改めてこの説明から、プログラミングしてみることにした。

IFSとは、iterated function systemの略らしくて、日本語では反復関数系と訳される。なんともありきたりなネーミングだと苦笑するのであるが、米国の応用数学者M. Barnsleyが研究したものらしい。

Barnsleyは、羊歯を描くために、下記の4つの写像を用意した。
  1. W1(x,y) = (0.836x + 0.044y,-0.044x + 0.836y + 0.169)
  2. W2(x,y) = (-0.141x + 0.302y,0.302x + 0.141y + 0.127)
  3. W3(x,y) = (0.141x - 0.302y,0.302x + 0.141y + 0.169);
  4. W4(x,y) = (0.0,0.175337y);

そして、(x,y) ← W(x,y)の処理を繰り返すのだが、W(x,y)は実際は、W1(x,y),W2(x,y),W3(x,y),W4(x,y)のどれかである。その呼び出す確率がそれぞれ、1/2, 1/6, 1/6, 1/6である。こうして描いた図は、下記のとおりである。バリエーションとして、点の色を少し変えてみた。

なお、例によって、プログラムのソースコードも付ける。

//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdlib.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Close1Click(TObject *Sender)
{
  Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Draw1Click(TObject *Sender)
{
int i = 0, n, c;
double x,y,px,py,tx,ty;

// x, yに適当な初期値を与える
x = 0.1;
y = 0.1;

while ( i++ < 30000000 )   {

 n = random(1000)%6 + 1;
 switch(n)  {
  case 4:
   tx = -0.141*x + 0.302*y;
   ty = 0.302*x + 0.141*y + 0.127;
   break;
  case 5:
   tx = 0.141*x - 0.302*y;
   ty = 0.302*x + 0.141*y + 0.169;	
   break;
  case 6:
   tx = 0.0;
   ty = 0.175337*y;	
   break;
  default:
   tx = 0.836*x + 0.044*y;
   ty = -0.044*x + 0.836*y + 0.169;
   break;
 }
  if ( i > 100 )    {
	px = 300.0 + 550.0*tx;
	py = 600.0 - 550.0*ty;
	c = i%6 + 1;
	if ( c == 4 )
    Canvas->Pixels[px][py] = clYellow;
	else if ( c == 5 )
    Canvas->Pixels[px][py] = clRed;
    else if ( c == 6 )
    Canvas->Pixels[px][py] = clBlue;
    else
    Canvas->Pixels[px][py] = clGreen;
  }
  x = tx;
  y = ty;
}
  return;
}
//---------------------------------------------------------------------------

もう15年以上、C++ Builderを使っていて、それはVer.3やVer. 6というかなり古いものだったが、Windows XPであるうちは何も問題なく導入できていた。ところがWindows XPからWindows 7に移行すると、Ver.3は完全に導入を拒否され、Ver. 6はなんとかWindows 7に導入できるものの、以前に作成したアプリの編集しかできず、新規アプリは作成できないという極めて妙なことになっていた。それでも、外資系に勤務していたときは、私のアプリのユーザが若干名いたので、Windows 7上のC++ Builder Ver. 6上でなんとかメンテしていて、どうしてもスクラッチでアプリを作成しなくてはならないときは、自宅のXPマシンで作成して、VPN経由で会社の共有ディスクに送ることでなんとかしていた。

しかし、外資系を退職すると、Windows 7ネイティブで動くC++ Builderが必要だと思い、購入したのが、ボーランドから引き継いでC++ Builderを開発しているエンバカデロのC++ Builder XE4である。実は自宅にはまだオフラインで使用しているXPマシンでC++ Builder Ver. 3が使えるので、アプリの作成は主としてこちらを使ってきたが、Windows 10マシンにも幸いC++ Builder XE4が導入できたので、最近になってやっと、C++ Builder XE4にも馴染んできた。

で、C++ Builder XE4で困ったことがあった。それは、折角アプリを作成しても、アイコンが設定できず、デフォールトのアイコンのままなのである。C++ Builder Ver.3やVer. 6では、そういう悩みは無用で、ちゃんとimagedit.exeというリソース・エディタが標準でついてくるので、それを使って、アプリケーション作成時に自動的に生成される、resという拡張子のファイルを編集して、アイコンを作成すればよい。

ところが、C++ Builder XE4にはそういうリソース・エディタがついてこない。なら、フリーウェアでリソース・エディタがあるだろうとみてみたら、見つかるのは古いものばかりで、互換性が怪しそうである。

そこで困って、いろいろウェブで検索していると、アイコンのファイルを作成して、リソース・コンパイラにかければよい、とある。C++ Builder XE4にもちゃんと、コマンドライン版のbrcc32.exeというリソース・コンパイラがある。そこで、32×32のビットマップファイルとして適当なアイコンを作成し、それをicon.bmpとして保存した。そして、icon.bmpをicon.icoとして保存し直し、icon.rcというテキストファイルに次のように記述した。

MAINICON ICON "ICON.ICO"

そして、コマンド・ラインでbrcc32 -foicon.res icon.rcとすると、なんとファイルフォーマットが正しくないとエラーになった。あれっそうか、と改めてicon.bmpをXnViewでico形式で変換してセーブすると、今度はうまくいって、icon.resが作成された。

それから、C++ Builder XE4を起動して、作成中のアプリのプロジェクトに、コンパイル済のリソースとしてicon.resを取り込んでビルドしたのだが、何も起きない。がっかりしてさらにウェブ上で情報を探すと、プロジェクトのアプリケーションというセクションに、アイコンの指定ができるらしい。そこで今度は、そのメニューで先に作成しておいたicon.icoを指定すると、今度はフォームにきちんと期待したアイコンがあらわれた。

ところが、C++ Builder XE4を終了して、エクスプローラーで当該アプリのアイコンを確認すると、デフォールトのままである。ちなみに、デフォールトのアイコンは、C:\Program Files(x86)\Embarcadero\RAD Studio\11.0\bin\cbuilder_PROJECTION.icoが使われる。だとしても、このアイコンをその都度置き換えるのは変である。そこである実験をしてみたくなった。それは、作成したアプリを他のパソコンにコピーしてみることである。すると、あら不思議、エクスプローラーで確認すると、ちゃんと作成したアイコンが見える。ただ、それをダブルクリックして起動すると、何かのdllがないと動かない。ああ、あれか、とプロジェクトで「実行時パッケージを使う」のオプションを外してビルドしたら、今度は他のパソコンでもちゃんと動いた。ただ、普通にビルドしたら、アプリはせいぜい500KB程度なのに、「実行時パッケージを使う」のオプションを外すとなんと、3.5MBのサイズになってしまう。たったアイコン程度にこんなに手間がかかるとはと、ちょっとうんざりしたが、マルチアイコン・フォーマットなど、副産物でいろいろ勉強になったので、よしとする。

西垣通氏は、情報処理全般の学際的議論で有名で、以前からその著書に親しんできたが、少し前に、人工知能のシンギュラリティを批判する本である「ビッグデータと人工知能」中公新書を刊行したと知ったので、読んでみた。

すなわち、この本は、人工知能によるバラ色の、あるいは人工知能によって支配される、凍り付くような未来世界の出現をきちんと否定する。その内容を私なりに要約してコメントしてみる。

まず、自動運転など、期待されているAIによる自動化であるが、実際は人間によるメンテナンスを必要とする、ということがある。その手間はそんなに半端ではなく、もしかしたら自動化という趣旨とは違ってくるかもしれない。

また、ビッグデータであるが、それはある意味個人情報の塊なので、それゆえに警察の捜査などにも利用可能であるが、逆に個人情報の扱いには極めて慎重になる必要があり、そのアクセスというか取り扱いをどう制御するか、極めて難しい。

より技術的な問題として、ビッグデータの処理は伝統的な推計学のような、サンプリングではなく、全数処理なので、処理時間が発散してしまう可能性がある。これに対処するために、例えばC.M.ビショップ著、パターン認識と機械学習、シュプリンガー・フェアラーク上下二巻に記述されているようなアルゴリズムがあるが、さりとて、計算量の問題は依然として課題である。

それとは別の問題もある。それは記号接地問題であり、フレーム問題である。すなわち、概念をコードに落とし込むことの困難と、状況を把握することの困難である。

しかし、ディープラーニングの成功が、世間を過度に楽観的にした。すなわち、ディープラーニングの多層モデルは脳をシミュレートしたものであり、多層モデルを実装するハードウェアがムーアの法則で指数関数的に発展することが約束されているものあってみれば、脳=人間の心そのものであるので、人工知能は人間の心と同等な機能をもつ存在になれる、という見込みである。

しかし、見落とされているのは、生命と自由で述べたような、自由追求としての生命の議論である。すなわち、人工知能は人間が記述した仕様のとおりにしか動作しないのであり、自ら目的を作り出すことができない。これはオートポイエーシス理論にもつながる。

さらに人工知能は、脳をクオリアを扱えないという問題もある。

なのに、西洋文明で脳を機械のように記述すれば、それが人間の心そのものになるという思想はキリスト教的一神教だからと著者は指摘するのだが、私はそれは、キリスト教的一神教というよりも人間機械論というか、デカルト主義なのではあるまいか。ともかく西欧文明が、デカルト主義に染まっていることは確かである。

また著者は、シャノンの情報理論は、情報の意味という視点がないので、不十分であると指摘されているが、なるほど、その観点は面白いと思う。

しかし、クラウドソーシングというか、集合知に対する楽観論には私はどうしても与することはできない。確かに集合知が有利に働く場面もあるだろうが、少なくとも集合知は将棋やチェス、囲碁などの分野では威力は発揮できないのではないか。ある優れたコーディネータの存在によって、アマチュアの烏合がプロを脅かすことができる、というのだが、本当だろうか。

ともかく、いろいろ細かい疑問はあるものの、「ビッグデータと人工知能」中公新書は、なかなか啓発的な本だと思われる。

このアーカイブについて

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

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

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