Как быстро и точно получить собственные значения и собственные векторы?

Мне нужно вычислить собственные значения и собственные векторы большой матрицы (около 1000 * 1000 или даже больше). Matlab работает очень быстро, но не гарантирует точности. Мне нужно, чтобы это было довольно точно (примерно ошибка 1e-06 в порядке) и в течение разумного времени (час или два в порядке).

Моя матрица симметрична и довольно разрежена. Точные значения: единицы по диагонали, и по диагонали под главной диагональю, и по диагонали над ней. Пример:

Пример матрицы

Как я могу это сделать? С++ мне удобнее всего.


person Ella Sharakanski    schedule 29.10.2013    source источник
comment
Это вопрос не столько о программировании, сколько о математике. Взгляните на: mathoverflow.net/questions/131527/   -  person Dang Khoa    schedule 29.10.2013
comment
посмотрите на броненосец или eigen и используйте то, что лучше всего соответствует вашим потребностям   -  person pyCthon    schedule 29.10.2013
comment
eigen слишком медленный (или, может быть, у него есть параметры конфигурации, о которых я не знаю?). И это вопрос программирования, потому что я не пытаюсь написать это самостоятельно, я хочу использовать что-то готовое.   -  person Ella Sharakanski    schedule 29.10.2013


Ответы (3)


Ваша система трехдиагональная и (симметричная) Теплицевая матрица. Я предполагаю, что у eigen и Matlab eig есть особые случаи для обработки таких матриц. В этом случае существует решение для собственных значений (ссылка (PDF)). В Matlab для вашей матрицы это просто:

n = size(A,1);
k = (1:n).';
v = 1-2*cos(pi*k./(n+1));

Это можно дополнительно оптимизировать, заметив, что собственные значения сосредоточены вокруг 1, и поэтому нужно вычислить только половину из них:

n = size(A,1);
if mod(n,2) == 0
    k = (1:n/2).';
    u = 2*cos(pi*k./(n+1));
    v = 1+[u;-u];
else
    k = (1:(n-1)/2).';
    u = 2*cos(pi*k./(n+1));
    v = 1+[u;0;-u];
end

Я не уверен, как вы собираетесь работать быстрее и точнее (кроме выполнения шага уточнения с использованием собственных векторов и оптимизации) с помощью простого кода. Вышеприведенное должно иметь возможность очень легко переводиться на C++ (или использовать codgen для создания кода C/C++, использующего this или eig). Однако ваша матрица все еще плохо обусловлена. Просто помните, что оценки точности являются наихудшим случаем.

person horchler    schedule 29.10.2013
comment
@horchler Хороший аналитический ответ, +1. Однако разве в выражении v = 1-2*cos(pi*k./(n+1)) не должен стоять знак плюс вместо знака минус? (согласно статье) - person Eitan T; 29.10.2013
comment
@EitanT: статья в формате PDF и Википедия используют разные знаки. Я предполагаю, что это соглашение, поскольку оно просто меняет порядок собственных значений (cos - четная функция), что имеет значение только в том случае, если нужны собственные векторы. - person horchler; 29.10.2013
comment
Вау, это действительно здорово! Интересно, почему eigen этого не делает (я так предполагаю, потому что он слишком медленный). PDF также имеет аналитическую формулу для собственных векторов. - person Ella Sharakanski; 29.10.2013
comment
@EllaShar Хотя я абсолютно не обижаюсь на то, что не принимаю свой ответ, я действительно не вижу практической разницы между eig(A) и явным вычислением. Хотя eig(A) не такой молниеносный (но все же довольно быстрый), ему удается давать те же результаты (и, вероятно, используются те же оптимизации), и это это более понятный способ сделать это. Возможно, я что-то упускаю, но зачем прибегать к повторной реализации этого, если встроенная функция уже сделала это за вас? - person Eitan T; 29.10.2013
comment
@EllaShar: я ничего не знаю о eigen, но медлительность может быть связана с тем, что он не был скомпилирован против самых быстрых библиотек математики и линейной алгебры, с неправильным распределением памяти или, возможно, вы делаете что-то не так (вы бы чтобы задать другой вопрос). - person horchler; 29.10.2013
comment
Я не думаю, что это причина. eigen не нуждается в других библиотеках математики или линейной алгебры, и память не должна так сильно влиять (и я использовал ее просто как в примерах eigen). И я не думаю, что делаю что-то не так (ну, вы никогда не можете знать..). - person Ella Sharakanski; 29.10.2013

MATLAB не гарантирует точность

Я считаю это утверждение необоснованным. На каком основании вы говорите, что можете найти (значительно) более точную реализацию, чем усовершенствованные вычислительные алгоритмы MATLAB?

И... используя MATLAB eig, следующее вычисляется менее чем за полсекунды:

%// Generate the input matrix
X = ones(1000);
A = triu(X, -1) + tril(X, 1) - X;

%// Compute eigenvalues
v = eig(A);

Это быстро хорошо!

Мне нужно, чтобы это было довольно точно (примерно ошибка 1e-06 в порядке)

Помните, что точное решение собственных значений связано с нахождением корней характеристического многочлена. Эта конкретная матрица 1000 x 1000 очень плохо обусловлена:

>> cond(A)

ans =
    1.6551e+003

Общее практическое правило заключается в том, что для числа условия 10k< /i>, вы можете потерять до k цифр точности (помимо того, что было бы потеряно для численного метода из-за потери точности арифметического метода).

Поэтому в вашем случае я ожидаю, что результаты будут точными с точностью до приблизительной ошибки 10-3.

person Eitan T    schedule 29.10.2013
comment
Как это отвечает на вопрос? ОП хочет вычислить собственные значения на С++, вы показали ему, что MATLAB точен. - person Tyler Jandreau; 29.10.2013
comment
@TylerJandreau Вопрос заключается в точном вычислении собственных значений, и я делаю вывод, что если бы MATLAB был достаточно точным, этого было бы достаточно. Кажется, что невозможно численно вычислить собственные значения для этого типа матрицы с требуемой точностью, поэтому нет причин не удовлетворяться ответом MATLAB. Почему вы заминусовали мой ответ? - person Eitan T; 29.10.2013
comment
Спасибо! Но почему [V,D]=eig(A) и d=eig(A) дают разные собственные значения в качестве ответа и что мне следует использовать? Кроме того, я думаю, что потеря точности численного метода может быть большой, могу ли я это установить? Matlab нигде не говорит, насколько точным будет ответ, поэтому я не могу ему доверять. - person Ella Sharakanski; 29.10.2013
comment
@EllaShar Как упоминалось в ответе, вы можете оценить точность, используя номер условия. Я сомневаюсь, что вы сможете найти численный алгоритм заметно лучше, чем в MATLAB. Наиболее точным методом, вероятно, было бы решить это аналитически, но это не всегда возможно. - person Eitan T; 29.10.2013
comment
@EllaShar относительно результатов eig(A), документация MATLAB утверждает, что указание одного output дает вам вектор собственных значений, а указание двух дает диагональную матрицу собственных значений и матрицу собственных векторов. Если вы сделаете diag(D) после [V, D] = eig(A), вы получите тот же результат, что и d = eig(A). - person Eitan T; 29.10.2013
comment
Дело в том, что я не получил точно такого же результата (и да, я пробовал сортировать векторы). Почему так и какой из них точнее? Кроме того, номер условия ничего не гарантирует в отношении числового округления, поэтому у меня нет верхнего предела ошибки. - person Ella Sharakanski; 29.10.2013
comment
@EllaShar, что вы имеете в виду, говоря, что не получите точно такой же результат? Что касается числового округления, то оно незначительно по сравнению с алгоритмической точностью вычисления собственных значений (опять же, как уже упоминалось, я ожидаю, что в вашем случае оно будет около 1e-3). - person Eitan T; 29.10.2013
comment
Я имею в виду, что sort(diag(D)) не равно sort(d). Вы можете проверить это. Если вы уверены, что ошибка будет в районе 1e-3 и в любом случае не может быть намного лучше, то ваш ответ действительно хорош. Большое спасибо! - person Ella Sharakanski; 29.10.2013
comment
@EllaShar Ну, я проверил, что mean(abs(d - diag(D))) составляет около 1e-16, как и их отсортированные аналоги, а это значит, что их можно считать эквивалентными. Надеюсь, это помогло! - person Eitan T; 29.10.2013
comment
Очень помог! Вы знаете, в чем причина такой разницы? - person Ella Sharakanski; 29.10.2013
comment
@EllaShar 1e-16? Это ничтожно мало, зачем тратить на это время? :) - person Eitan T; 29.10.2013
comment
Ну мне просто было интересно :) - person Ella Sharakanski; 29.10.2013
comment
@EllaShar Что ж, я разделяю ваше мнение, что оба результата должны быть полностью идентичными, и мне непонятно, почему это не так (при условии, что они получены по одному и тому же алгоритму). С другой стороны, реализация MATLAB может включать математическую манипуляцию для одной из форм, что, в свою очередь, приводит к ошибкам округления. Однако, поскольку эти мельчайшие различия столь незначительны, вы можете рассматривать оба результата как идентичные для всех практических целей. - person Eitan T; 29.10.2013

Если вы не против использования сторонней библиотеки, я добился больших успехов, используя линейную алгебру Armadillo. библиотеки.

В приведенном ниже примере arma — это пространство имен, которое им нравится использовать, vec — вектор, mat — матрица.

arma::vec getEigenValues(arma::mat M) {
    return arma::eig_sym(M);
}

Вы также можете сериализовать данные непосредственно в MATLAB и наоборот.

person Tyler Jandreau    schedule 29.10.2013
comment
Можешь попробовать запустить это для меня? Я пытался использовать eigen, и это было слишком медленно. Спасибо! - person Ella Sharakanski; 29.10.2013
comment
Было бы быстрее использовать arma::eig_sym() напрямую, вместо включения в функцию. Просто используйте vec v = eig_sym(M);. Armadillo также имеет больше форм функции eig_sym(), например. используя алгоритм разделяй и властвуй, который намного быстрее для больших матриц. - person mtall; 29.10.2013
comment
Спасибо, но я не хочу зря качать armadillo, он должен быть быстрее библиотеки eigen? Будет лучше, если вы сможете запустить его и проверить. Каковы недостатки алгоритма «dc», насколько он будет неточен? И есть ли что-то подобное в eigen (который у меня уже есть)? Спасибо еще раз! - person Ella Sharakanski; 29.10.2013