倭算数理研究所

科学・数学・学習関連の記事を、「倭マン日記」とは別に書いていくのだ!

倭マン's Math (8) : Complex (org.apache.commons.math.complex パッケージ)

今回は複素数を表す Complex クラスにメソッドを追加していきます。

インスタンス生成

まずはやっぱりインスタンス生成。 Complex クラスにも、分数 Fraction クラスに追加したようなメソッドを定義してますが、なんと言っても以下のようにインスタンス化できるのがエレガント!

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

// static import しておこう!
import static org.apache.commons.math.complex.Complex.I

WamansMath.enhance(CommonsMathPackage.COMPLEX)

// まるで言語レベルで複素数をサポートしてるようなインスタンス化!!
assert 1 + 2*I == new Complex(1d, 2d)

static import 文によって Complex.I を読み込む必要がありますが、それを行えば「1 + 2*I」という式で Complex オブジェクトが生成できます! ほとんど言語レベルで複素数をサポートしてるのと変わりませんね。

他にも(Number の)配列や String / GString から Complex オブジェクトを生成するメソッドも追加してます:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

import static org.apache.commons.math.complex.Complex.I

WamansMath.enhance(CommonsMathPackage.COMPLEX)

// Number の配列からインスタンス化
assert [1, 2].complex() == 1 + 2*I

// String, GString からインスタンス化
assert '3 + 4i'.complex() == 3 + 4*I
assert '5 + 6j'.complex('j') == 5 + 6*I
assert "7 + 8i".complex() == 7 + 8*I

// Complex に追加した parse() メソッドによって String からインスタンス化
assert Complex.parse('1 + 2i') == 1 + 2*I
assert Complex.parse('1 + 2j', 'j') == 1 + 2*I

虚数単位をお好みに合わせて 'j' にすることもできます。

メソッド使用サンプル

次はメソッドの使用方法。 これらは Fraction クラスに追加したメソッドと大体同じです。 少々変えてるのは ~ (bitwiseNegate) が複素共役を返すことくらいです。

  • 単項演算
  • Complex 同士の演算
  • Complex と Number の演算

単項演算

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

import static org.apache.commons.math.complex.Complex.I

WamansMath.enhance(CommonsMathPackage.COMPLEX)

def c = 1 + 3*I

// 符号
assert +c == 1 + 3*I
assert -c == -1 - 3*I

// 共役な複素数
assert ~c == 1 - 3*I

// 絶対値 返り値の型は double
assert c.abs() == sqrt(10)    // √1^2 + 3^2


Complex 同士の演算

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

import static org.apache.commons.math.complex.Complex.I

WamansMath.enhance(CommonsMathPackage.COMPLEX)

def c0 = 1 + 3*I
def c1 = 3 + 4*I

assert c0 + c1 == 4 + 7*I
assert c0 - c1 == -2 - I
assert c0 * c1 == -9 + 13*I
assert c0 / c1 == 0.6d+ 0.2d*I

Complex と Number の演算

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

import static org.apache.commons.math.complex.Complex.I

WamansMath.enhance(CommonsMathPackage.COMPLEX)

def c = 1 + 3*I

assert c + 1 == 2 + 3*I
assert c - 2 == -1 + 3*I
assert c * 3 == 3 + 9*I
assert c / 10 == 0.1d + 0.3d*I

assert 1 + c == 2 + 3*I
assert 2 - c == 1 - 3*I
assert 3 * c == 3 + 9*I
assert 4d / c == 0.4d - 1.2d*I

Groovy の力を借りるとかなり簡潔にコードが書けまする!

メソッド追加の詳細

一応、メソッド追加の詳細も載せておきます。 そのうち変更されてるかも知れないけど。

        // -1 を追加
        def _1 = new Complex(-1d, 0d)
        ComplexField.metaClass.getMinusOne = { -> _1 }
        Complex.metaClass.'static'.getMINUS_ONE = { -> _1 }

        // -i を追加
        def _I = new Complex(0d, -1d)
        ComplexField.metaClass.getMinusI = { -> _I }
        Complex.metaClass.'static'.getMINUS_I = { -> _I }

        // Complex にメソッドを追加
        Complex.metaClass.define{
            // 単項演算
            isMinusOne = { -> delegate == _1 }
            negative = { -> negate() }
            bitwiseNegate = { -> conjugate() }

            // Complex 同士の演算
            plus = { Complex c -> add(c) }
            minus = { Complex c -> subtract(c) }
            div = { Complex c -> divide(c) }
            power = { Complex c -> pow(c) }

            // Number との演算
            plus = { Number arg -> add(arg.complex()) }
            minus = { Number arg -> subtract(arg.complex()) }
            multiply = { Number arg -> multiply(arg.complex()) }
            div = { Number arg -> divide(arg.complex()) }
            power = { Number arg -> pow(arg.complex()) }

            // 型変換
            asType = { Class type ->
                switch(type){
                    case Complex:
                        return delegate
                    case String:
                        return ComplexFormat.instance.format(delegate)
                    case List:
                        return [getReal(), getImaginary()]
                    case double[]:
                        return [getReal(), getImaginary()] as double[]
                    default:
                        assert false
                }
            }
        }

        // Complex#parse() メソッド
        Complex.metaClass.'static'.parse = { String arg, String i = null ->
            (i == null) ? ComplexFormat.instance.parse(arg) : new ComplexFormat(i).parse(arg)
        }

        // Complex 以外に追加するメソッド
        Number.metaClass.define{
            complex = { new Complex(doubleValue(), 0d) }
            plus = { Complex c -> complex().add(c) }
            minus = { Complex c -> complex().subtract(c) }
            multiply = { Complex c -> complex().multiply(c) }
            div = { Complex c -> complex().divide(c) }
            power = { Complex c -> complex().pow(c) }
        }
        List.metaClass.complex = { -> new Complex(*delegate) }
        String.metaClass.complex = { String i = null -> Complex.parse(delegate, i) }
    }

Javaによるアルゴリズム事典

Javaによるアルゴリズム事典