マンデルブロ集合の描き方




|複素数平面の海>



フラクタルや美しい図形に戻る



目次



マンデルブロ集合とバーニングシップフラクタルの美しさ


 マンデルブロ集合とは、複素数平面上の集合です。画像で表現すると、下のようになります。

マンデルブロ集合の全体図



 この図形の淵を拡大していくと、この世の物とは思えないような、美しく、複雑な模様が現れます。縁を拡大した動画と画像を載せておきます。

マンデルブロ集合の淵1
マンデルブロ集合の淵2
マンデルブロ集合の淵3
マンデルブロ集合の淵4

 動画もご覧ください。

マンデルブロ集合の拡大



 また、バーニングシップフラクタルというのは、これも複素数平面上の集合で、マンデルブロ集合と同じように描くことができます。全体図は下の画像のようになります。

バーニングシップフラクタルの全体図

 正直なところ、バーニングシップフラクタルは、全体図はあまり美しくありません。ところが、全体図の左下部分、突き出しているところを拡大すると、下の画像のような構造が見えてきます。

バーニングシップフラクタル1
バーニングシップフラクタル2
バーニングシップフラクタル3

 マンデルブロ集合とは趣の違う、まるで建築物のようなパターンが見られます。動画もご覧ください。

バーニングシップフラクタルの全容



 人はなぜ、マンデルブロ集合やバーニングシップフラクタルに魅了されるのか。それは、図形自体の美しさもありますが、数学的な美しさという点も見逃せない要因です。

 実は、マンデルブロ集合もバーニングシップフラクタルも、この複雑な模様はたった一つの数列から生み出されています。一つのシンプルな数列からこれほど複雑で壮大な図形が現れる。その不可思議さもこれら図形の魅力だと思います。

 次の項から、マンデルブロ集合の描き方を説明します。興味のある方はご覧ください。





複素数と複素数平面について


 マンデルブロ集合とは、複素数平面上の集合です。なので、マンデルブロ集合について説明する前に、まずは複素数複素数平面について説明します。これらをご存知の方は、次の項まで読み飛ばしてください。




 複素数とは、実数虚数を組み合わせたものです。実数とは、$1,-5,0.33,\sqrt{2}$など、中学校までに習う数のまとまりです。そして虚数というのは、2乗して負になる数のことです。




 虚数を式で表してみましょう。虚数単位$i$とすると、

$i^2=-1$

ということになります。虚数単位を使って虚数を表すと、$5i , -1.1i$などとなります。それぞれ2乗すると、

$(5i)^2=-25$
$(-1.1i)^2=-1.21$

となります。




 実数の範囲では、2乗して負になる数というのはありませんでした。正の実数も負の実数も、2乗すれば正の実数になります。なので虚数は、普段実数に囲まれている私たちから見れば、とても不思議な数です。




 さて、複素数とは実数虚数を組み合わせたもの、と先ほど書きました。複素数は、

$1+3i$
$-3.4-23i$
$\sqrt{3}+\frac{1}{2}i$

などというように、実数虚数の和で表します。




 複素数の計算は基本的に、虚数単位$i$を文字として扱って計算します。ただし、$i^2=-1$となることにご注意ください。いくつか計算例を挙げてみます。

$(1+i)+(2+3i)=3+4i$
$(5-2i)-(-1+i)=5-2i+1-i=6-3i$
$(1+7i)*(2-2i)=2-2i+14i-14i^2=2+12i+14=16+12i$



 それでは複素数平面について説明しましょう。複素数平面とは、横軸を実数の軸(実軸)、縦軸を虚数の軸(虚軸)として作った面です。図にすると下のようになります。

複素数平面の説明

 $a+bi$という複素数は、図のように、実軸の値が$a$で、虚軸の値が$b$のところに位置しています。このようにすると、一つの複素数は、複素数平面上の1点を表していることになります。なお、この$a+bi$という複素数で、$a$を実部、$b$を虚部と呼びます。





マンデルブロ集合とは


 さて、ここまでにマンデルブロ集合の理解に必要な事柄を説明してきました。いよいよマンデルブロ集合について説明しましょう。




 マンデルブロ集合とは、複素数平面上の複素数列

$z_0=0$
$z_{n+1}=z_n^2+c$
$(z_n,cは複素数)$

について、$n \rightarrow \infty$としたとき、$z_n$が発散(値が無限大になること)しない点$c$の集合です。




 これだけでは何のことやら、という感じなので、詳しく説明します。例えば、$c=2+2i$という複素数を考え、上の数列に適用してみましょう。

$z_0=0$
$z_1=z_0^2+c=0^2+(2+2i)=2+2i$
$z_2=z_1^2+c=(2+2i)^2+(2+2i)=4+8i-4+2+2i=2+10i$


という具合になります。$c=2+2i$という点では、計算していくとどんどん値が大きくなり、この数列は発散してしまいそうです。




 他にも、$c=0+0i$という点を考えてみましょう。

$z_0=0$
$z_1=z_0^2+c=0^2+(0+0i)=0$
$z_2=z_1^2+c=0^2+(0+0i)=0$


$c=0+0i$という点では、数列の値が0になり、収束しました。なのでこの点は、マンデルブロ集合に含まれる点だということになります。




 このようにして複素数平面上の点が発散するかどうかを調べていき、発散しない(マンデルブロ集合に含まれる)点を白、発散する点を黒で塗っていくと、下のような図が描けます。

マンデルブロ集合の全体図

 これが、ページの先頭にも上げた、マンデルブロ集合の全体図です。上の図は、どのくらいの速度で発散するかによっても色分けされています。マンデルブロ集合に近い所では、うっすらと白くなっているのが見えるでしょうか。




 ここまでで、マンデルブロ集合のごく大まかな描き方を紹介しました。ですが、描き方を知ることと、実際に描くことには大きな隔たりがあります。これから、実際にはどう描けばよいのか説明します。





マンデルブロ集合を描くアルゴリズム


 マンデルブロ集合の、プログラミングでの描き方を説明します。マンデルブロ集合を描くアルゴリズムを大まかに書くと、

  1. あらかじめ計算回数$roop$としきい値$thresh$を決めておきます。

  2. 複素数平面上に格子点を取ります。この各格子点が、先ほどの$c$に対応します。

  3. 各格子点について、上で説明した複素数列を、あらかじめ決めた計算回数$roop$だけ計算します。

  4. 計算した複素数列の絶対値をとり、もしそれがしきい値$thresh$を越えていたら、その点では数列が発散したと見なします。

  5. 計算回数$roop$だけ計算しても、数列の絶対値がしきい値$thresh$を越えなかったら、その点は発散せず、マンデルブロ集合に含まれると見なします。

  6. このようにして格子点を一つずつ見ていき、発散する点としない点を色分けしていきます

 これが具体的な描き方です。もう少し詳しく説明すると、計算回数$roop$は100回ほどで十分です。マンデルブロ集合の淵を拡大していく場合は、それに合わせて$roop$を大きくしないと、画像がぼやけていきます。

 しきい値$thresh$は2にします。それ以上大きくする必要はありません。マンデルブロ集合の数列の絶対値が2を越えなければ、数列は発散しないことが証明されています。




 ここまでの説明で、プログラミングでグラフなどを描いたことのある方なら、どういうアルゴリズムにすればよいか見当が付いているかもしれません。ぜひ、自力でマンデルブロ集合を描くプログラムを作ってみてください。




 なお、複素数列を

$z_0=0$

$z_{n+1}=(|re(z_n)|+i*|im(z_n)|)^2+c$

$(z_n,cは複素数)$

と変えると、冒頭で説明したバーニングシップフラクタルが描けます。ここで、re(),im()というのは、それぞれ複素数z_nの実部、虚部を表しています。それらの絶対値をとり、虚部の方に$i$をかけ、2乗しています。





octaveによる描画のサンプルプログラムソース


 プログラミング言語octaveを使い、マンデルブロ集合を描いてみましょう。octaveは簡単にインストールでき、使いやすいのでプログラミング初心者の方にお勧めです。

 以下はプログラムソースの例です。



clear;
clf;

point=[-0.6,0];
length=3.3;
roop=15;
thresh=2;
cut=0.004*length;

remin=point(1)-length/2;
remax=point(1)+length/2;
immin=point(2)-length/2;
immax=point(2)+length/2;
re=[remin:cut:remax];
im=[immin:cut:immax];

M=size(re)(2);
N=size(im)(2);
F=zeros(M,N);

for m=1:M
    for n=1:N
        z=0;
        c=re(m)+i*im(n);
        for k=1:roop
            z=z^2+c;
            if abs(z)>thresh
                F(N+1-n,m)=k;
                break
            endif
            if k==roop
                F(N+1-n,m)=roop;
            endif
        endfor
    endfor
endfor

contourf(re,im,F)
axis([remin remax immin immax],"square")
xlabel("Re")
ylabel("Im")


 このプログラムにより、下のようなマンデルブロ集合が描けるはずです。

octaveによるマンデルブロ集合



 プログラムの説明をします。最初のpointというのは、描画する範囲の中心を表しています。次のlengthは、描画範囲の横と縦の幅を表しています。このプログラムでは、$(-0.6,0)$という点を中心に、$(横 \times 縦)=(3.3 \times 3.3)$という範囲を描画するという設定になります。

 roopは前に説明した計算回数で、threshはしきい値です。無事にマンデルブロ集合を描けたら、roopを100ぐらいにして描画してみてください。

 次のcutは、描画範囲の区切り幅を表しています。このプログラムでは、lengthを0.004ずつに区切っています。この0.004を0.002ぐらいにすると、より精細なマンデルブロ集合が描けます。

 その次のreminからimmaxは、描画範囲の四隅の座標で、reやimは、描画範囲を区切って配列にしたものです。

 MやNはreとimのサイズで、$F$は複素数平面上の各点について、計算した回数を代入していく2次元の配列です。

 これ以降のfor文は、格子上の各点について、発散するかどうかを判定する部分です。最初の2つのforは、格子点を指定するためのforで、その中のkについてのforは、一つの点について計算を繰り返すforです。

 z,cは上の方で解説した時のz,cと同じ役割です。zは複素数列の項で、cは格子点上の複素数を表しています。

 最初の2つのforを通り抜けることで、複素数平面の点cが決まります。次のkについてのforの中で、その点cについて、数列の計算が繰り返されます。

 for中の1つ目のif文は、zの絶対値abs(z)がしきい値threshを越えていないか判定するための物です。もしもabs(z)がしきい値を越えていたら、その時のループ回数kをFに書き込み、forループを抜けます。

 2つ目のif文では、kがあらかじめ決めた計算回数roopと同じになったら、そのcでの数列は発散しないと見なし、Fにroopを代入するという操作をします。

 forを抜けた後の部分は、描画をする命令です。contourfは2次元のマップを作る関数です。axisは軸の範囲を指定する関数で、xlabel,ylabelで軸に名前を付けています。




 このようにして、、マンデルブロ集合の描画をすることができます。上のプログラムで書くことができたら、roopを増やしたり、cutを小さくしたりして、より高精細なマンデルブロ集合を描いてみてください。ただし、時間はかかります。




 pointを変えれば描画範囲の中心を変更できますし、lengthを小さくすれば図を拡大できます。ぜひ、これらをいじってマンデルブロ集合の淵を拡大してみてください。




 最後に、バーニングシップフラクタルの描き方についてですが、プログラム中の

z=z^2+c;

という式を、

z=(abs(real(z))+i*abs(imag(z)))^2+c;

とするだけで、バーニングシップフラクタルを描くことができます。




 また、式を

z=(abs(real(z))+i*abs(imag(z)))^4+c;

とすると、複素数平面の海のページのパターンを描くことができます。


|複素数平面の海>



フラクタルや美しい図形に戻る

ホームページのTOPに戻る