JAMA не вычисляет правильные собственные векторы

1

У меня есть следующая матрица, симметричная и действительная (это гамильтонов оператор): (Matlab friendly)

[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]

Если я создаю 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();
}
}

EDIT: результат (V * D * транспонирование (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 [ CN00].7813 1.3323 61.2455 0.0972 -0.8075 -0.9004 0,0242 0,3363 -1.2527 0,0938 -0.0851 -0.4099 0,0972 60,3086 -0.1899 0,0394 0,1987 -0.0484 [ CN024].1495 -0.0437 0,7949 -1.8834 -0.8075 -0.1899 61,7941 0,3774 0,8237 0,7772 0,7757 -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.0946 0.3963 -0.0484 0.7772 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

  • 0
    Если вывод, который вы вставили, действительно является результатом печати recomb , то, похоже, что-то не так. Какую версию JAMA вы используете? Я только что проверил его с версией 1.0.3, и там "epsilonEquals" (из моего ответа ниже) вернул true - что не было бы так, если recomb матрица recomb выглядела как та, которую вы опубликовали ...
  • 0
    Похоже, новая версия работает. Я не уверен, какую старую версию я использовал, но большое спасибо !!!
Теги:
jama

1 ответ

0

Если речь идет только о значении матрицы: это правильно.

EDIT: вышеприведенное выражение было написано до того, как результирующая матрица была вставлена в исходный вопрос. Конечно, эта матрица неверна. Согласно комментариям, кажется, что эта неправильная матрица была вызвана некоторой ошибкой в более старой версии JAMA. С JAMA 1.0.3 он работал. Остальная часть этого ответа по-прежнему актуальна и актуальна для вопроса:

Здесь есть две проблемы. Прежде всего, метод equals класса JAMA Matrix не переопределяется для сравнения содержимого матриц. И стандартная реализация equals сравнивается для идентичности упомянутых объектов. Поэтому даже тривиальное сравнение, подобное этому

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

будет печатать false.

Вторая проблема довольно простая (и общая): вычисления с double значениями не являются бесконечно точными. Вы можете найти много вопросов об этом в StackOverflow, но, возможно, захотите взглянуть на Википедию о проблемах с точки зрения точности с плавающей точкой. (Некоторые люди рекомендуют статью о том, что каждый компьютерный ученый должен знать о арифметике с плавающей точкой, но она скорее участвует...).

Основная проблема может быть легко воспроизведена с помощью этого небольшого примера:

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.0, а y==0.99999999999999... вместо этого.

Таким образом, даже если метод equals был переопределен для выполнения элементарного сравнения, сравнение все равно дало бы false из-за ошибки с плавающей запятой.

Одним из способов облегчения этой проблемы является проверка того, являются ли некоторые значения "равными до небольшого эпсилона". Это может быть сложно, потому что значение сравниваемых значений будет иметь здесь значение, но одним из решений может быть использование следующего метода для сравнения матриц для epsilon-равенства:

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;
}    
  • 0
    Спасибо за этот очень хороший ответ. Глядя на явный результат, я бы сказал, что это не из-за компьютерной точности? Не должно быть, я думаю, когда, например, MATLAB может это сделать?
  • 0
    @gedemagt Существуют способы решения таких вычислительных задач с большей точностью. В Java теоретически возможно выполнить вычисления «произвольной точности» с java.math.BigDecimal (но вы не должны использовать его в таком случае, это очень непрактично). Я не достаточно знаком с MATLAB, чтобы знать, как он работает внутри, и что позволяет думать, что это правильно, но ... я добавлю еще один комментарий к исходному вопросу ...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню