ONLY DO WHAT ONLY YOU CAN DO

こけたら立ちなはれ 立ったら歩きなはれ

Go 言語で固有値を求める (ハウスホルダー法)

対称行列 f:id:fornext1119:20150805175030p:plain を, 以下のように分割して考える.

f:id:fornext1119:20150809173151p:plain
それに対応する, 以下のような対称行列 f:id:fornext1119:20150805175119p:plain を考える.

f:id:fornext1119:20150809173515p:plain
f:id:fornext1119:20150805175030p:plain に、左右から行列 f:id:fornext1119:20150805175119p:plain と転置行列 f:id:fornext1119:20150805175211p:plain をかける.

f:id:fornext1119:20150809173853p:plain

f:id:fornext1119:20150809174151p:plain
このとき

f:id:fornext1119:20150809175656p:plain
となるように f:id:fornext1119:20150805175119p:plain を設定して
処理を反復すると, 三重対角行列が得られる.

f:id:fornext1119:20150809180944p:plain
得られた三重対角行列から, QR分解によって, 固有値固有ベクトルを求める.

package main

import "fmt"
import "math"

const N = 4

// ハウスホルダー変換とQR分解で固有値を求める
func main() {
    var a [N][N]float64 = [N][N]float64{{5.0, 4.0, 1.0, 1.0},
                                        {4.0, 5.0, 1.0, 1.0},
                                        {1.0, 1.0, 4.0, 2.0},
                                        {1.0, 1.0, 2.0, 4.0}}
    var d               = []float64     {0.0 ,0.0 ,0.0 ,0.0}
    var e               = []float64     {0.0 ,0.0 ,0.0 ,0.0}

    // ハウスホルダー変換
    tridiagonalize(&a, d, e)

    // QR分解
    decomp(&a, d, e)

    fmt.Println("\neigenvalue")
    disp_vector(d)

    fmt.Println("\neigenvector")
    disp_matrix(a)
}

// ハウスホルダー変換
func tridiagonalize(a *[N][N]float64, d []float64, e []float64) {
    var v = []float64 {0.0 ,0.0 ,0.0 ,0.0}
    
    for k := 0; k < N - 2; k++ {
        var w = []float64 {0.0 ,0.0 ,0.0 ,0.0}

        // d[1..N] には対角成分をセット
        d[k] = a[k][k]

k=1 の場合
f:id:fornext1119:20150905160646p:plain
f:id:fornext1119:20150905165514p:plain

        var t float64 = 0.0
        for i := k + 1; i < N; i++ {
            w[i] =  a[i][k]
            t    += w[i] * w[i]
        }
        // 符号を a_{k+1,k} と揃える
        var s float64 = math.Sqrt(t)
        if (w[k + 1] < 0) {
            s = -s
        }
        // e[2..N] には隣の成分をセット
        // (s = 0 なら何もしない)
        if (math.Fabs(s) < 0.00000000001) {
            e[k + 1] = 0.0
        } else {
            e[k + 1]  = -s

f:id:fornext1119:20150906171934p:plain

            w[k + 1] +=  s
            s = 1 / math.Sqrt(w[k + 1] * s)
            for i := k + 1; i < N; i++ {
                w[i] *= s
            }
            for i := k + 1; i < N; i++ {
                s = 0.0
                for j := k + 1; j < N; j++ {
                    if (j <= i) {
                        s += a[i][j] * w[j]
                    } else {
                        s += a[j][i] * w[j]
                    }
                }
                v[i] = s
            }

            s = 0.0
            for i := k + 1; i < N; i++ {
                s += w[i] * v[i]
            }
            s /= 2.0
            for i := k + 1; i < N; i++ {
                v[i] -= s * w[i]
            }
            for i := k + 1; i < N; i++ {
                for j := k + 1; j < i + 1; j++ {
                    a[i][j] -= w[i] * v[j] + w[j] * v[i]
                }
            }
            // a には変換行列を上書き.
            // (固有ベクトルを求めないなら不要)
            for i := k + 1; i < N; i++ {
                a[i][k] = w[i]
            }
        }
    }

    // d[1..N] には対角成分をセット
    d[N - 2] = a[N - 2][N - 2]
    d[N - 1] = a[N - 1][N - 1]

    // e[2..N] には隣の成分をセット
    e[0]     = 0.0
    e[N - 1] = a[N - 1][N - 2]

    // a には変換行列を上書き.
    // (固有ベクトルを求めないなら不要)
    for k := N - 1; k >= 0; k-- {
        var w = []float64 {0.0 ,0.0 ,0.0 ,0.0}
        if (k < N - 2) {
            for i := k + 1; i < N; i++ {
                w[i] = a[i][k]
            }
            for i := k + 1; i < N; i++ {
                var s float64 = 0.0
                for j := k + 1; j < N; j++ {
                    s += a[i][j] * w[j]
                }
                v[i] = s
            }
            for i := k + 1; i < N; i++ {
                for j := k + 1; j < N; j++ {
                    a[i][j] -= v[i] * w[j]
                }
            }
        }
        for i := 0; i < N; i++ {
            a[i][k] = 0.0
        }
        a[k][k] = 1.0
    }

}

陰的なシフトを使ったQR分解 (らしい. 後で調べる)

// QR分解
func decomp(a *[N][N]float64, d []float64, e []float64) {
    e[0] = 1.0
    var h int = N - 1
    for (math.Fabs(e[h]) < 0.00000000001) {
        h--
    }
    for (h > 0) {
        e[0] = 0.0
        var l int = h - 1
        for (math.Fabs(e[l]) >= 0.00000000001) {
            l--
        }

        for j := 1; j < 100; j++ {
            var w float64 = (d[h - 1] - d[h]) / 2.0
            var s float64 = math.Sqrt(w * w + e[h] * e[h])
            if (w < 0.0) {
                s = -s
            }

            var x float64 = d[l] - d[h] + e[h] * e[h] / (w + s)
            var y float64 = e[l + 1]
            var z float64 = 0.0
            var t float64 = 0.0
            var u float64 = 0.0
            for k := l; k < h; k++ {
                if (math.Fabs(x) >= math.Fabs(y)) {
                    t = -y / x
                    u = 1 / math.Sqrt(t * t + 1.0)
                    s = t * u
                } else {
                    t = -x / y
                    s = 1 / math.Sqrt(t * t + 1.0)
                    if (t < 0) {
                        s = -s
                    }
                    u = t * s
                }
                w = d[k] - d[k + 1]
                t = (w * s + 2 * u * e[k + 1]) * s
                d[k    ] = d[k    ] - t
                d[k + 1] = d[k + 1] + t
                e[k    ] = u * e[k] - s * z
                e[k + 1] = e[k + 1] * (u * u - s * s) + w * s * u

                // 次の行は固有ベクトルを求めないなら不要
                for i := 0; i < N; i++ {
                    x = a[k    ][i]
                    y = a[k + 1][i]
                    a[k    ][i] = u * x - s * y
                    a[k + 1][i] = s * x + u * y
                }

                if (k < N - 2) {
                    x = e[k + 1]
                    y = -s * e[k + 2]
                    z = y
                    e[k + 2] = u * e[k + 2]
                }
            }

            fmt.Printf("%3d\t", j)
            disp_vector(d)

            // 収束判定
            if (math.Fabs(e[h]) < 0.00000000001) {
                break
            }
        }

        e[0] = 1.0
        for (math.Fabs(e[h]) < 0.00000000001) {
            h--
        }
    }

    // 次の行は固有ベクトルを求めないなら不要
    for k := 0; k < N - 1; k++ {
        var l int = k
        for i := k + 1; i < N; i++ {
            if (d[i] > d[l]) {
                l = i
            }
        }

        var t float64 = d[k]
        d[k] = d[l]
        d[l] = t

        for i := 0; i < N; i++ {
            t       = a[k][i]
            a[k][i] = a[l][i]
            a[l][i] = t
        }
    }
}

// 1次元配列を表示
func disp_vector(row[]float64) {
    for _, col := range row {
        fmt.Printf("%14.10f\t", col)
    }
    fmt.Println("")
}
// 2次元配列を表示
func disp_matrix(matrix[N][N]float64) {
    for row := 0; row < N; row++ {
        for col := 0; col < N; col++ {
            fmt.Printf("%14.10f\t", matrix[row][col])
        }
        fmt.Println()
    }
}
z:\go>8g GO1106.go

z:\go>8l -o GO1106.exe GO1106.8

z:\go>GO1106
  1       7.8421052632    3.1783029001    4.9795918367    2.0000000000
  2       8.4366343427    2.5633699270    4.9999957303    2.0000000000
  3       8.9326997461    2.0673002539    5.0000000000    2.0000000000
  4       9.2864656206    1.7135343794    5.0000000000    2.0000000000
  1      10.0000000000    1.0000000000    5.0000000000    2.0000000000

eigenvalue
 10.0000000000    5.0000000000    2.0000000000    1.0000000000

eigenvector
  0.6324555320    0.6324555320    0.3162277660    0.3162277660
  0.3162277660    0.3162277660   -0.6324555320   -0.6324555320
  0.0000000000   -0.0000000000   -0.7071067812    0.7071067812
  0.7071067812   -0.7071067812    0.0000000000    0.0000000000