1

対称で実数の次の行列があります(ハミルトン演算子です):(Matlabフレンドリー)

[63.000000、-1.732051、0.000000、0.000000、0.000000、0.000000、0.000000、0.000000、0.000000、0.000000; -1.732051、61.000000、-2.000000、0.000000、-1.000000、0.000000、0.000000、0.000000、0.000000、0.000000; 0.000000、-2.000000、61.000000、-1.732051、0.000000、-1.414214、0.000000、0.000000、0.000000、0.000000; 0.000000、0.000000、-1.732051、63.000000、0.000000、0.000000、-1.732051、0.000000、0.000000、0.000000; 0.000000、-1.000000、0.000000、0.000000、61.000000、-1.414214、0.000000、0.000000、0.000000、0.000000; 0.000000、0.000000、-1.414214、0.000000、-1.414214、60.000000、-1.414214、-1.414214、0.000000、0.000000; 0.000000、0.000000、0.000000、-1.732051、0.000000、-1.414214、61.000000、0.000000、-2.000000、0.000000; 0.000000、0.000000、0.000000、0.000000、0.000000、-1.414214、0.000000、61.000000、-1.000000、0.000000; 0.000000、0.000000、0.000000、0.000000、0.000000、0.000000、-2.000000、-1.000000、61.000000、-1.732051; [

これらの値で JAMA 行列を作成し、固有値分解を実行すると、V*D*transpose(V) はハミルトンに等しくなりません。何がうまくいかないのか知っている人はいますか?固有値は MATLAB と一致していますが、固有ベクトルは一致していません。

ここにそれをテストするクラスがあります

public class TestJama {


public static void main(String[] args) {

    double[][] m = new double[][] {
            {63.000000, -1.732051, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000},
            { -1.732051, 61.000000, -2.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000},
            { 0.000000, -2.000000, 61.000000, -1.732051, 0.000000, -1.414214, 0.000000, 0.000000, 0.000000, 0.000000},
            { 0.000000, 0.000000, -1.732051, 63.000000, 0.000000, 0.000000, -1.732051, 0.000000, 0.000000, 0.000000},
            { 0.000000, -1.000000, 0.000000, 0.000000, 61.000000, -1.414214, 0.000000, 0.000000, 0.000000, 0.000000},
            { 0.000000, 0.000000, -1.414214, 0.000000, -1.414214, 60.000000, -1.414214, -1.414214, 0.000000, 0.000000},
            { 0.000000, 0.000000, 0.000000, -1.732051, 0.000000, -1.414214, 61.000000, 0.000000, -2.000000, 0.000000},
            { 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.414214, 0.000000, 61.000000, -1.000000, 0.000000},
            { 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, -2.000000, -1.000000, 61.000000, -1.732051},
            { 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.732051, 63.000000}
    };


    Matrix hamilton = new Matrix(m);
    System.out.println(jamaToString(hamilton));

    EigenvalueDecomposition e = hamilton.eig();
    System.out.println(jamaToString(e.getD()));
    System.out.println(jamaToString(e.getV()));

    Matrix recomb = e.getV().times(e.getD()).times(e.getV().transpose());
    System.out.println(jamaToString(recomb));

    System.out.println(hamilton.equals(recomb));

}

private static String jamaToString(Matrix m) {
    StringBuilder b = new StringBuilder();
    b.append("[");
    for(int i=0; i<m.getRowDimension(); i++) {

        for(int j=0; j<m.getColumnDimension(); j++) {
            b.append(m.get(i, j));
            if(j<m.getColumnDimension() - 1) b.append(",");
        }
        if(i<m.getRowDimension() - 1) b.append(";");
    }
    b.append("]");
    return b.toString();
}
}

編集:結果 (V*D*transpose(V)) は

63.1093 -0.6379 0.3400 -0.6576 0.0938 -0.0437 -0.6056 -0.5066 0.3463 0.5039 -0.6379 61.3082 -0.2368 -1.7813 -0.0851 0.7949 -0.1337 0.7668 -0.0422 -2.4329 0.3400 -0.2368 60.1481 1.3323 -0.4099 -1.8834 -0.5780 0.7516 0.0946 0.1705 -0.6576 -1.7813 1.3323 61.2455 0.0972 -0.8075 -0.9004 0.0242 0.3963 -1.2527 0.0938 -0.0851 -0.4099 0.0972 60.3086 -0.1899 0.0394 0.1987 -0.0484 -0.1495 -0.0437 0.7949 -1.8834 -0.8075 -0.1899 61.7941 0.3741 0.8237 0.7772 0.7557 -0.6056 -0.1337 -0.5780 -0.9004 0.0394 0.3741 60.6415 0.6351 0.7099 0.3349 -0.5066 0.7668 0.7516 0.0242 0.1987 0.8237 0.6351 62.8108 1.3507 1.3002 0.3463 -0.0422 0.09464 -0.04867772 0.7099 1.3507 63.3270 0.1244 0.5039 -2.4329 0.1705 -1.2527 -0.1495 0.7557 0.3349 1.3002 0.1244 60.3069

4

1 に答える 1

0

マトリックスの値のみについての場合: 正しいです。

編集:上記のステートメントは、結果のマトリックスが元の質問に挿入される前に書かれました。もちろん、このマトリックスは正しくありません。コメントによると、この間違ったマトリックスは、古い JAMA バージョンのバグが原因だったようです。JAMA 1.0.3 では動作しました。それにもかかわらず、この回答の残りの部分は依然として有効であり、質問に関連しています。

ここには 2 つの問題があります。まず、行列の内容を比較するためにequalsJAMA クラスのメソッドがオーバーライドされていません。そして、参照されたオブジェクトの同一性を比較するためのデフォルトの実装。こんな些細な比較でもMatrixequals

Matrix A = new Matrix(new double[][]{{1.0}});
Matrix B = new Matrix(new double[][]{{1.0}});
System.out.println(A.equals(B));

印刷しますfalse

2 番目の問題はかなり単純 (かつ一般的) ですdouble。値を使用した計算は無限に正確ではありません。これについては StackOverflow で多くの質問を見つけることができますが、Wikipedia で Floating Point Accuracy Problemsを参照することをお勧めします。( What Every Computer Scientist Should Know About Floating-Point Arithmeticに関する記事を推奨する人もいますが、かなり複雑です...)。

基本的な問題は、次の小さな例で簡単に再現できます。

double x = 0.1;
double y = 0;
for (int i=0; i<10; i++)
{
    y += x;
}
System.out.println(y+" == 1.0: "+(y==1.0));

この計算の結果はではなく y==1.0y==0.99999999999999...代わりに です。

そのため、要素単位の比較を実行するためにメソッドがオーバーライドされた場合でも、浮動小数点エラーのために比較は引き続き行われます。equalsfalse

この問題を軽減する 1 つの方法は、いくつかの値が「小さなイプシロンに等しい」かどうかを確認することです。比較する値の大きさがここで違いを生むため、これはまだ難しいかもしれませんが、1 つの解決策は、次の方法を使用してイプシロンが等しいか行列を比較することです。

private static boolean epsilonEqual(Matrix a, Matrix b)
{
    int ra = a.getRowDimension();
    int rb = b.getRowDimension();
    if (ra != rb)
    {
        return false;
    }
    int ca = a.getColumnDimension();
    int cb = b.getColumnDimension();
    if (ca != cb)
    {
        return false;
    }
    for (int c=0; c<ca; c++)
    {
        for (int r=0; r<ra; r++)
        {
            double ea = a.get(r, c);
            double eb = b.get(r, c);
            if (!epsilonEqual(ea, eb))
            {
                return false;
            }
        }
    }
    return true;
}

private static boolean epsilonEqual(double x, double y)
{
  final double epsilon = 1e-8;
  return Math.abs(x - y) <= epsilon;
}    
于 2014-05-01T17:03:27.033 に答える