ブッダブロと仲間たち
どうも、108Hassiumです。
この記事では、「ブッダブロ」という有名なフラクタル図形や関連する別のフラクタル図形を紹介します。
定義・描画法
ブッダブロはこんな形のフラクタル図形です。
皆さんお馴染みのマンデルブロ集合は$${z_0=0,z_{n+1}=z_n^2+c}$$という数列が無限大に発散するかどうかで複素平面を塗り分けたものですが、ブッダブロは同じ数列を使って「$${z_n}$$が発散する場合だけ$${z_0,z_1,z_2…}$$をプロットする」という操作を$${c}$$を変えながら繰り返したものです。
この説明におおよそ忠実にコードを書くと、以下のようになります。
void setup(){
size(2000,2000);
background(0);
stroke(255,5);
double x,y,px,py,cx,cy;
boolean o=true;
for(int a=0;a<4000;a++){
for(int b=0;b<4000;b++){
cx=(double)a/1000.0-2.0;
cy=(double)b/1000.0-2.0;
x=cx;
y=cy;
o=true;
for(int c=0;c<50000&&o;c++){
px=x;
py=y;
x=px*px-py*py+cx;
y=2.0*px*py+cy;
if(x*x+y*y>4){
o=false;
}
}
if(!o){
x=cx;
y=cy;
o=true;
for(int c=0;c<50000&&o;c++){
px=x;
py=y;
x=px*px-py*py+cx;
y=2.0*px*py+cy;
point((float)(x+2.0)*500.0,(float)(y+2.0)*500.0);
if(x*x+y*y>4){
o=false;
}
}
}
}
}
}
※言語はProcessing
詳しい内容の解説は省略しますが、以下の記事で解説しているものと大体同じ仕組み(変数名は全然違いますが)なので気になる人は読んでみてください。
なお、wikipediaの説明では$${c}$$の値は一定範囲内からランダムに決めると書いてありますが、先程のコードでは格子状にとった点を総当たりして発散する点を探すような仕様にしてあります。
これは「成果物にランダム性が出ないものに乱数を使いたくない」という私の好みによるものです。
さて、先程のコードを実行すると以下のような画像が生成されます。
冒頭のカラーの画像は、少し違った方法で描画されています。
void setup(){
size(2000,2000);
background(0);
noStroke();
double x,y,px,py,cx,cy,dx=0,dy=0;
int[][][] ar=new int[2000][2000][3];
boolean o=true;
for(int a=0;a<2000;a++){
for(int b=0;b<2000;b++){
ar[a][b][0]=0;
ar[a][b][1]=0;
ar[a][b][2]=0;
}
}
for(int a=0;a<20000;a++){
for(int b=0;b<20000;b++){
cx=(double)a/5000.0-2.0;
cy=(double)b/5000.0-2.0;
x=0;
y=0;
o=true;
for(int c=0;c<50000&&o;c++){
px=x;
py=y;
x=px*px-py*py+cx;
y=2.0*px*py+cy;
if(c%100==0){
dx=x;
dy=y;
}else if((x-dx)*(x-dx)+(y-dy)*(y-dy)<1e-10){
c=50000;
}
if(x*x+y*y>4){
o=false;
}
}
if(!o){
x=0;
y=0;
o=true;
for(int c=0;c<50000&&o;c++){
px=x;
py=y;
x=px*px-py*py+cx;
y=2.0*px*py+cy;
if(0<(x+2.0)*500.0&&(x+2.0)*500.0<2000&&0<(y+2.0)*500.0&&(y+2.0)*500.0<2000){
if(c<500){
ar[(int)((x+2.0)*500.0)][(int)((y+2.0)*500.0)][0]++;
}else if(c<5000){
ar[(int)((x+2.0)*500.0)][(int)((y+2.0)*500.0)][1]++;
}else{
ar[(int)((x+2.0)*500.0)][(int)((y+2.0)*500.0)][2]++;
}
}
if(x*x+y*y>4){
o=false;
}
}
}
}
}
for(int a=0;a<2000;a++){
for(int b=0;b<2000;b++){
fill(ar[a][b][0]/5,ar[a][b][1]/5,ar[a][b][2]/5);
rect(a,b,1,1);
}
}
}
RGB値を格納する配列を画像の1ピクセルずつに対して用意し、$${z_n}$$の位置に点を打つ代わりに配列の中身を1ずつ増やして、数列の計算が全て終わってから配列の値を使って画像を描画しています。
色の決定には$${z_n}$$の$${n}$$(プログラム中ではc)の値が使われ、$${n}$$が0~499のときは赤、500~4999のときは緑、それ以外は青の値を増やします。
なお、wikipediaではカラー画像を「ネブラブロ」と呼ぶことがあるという記述がありますが、わざわざ呼び分ける必要性を感じないのでこの記事ではカラーのものもブッダブロと呼ぶことにします。
バリエーション
本来のブッダブロは「マンデルブロ集合」という概念と同様に$${z^2+c}$$という関数と紐づけられた存在ですが、この記事では普段の私のマンデルブロ集合の扱いと同様に、別の関数を使ったものもブッダブロと呼ぶことにします。(今までのものは「$${z^2+c}$$のブッダブロ」になります)
というわけで、いろいろな関数によるブッダブロを紹介します。
$${z^n+c}$$のブッダブロです。
$${z^n+c}$$のマンデルブロ集合と同じく$${n-1}$$回回転対称になるようです。
マンデルブロ集合では何もなかった部分に現れる赤いトゲが特徴的です。
$${\frac{c}{z^2-1}+1}$$といえば網目状の模様が特徴ですが、ブッダブロには表れないようです。
$${\frac{z^3}{3}-\frac{z^2}{3}+c}$$のマンデルブロ集合はジュリア集合のようなガタガタの輪郭が特徴ですが、よく見るとブッダブロでも同じ特徴が表れています。
他のものと同様にマンデルブロ集合に似たシルエットが現れましたが、何故か位置がずれています。
$${\text{con}(z)^2+c}$$のものもそうですが、非解析的関数のブッダブロは解析的関数のものとはかなり違った見た目になるようです。
アンチブッダブロ?
ブッダブロ(狭義の方)は$${z_{n+1}=z_n^2+c}$$という数列のうち発散するものの値を使って描画されるものですが、逆に収束するもので同じことをすると以下のような画像が得られました。
ソースコードは以下の通りです。
void setup(){
size(2000,2000);
background(0);
noStroke();
double x,y,px,py,cx,cy,dx=0,dy=0;
int[][][] ar=new int[2000][2000][3];
boolean o=true;
for(int a=0;a<2000;a++){
for(int b=0;b<2000;b++){
ar[a][b][0]=0;
ar[a][b][1]=0;
ar[a][b][2]=0;
}
}
for(int a=0;a<20000;a++){
for(int b=0;b<20000;b++){
cx=(double)a/5000.0-2.0;
cy=(double)b/5000.0-2.0;
x=0;
y=0;
o=true;
for(int c=0;c<5000&&o;c++){
px=x;
py=y;
x=px*px-py*py+cx;
y=2.0*px*py+cy;
if(c%100==0){
dx=x;
dy=y;
}else if((x-dx)*(x-dx)+(y-dy)*(y-dy)<1e-10){
c=50000;
}
if(x*x+y*y>4){
o=fals;
}
}
if(o){
x=0;
y=0;
o=true;
for(int c=0;c<5000&&o;c++){
px=x;
py=y;
x=px*px-py*py+cx;
y=2.0*px*py+cy;
if(0<(x+2.0)*500.0&&(x+2.0)*500.0<2000&&0<(y+2.0)*500.0&&(y+2.0)*500.0<2000){
if(c<50){
ar[(int)((x+2.0)*500.0)][(int)((y+2.0)*500.0)][0]++;
}else if(c<500){
ar[(int)((x+2.0)*500.0)][(int)((y+2.0)*500.0)][1]++;
}else{
ar[(int)((x+2.0)*500.0)][(int)((y+2.0)*500.0)][2]++;
}
}
if(x*x+y*y>4){
o=false;
}
}
}
}
}
for(int a=0;a<2000;a++){
for(int b=0;b<2000;b++){
fill(ar[a][b][0]/50,ar[a][b][1]/50,ar[a][b][2]/50);
rect(a,b,1,1);
}
}
}
主な変更点としては、発散しない数列を描画に使うようにしていること以外にはcの上限を50000から5000に減らし、それに合わせて彩色に関する値を調整している、というものがあります。
wikipediaのブッダブロのページでは、説明は意味不明なもののこの画像とよく似たものが「アンチブッダブロ」という名前で紹介されています。
というわけで、この記事では「ブッダブロの収束列バージョン」のことをアンチブッダブロと呼ぶことにします。
この辺は特に説明することも無いです。
$${\frac{z^3}{3}-\frac{z^2}{3}+c}$$のマンデルブロ集合とブッダブロにはジュリア集合のような見た目の部分があるというのはすでに説明した通りですが、アンチブッダブロではさらに顕著にジュリア集合っぽさが出ます。
$${c(z+\frac{1}{z})}$$のブッダブロは綺麗に描画できなかったのですが、アンチブッダブロはこうなりました。
非解析的関数のアンチブッダブロはやはり独特です。
複素平面の下半分の値だけを使って描画した、$${z^2+c}$$のアンチブッダブロです。
どうやら中央の大きな円状の構造の下にも、細かい模様が隠れているようです。
ちなみにブッダブロで同じことをすると以下のようになります。