Goで行列のランクを取得する簡単な方法がパッと調べて出てこなかったのでメモ。
行列のランクはその非零な特異値の個数と一致することが知られているので、行列を特異値分解(Singular Value Decomposition; SVD)し、非零な特異値の個数を求めれば良い。
便利なことに、Goではgonum.org/v1/gonum/matに特異値分解専用のstruct mat.SVD
があるので、それを利用する。
// 浮動小数点のずれを考慮して、十分小さい値を0とみなす
const epsilon = 1e-15
func rank(m mat.Matrix) int {
svd := new(mat.SVD)
// 特異値分解
svd.Factorize(m, mat.SVDNone)
// 特異値を取得
svs := svd.Values(nil)
// 非零な特異値を数えて返す
var count int
for _, v := range svs {
if v > epsilon {
count++
}
}
return count
}
これだけでランクを得ることができる。以下は検証用コード。
func main() {
m := mat.NewDense(4, 5, nil)
m.SetRow(0, []float64{1, 1, 1, 1})
m.SetRow(1, []float64{0, 1, 1, 1})
m.SetRow(2, []float64{0, 0, 1, 1})
m.SetRow(3, []float64{0, 0, 0, 1})
m.SetRow(4, []float64{0, 0, 0, 1})
fmt.Println(rank(m)) // 4
m.SetRow(3, []float64{0, 0, 1, 1})
m.SetRow(4, []float64{0, 0, 1, 1})
fmt.Println(rank(m)) // 3
}