PHPでマンデブロ集合を描く

(1/1)
マンデブロ集合を2次元平面にプロットした魅惑的なフラクタル図形をPHPで描くプログラムを紹介する。

(2022年4月29日)PHP8対応,リファラ・チェック改良

サンプル・プログラム:実行例

PHPでマンデブロ集合を描く

目次

サンプル・プログラム

圧縮ファイルの内容
MandelbrotSet.phpサンプル・プログラム本体

マンデルブロ集合とは

マンデルブロ集合とは、
 mimetex 
で定義される複素数列 zn が n→∞ のときに発散しないという条件を満たす複素数cの集合である。
ここで複素数cを  mimetex  と表し、cをXY平面上の点(a, b)とプロットしてゆくと、冒頭のようなフラクタル図形が得られる。

さて、高校数学で登場する複素数だが、実生活では使用しない「数」と誤解していないだろうか。
19世紀に複素数を使う複素力学系が登場する。そして、光や電波、音波などの「」を物理学的に記述するのに複素数が利用されるようになった。スマホのノイズキャンセリングや送受信電波制御にも複素数が使われているのである。

話をフラクタル図形に戻そう。
1975年(昭和50年)、フランスの数学者ブノワ・マンデルブロは、図形の部分と全体が自己相似(再帰)になっているフラクタルという概念を提唱する。「PHPで樹木曲線を描く」「PHPで雪の結晶を描く」で紹介したのもフラクタル図形だ。
1979年(昭和54年)、マンデルブロはコンピュータを使って充填ジュリア集合を2次元に描くことに成功した。これが冒頭のフラクタル図形である。このことから、マンデルブロ集合と呼ばれるようになった。
マンデルブロ集合を拡大すると、似たような図形が繰り返し現れるのだが、けっして同じ図形ではないとされている。樹木曲線や雪の結晶に比べて魅惑的な図形となっている。

2011年(平成23年)1月からフジテレビ系で放映されたアニメ『フラクタル』のオープニングで、美しいマンデルブロ集合のアニメーションが流れたことを記憶している方もいると思う。

準備:初期値

  34: //表示サイズ(縦横ピクセル)
  35: define('SIZE', 600);
  36: 
  37: //パラメータ・テーブル
  38: // kl = 最大繰返し回数
  39: // 複素平面の範囲
  40: //   開始:rs=実部, is=虚部
  41: //   終了:re=実部,ie=虚部
  42: static $Table = array(
  43: array('kl'=>100, 'rs'=>-2.10, 're'=>+0.50, 'is'=>-1.35, 'ie'=>+1.35),
  44: array('kl'=>150, 'rs'=>-0.50, 're'=>+0.50, 'is'=>+0.25, 'ie'=>+1.35),
  45: array('kl'=>250, 'rs'=>-0.20, 're'=>+0.30, 'is'=>+0.55, 'ie'=>+1.10),
  46: array('kl'=>300, 'rs'=>-0.05, 're'=>+0.05, 'is'=>+0.70, 'ie'=>+0.80),
  47: array('kl'=>350, 'rs'=>-0.03, 're'=>+0.04, 'is'=>+0.71, 'ie'=>+0.79)
  48: );

上記の初期値は任意に変更が可能である。
マンデルブロ集合を描く複素平面の範囲は、配列 $Table に定義している。最初の配列は、マンデルブロ集合の全景を描くパラメータである。このパラメータを適当に変更することで、描画したい範囲を変更できる。配列の数も自由に増減できる。

解説:マンデブロ集合を描画

 203: /**
 204:  * マンデブロ集合を描画
 205:  * @param   resource $img イメージストリーム
 206:  * @param   int $ks 複素平面の縦横の分割数
 207:  * @param   array $param パラメータ($Tableに準ずる)
 208:  * @return  なし
 209: */
 210: function drawMandelbrot($img, $ks, $param) {
 211:     $rs = $param['rs'];
 212:     $re = $param['re'];
 213:     $is = $param['is'];
 214:     $ie = $param['ie'];
 215:     $kl = $param['kl'];
 216: 
 217:     $dr = ($re - $rs) / $ks;
 218:     $di = ($ie - $is) / $ks;
 219:     for ($cr = $rs$cr <$re$cr +$dr) {
 220:         for ($ci = $is$ci <$ie$ci +$di) {
 221:             $zr = $zi = 0.0;
 222:             $k = 0;
 223:             $flag = TRUE;
 224:             //複素関数 f(z)
 225:             while (1) {
 226:                 $k++;
 227:                 if ($k > $kl) {
 228:                     $flag = FALSE;
 229:                     break;
 230:                 }
 231:                 $r = $zr * $zr - $zi * $zi + $cr;   //f(z)=Z*Z+A の実部
 232:                 $i = 2 * $zr * $zi + $ci;           //f(z)=Z*Z+A の虚部
 233:                 if (($r * $r + $i * $i> 4) {      //Znが発散した場合は描画
 234:                     $flag = TRUE;
 235:                     break;
 236:                 }
 237:                 $zr = $r;
 238:                 $zi = $i;
 239:             }
 240:             //描画
 241:             if ($flag) {
 242:                 list($r, $g, $b) = hsv2rgb($k / $kl, 1.0, 1.0);
 243:                 $color = imagecolorallocate($img, (int)($r * 255), (int)($g * 255), (int)($b * 255));
 244:                 $x = (SIZE / ($re - $rs)) * ($cr - $rs);
 245:                 $y = (SIZE / ($ie - $is)) * ($ci - $is);
 246:                 imagesetpixel($img, (int)$x, (int)$y, $color);
 247:             }
 248:         }
 249:     }
 250: }

ユーザー関数 drawMandelbrot は、冒頭の複素数列の定義にしたがってマンデルブロ集合を描くものである。

解説:HSV→RGB変換

 160: /**
 161:  * HSV→RGB変換
 162:  * @param   resource $img イメージストリーム
 163:  * @param   double ($x1, $y1) - ($x1, $y2)  線分の座標
 164:  * @return  -
 165: */
 166: function hsv2rgb($h, $s, $v) {
 167:     $r = $g = $b = $v;
 168:     if ($s > 0.0) {
 169:         $h *= (float)6.0;
 170:         $i = (int)$h;
 171:         $f = $h - (float)$i;
 172:         switch ($i) {
 173:             default:
 174:             case 0:
 175:                 $g *1 - $s * (1 - $f);
 176:                 $b *1 - $s;
 177:                 break;
 178:             case 1:
 179:                 $r *1 - $s * $f;
 180:                 $b *1 - $s;
 181:                 break;
 182:             case 2:
 183:                 $r *1 - $s;
 184:                 $b *1 - $s * (1 - $f);
 185:                 break;
 186:             case 3:
 187:                 $r *1 - $s;
 188:                 $g *1 - $s * $f;
 189:                 break;
 190:             case 4:
 191:                 $r *1 - $s * (1 - $f);
 192:                 $g *1 - $s;
 193:                 break;
 194:             case 5:
 195:                 $g *1 - $s;
 196:                 $b *1 - $s * $f;
 197:                 break;
 198:         }
 199:     }
 200:     return array($r, $g, $b);
 201: }

マンデルブロ集合の着色はレインボーカラーを採用した。
レインボーカラーは、HSV色空間において色相(H)だけ変化するものである。そこで、ユーザー関数 hsv2rgb を用意し、HSV色空間からRGBへ変換することができるようにした。

参考サイト

(この項おわり)
header