Метод Якоби вычисления собственных значений и векторов

Метод Якоби предназначен для вычисления собственных значений и векторов симметричных матриц. Этот алгоритм я взял из "Справочника алгоритмов на языке Алгол" ( Уилкинсон, Райнш ) и переписал его на С++. Идея метода Якоби состоит в том, чтобы обнулять недиагональные элементы вращениями до тех пор, пока они все не обнулятся и получится диагональная матрица. После каждого вращения сумма квадратов внедиагональных элементов уменьшается, что приводит к сходимости процесса диагональности. В данном алгоритме в первых трёх проходах по матрице используется порог 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.

Наверх

Hosted by uCoz