Arantium Maestum

プログラミング、囲碁、読書の話題

Quilでフラクタル Mandelbrot

「関数型オブジェクト指向AIプログラミング」に載っていなかったのでやっていなかったが、フラクタルで一番有名なものは多分マンデルブロだろう。

というわけでこれもClojure&Quilで書いてみる。

今まで書いてきたフラクタルに比べてかなり複雑な概念である。複素数を二次元で表して(いわゆる複素平面)、マンデルブロ集合に含まれる点に色をつけていくと出来上がる。

とりあえずnamespaceの定義:

(ns mandelbrot
  (:require [quil.core :as q]
            [quil.middleware :as m]))

複素数をベクトルで表すことにするので、それを踏まえて複素数の足し算・掛け算・絶対値の二乗を定義する:

(defn +c [[a i] [b j]]
  [(+ a b) (+ i j)])

(defn *c [[a i] [b j]]
  [(- (* a b) (* i j)) 
   (+ (* a j) (* b i))])

(defn abs-sq-c [[a i]]
  (+ (* a a) (* i i)))

マンデルブロ集合の定義から:

(defn Pc [c]
  (fn [z] (+c (*c z z) c)))

(defn mandelbrot? [c]
  (->> (iterate (Pc c) [0 0])
       (take 100)
       (not-any? #(< 4 (abs-sq-c %)))))

take 100の部分は、大きくすればするほど精度は上がるが計算に時間がかかるようになる。無限大に発散するかどうかを絶対値が2を超えるかどうかで近似している。

ここまでくれば後は描画するだけ。

(def ssize 300)

(def mandelbrots
  (doall (filter mandelbrot?
    (for [a (range -2 1 (/ 1.0 ssize))
          i (range -1 1 (/ 1.0 ssize))]
      [a i]))))

(defn setup []
  (q/frame-rate 1000)
  (q/background 255 255 255))

(defn draw []
  (q/push-matrix)
  (q/stroke 50 0 100)
  (q/translate (* 2 ssize) (* 1 ssize))
  (doseq [c mandelbrots]
    (apply q/point (map #(* % ssize) c)))
  (q/pop-matrix)
  (q/save "mandelbrot.png"))

(q/defsketch ant-sim
   :size [(* 3 ssize) (* 2 ssize)]
   :setup setup
   :draw draw)

結果がこれ:

f:id:zehnpaard:20160525220036p:plain

しかし、何故このマンデルブロ集合の定義がフラクタルを作り出すのか全然理解していない。不思議なものである。