倭算数理研究所

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

commons-math 解読 (4) : MathUtils に定義されている static メソッド〜幾何学編〜

今回は幾何学的関数(造語)を見ていきます。 幾何学的関数として念頭に置いているのは、「距離」や「角度」に関するものです。 今回扱うメソッドは以下の通り:

メソッド 返り値の型 説明
distance1(int p1, int p2)
distance1(double p1, double p2)
int
double
マンハッタン距離 ( { L_1 } 距離)
distance(int p1, int p2)
distance(double p1, double p2)
double
double
ユークリッド距離 ( { L_2 } 距離)
distanceInf(int p1, int p2)
distanceInf(double p1, double p2)
int
double
チェビシェフ距離 ( { L_\infty } 距離)
normalizeArray(double[] values, double normalizedSum) double[] 配列の正規化
normalizeAngle(double a, double center) double 角度の正規化

距離関数

サイズが  { N }、要素が数字の配列  { x,\,y } に対して、 { L_n } 距離  { L_n(x,\,y) } は以下のように定義されます:

  { \displaystyle
    L_n(x,\,y) = \left(\sum_{k=0}^{N-1}\left|x_k-y_k\right|^n\right)^{1/n}\qquad
    x_k = x[k],\quad y_k = y[k]
}

 { N = 3 } の場合に、具体的な [tex:{ n } の値に対して式を書き下してみると

  { \displaystyle
\begin{align*}
    L_1(x,\,y) &= |x_0 - y_0|+|x_1 - y_1|+|x_2 - y_2| \\[2mm]
    L_2(x,\,y) &= \sqrt{(x_0 - y_0)^2+(x_1 - y_1)^2+(x_2 - y_2)^2}
\end{align*}
}

となります。 { L_2 } は普通の距離(ユークリッド距離)、{ L_1 }マンハッタン距離平安京のような碁盤の目状の空間の距離)と呼ばれる距離です。 また、{ L_\infty }{ n \rightarrow \infty } の極限で定められますが、これは実際には

  { \displaystyle L_\infty(x,\,y) = {\rm max}\{|x_0 - y_0|,\,|x_1 - y_1|,\,|x_2 - y_2|\} }

つまり、配列の各成分の差をとって、それらの絶対値のうちもっとも大きいものが { L_\infty } となります*1

import static java.lang.Math.sqrt
import static java.lang.Math.abs

def u = [1, 2, 3] as double[]
def v = [4, 5, 6] as double[]

assert MathUtils.distance1(u, v) == abs(u[0]-v[0]) + abs(u[1]-v[1]) + abs(u[2]-v[2])
assert MathUtils.distance(u, v) == sqrt((u[0]-v[0])**2 + (u[1]-v[1])**2 + (u[2]-v[2])**2)
assert MathUtils.distanceInf(u, v) == [abs(u[0]-v[0]), abs(u[1]-v[1]), abs(u[2]-v[2])].max()

配列の正規化

次は配列の正規化。 ここでの「正規化」とは、配列の成分の和で各成分を割る処理のようです。 数学では普通、こういった場合の正規化では何らかのノルムで割り算をしそうなものですが、このメソッドでは成分の単純な和で割り算をしています。

def u = [1, 2, 3] as double[]

def norm = u[0] + u[1] + u[2]
assert MathUtils.normalizeArray(u, 1d) == ([u[0]/norm, u[1]/norm, u[2]/norm] as double[])

成分の和が0になる場合([1, 2, -3] のような場合)は ArithmeticException が投げられるので注意。

角度の正規化

最後は角度の正規化。 例えば450°を90°に変換するような処理です。 ただし、このメソッドは角度を弧度法(ラジアン)で測りますが。 第2引数は結果を含める角度の範囲の中間値です。

  • 第2引数が Math.PI のとき、結果は [0, 2*Math.PI] の範囲内になります。
  • 第2引数が 0 のとき、結果は [-Math.PI, Math.PI] の範囲内になります。

第2引数には Math.PI, 0 以外の値も設定できますが、あまり使わないかと。

import static java.lang.Math.PI

assert MathUtils.normalizeAngle(1.2*PI, PI) == 1.2*PI
assert MathUtils.normalizeAngle(1.2*PI, 0d) == 1.2*PI - 2.0*PI

余談:距離関数の定義

{ \textbf{X} } をある集合する。 { \textbf{X} } の元2つを引数にとり実数値を返す関数 { d }

  { \displaystyle d(x,\,y) \qquad \left(x,\,y \in \textbf{X} \right) }

が以下のような性質を持つとき、{ d }{ \textbf{X} }距離関数といい、{ d(x,\,y) } の値を { x,\,y }距離と言う:

  • { d(x,\,x) = 0 }。 また { x \ne y } なら { d(x,\,y) > 0 }
  • { d(x,\,y) = d(y, x) }
  • { d(x,\,z) \le d(x,\,y) + d(y,\,z) } (三角不等式)

数学の基礎―集合・数・位相 (基礎数学)

数学の基礎―集合・数・位相 (基礎数学)

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

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

*1:高校数学IIIの「極限」の知識を使えば示せます。