Метод Якоби предназначен для вычисления собственных значений и векторов симметричных матриц. Этот алгоритм я взял из "Справочника алгоритмов на языке Алгол" ( Уилкинсон, Райнш ) и переписал его на С++. Идея метода Якоби состоит в том, чтобы обнулять недиагональные элементы вращениями до тех пор, пока они все не обнулятся и получится диагональная матрица. После каждого вращения сумма квадратов внедиагональных элементов уменьшается, что приводит к сходимости процесса диагональности. В данном алгоритме в первых трёх проходах по матрице используется порог tresh ( обнуляются элементы большие по модулю, чем порог ), а в следующих проходах обнуляются все элементы подряд. Проходов делается не больше 50. В программе: a - это исходная матрица (n*n), d - массив (n) cобственных значений, v - массив собственных векторов (n указателей на массивы). Переменные c и s - это cos и sin угла поворота. В процессе работы наддиагональные элементы будут изменены, но их можно восстановить по поддиагональным. void jacobi ( const unsigned int n, double * const * a, double * d, double * const * v )
{
if ( n == 0 ) return;
double * b = new double[n+n];
double * z = b + n;
unsigned int i, j;
for ( i = 0; i < n; ++i )
{
z[i] = 0.;
b[i] = d[i] = a[i][i];
for ( j = 0; j < n; ++j )
{
v[i][j] = i == j ? 1. : 0.;
}
}
for ( i = 0; i < 50; ++i )
{
double sm = 0.;
unsigned int p, q;
for ( p = 0; p < n - 1; ++p )
{
for ( q = p + 1; q < n; ++q )
{
sm += fabs ( a[p][q] );
}
}
if ( sm == 0 ) break;
const double tresh = i < 3 ? 0.2 * sm / ( n*n ) : 0.;
for ( p = 0; p < n - 1; ++p )
{
for ( q = p + 1; q < n; ++q )
{
const double g = 1e12 * fabs ( a[p][q] );
if ( i >= 3 && fabs ( d[p] ) > g && fabs ( d[q] ) > g ) a[p][q] = 0.;
else
if ( fabs ( a[p][q] ) > tresh )
{
const double theta = 0.5 * ( d[q] - d[p] ) / a[p][q];
double t = 1. / ( fabs(theta) + sqrt(1.+theta*theta) );
if ( theta < 0 ) t = - t;
const double c = 1. / sqrt ( 1. + t*t );
const double s = t * c;
const double tau = s / ( 1. + c );
const double h = t * a[p][q];
z[p] -= h;
z[q] += h;
d[p] -= h;
d[q] += h;
a[p][q] = 0.;
for ( j = 0; j < p; ++j )
{
const double g = a[j][p];
const double h = a[j][q];
a[j][p] = g - s * ( h + g * tau );
a[j][q] = h + s * ( g - h * tau );
}
for ( j = p+1; j < q; ++j )
{
const double g = a[p][j];
const double h = a[j][q];
a[p][j] = g - s * ( h + g * tau );
a[j][q] = h + s * ( g - h * tau );
}
for ( j = q+1; j < n; ++j )
{
const double g = a[p][j];
const double h = a[q][j];
a[p][j] = g - s * ( h + g * tau );
a[q][j] = h + s * ( g - h * tau );
}
for ( j = 0; j < n; ++j )
{
const double g = v[j][p];
const double h = v[j][q];
v[j][p] = g - s * ( h + g * tau );
v[j][q] = h + s * ( g - h * tau );
}
}
}
}
for ( p = 0; p < n; ++p )
{
d[p] = ( b[p] += z[p] );
z[p] = 0.;
}
}
delete[] b;
}
Исходники находятся в файле mathem.cpp. Наверх |