Scalaで複素数とマンデルブロート集合

Scalaで複素数とマンデルブロート集合

よくある題材です。
四則演算を作ろうとしたとき、引数が Complex のやつと Double のやつの2種類を作ろうと思ったけど、それだとメソッドが4つから8つになってしまう。
なので、初めて implicit を使ってみました。
あっさりできました。


で、四則演算ができれば、マンデルブロート集合も計算できるので、複素数版を作ってみました。
ちゃんと動きました。
計算式は、マンデルブロート集合の公式そのものです。シンプル♪

package asanmath

/**
 * 複素数を作ってみる。immutableで。
 */
case class Complex(val re:Double, val im:Double) {
    def this(re:Double) = this(re, 0)

    lazy val norm = re * re + im * im
    lazy val abs = math.sqrt(norm)
    /** 複素共役 */
    lazy val conjugate = Complex(re, -im)

    // 四則演算
    def +(that:Complex) = Complex(this.re + that.re, this.im + that.im)
    def -(that:Complex) = Complex(this.re - that.re, this.im - that.im)
    def *(that:Complex) = Complex(
            this.re * that.re - this.im * that.im,
            this.re * that.im + this.im * that.re)
    def /(that:Complex) = Complex(
            (this.re * that.re + this.im * that.im) / that.norm,
            (this.im * that.re - this.re * that.im) / that.norm)

    override def toString() = "%f%+fi".format(re, im)
}

object Complex {
    lazy val Zero = Complex(0, 0)
    implicit def Real2Complex(x: Double) = Complex(x, 0)
}

/** 複素数でマンデルブロート集合を作ってみる */
object ComplexMandelbrotTest {
    import java.awt.Color
    import java.awt.image.BufferedImage
    import java.io.File
    import javax.imageio.ImageIO
    /**
     * マンデルブロートの各点において、z^2 > 4 になるまでの回数を計算する。
     * maxCount回を超えると-1を返す。
     */
    def calcMandelbrot(c:Complex, maxCount:Int):Int = {
        var z = Complex.Zero
        for (i <- 0 until maxCount) {
            if (z.norm >= 4.0) return i
            z = z * z + c
        }
        -1
    }
    /**
     * 画像を作成する。
     */
    def createImage(m:Complex, zoom:Double, maxCount:Int):BufferedImage = {
        var image = new BufferedImage(500, 500, BufferedImage.TYPE_3BYTE_BGR)
        for (y <- 0 until image.getHeight; x <- 0 until image.getWidth) {
            val c = Complex(
                    m.re + (x-image.getWidth  / 2) / zoom,
                    m.im - (y-image.getHeight / 2) / zoom)
            val count = calcMandelbrot(c, maxCount)
            image.setRGB(x, y, 
                if (count != -1) {
                    Color.HSBtoRGB(count/100.0f, 1.0f, 1.0f)
                } else {
                    0   // BLACK
                })
        }
        image
    }
    def main(args:Array[String]) {
//      val image = createImage(Complex(-0.745428, 0.113009), 100, 100)
//      val image = createImage(Complex(-0.745428, 0.113009), 1000, 100)
//      val image = createImage(Complex(-0.745428, 0.113009), 10000, 500)
//      val image = createImage(Complex(-0.745428, 0.113009), 100000, 1000)
//      val image = createImage(Complex(-0.745428, 0.113009), 1000000, 1000)
//      val image = createImage(Complex(-0.745428, 0.113009), 10000000, 1500)
        val image = createImage(Complex(-0.745428, 0.113009), 100000000, 2000)  // 13秒ぐらい
        ImageIO.write(image, "png", new File("ComplexMandelbrotTest.png"))
    }       
}

/** テスト */
object ComplexTest {
    def main(args: Array[String]) {
        val a = new Complex(3, -4)
        println("a = " + a)             // "a = 3.000000-4.000000i"
        println("a.im = " + a.im)       // -4.0
        println("a.norm = " + a.norm)   // 25.0
        println("a.abs = " + a.abs)     // 5.0 ピタゴラスの三角形
        val b = Complex(1,1)
        println("b = " + b)             
        println("b.abs = "+ b.abs)      // 1.414213562373095i   √2
        println("a + b = "+ (a + b))    // 4.000000-3.000000i
        println("a - b = "+ (a - b))    // 2.000000-5.000000i
        println("a - 1 = "+ (a - 1))    // 2.000000-4.000000i
        println("a * 2 = "+ (a * 2))    // 6.000000-8.000000i
        println("a * b = "+ (a * b))    // 7.000000-1.000000i
        println("a / b = "+ (a / b))
        ComplexMandelbrotTest.main(args)
    }
}