矩阵的特征值与特征向量算法

矩阵特征值和特征向量定义

A为n阶矩阵,若数λ和n维非0列向量x满足Ax=λx,那么数λ称为A的特征值,x称为A的对应于特征值λ的特征向量。式Ax=λx也可写成( A-λE)x=0,并且|λE-A|叫做A 的特征多项式。当特征多项式等于0的时候,称为A的特征方程,特征方程是一个齐次线性方程组,求解特征值的过程其实就是求解特征方程的解。

依据普通线性代数中的概念,特征值和特征向量能够用传统的方法求得,可是实际项目中一般都是用数值分析的方法来计算。

这里介绍一下雅可比(Jacobi)迭代法求解特征值和特征向量。

雅可比(Jacobi)迭代法

雅克比方法用于求实对称阵的所有特征值、特征向量。Jacobi算法计算简单、稳定性好、精度高、
求得的特征向量正交性好。但当A为稀疏阵时,Givens旋转变换将破坏其稀疏性,且只能适用于
实对称矩阵。

相关知识

  • 矩阵A与相似矩阵 B = P A P-1的特征值相同。
  • 若矩阵Q满足QT Q = I,则称Q为正交矩阵。显然Q-1 = QT,且正交阵的乘积仍为正交阵。
  • 若A为实对称矩阵,则存在正交阵Q,使Q A QT = diag(λ1,λ2,...,λn),且QT的列是相应的特征向量。
  • 实对称矩阵的特征值均为实数,且存在标准正交的特征向量系。
  • Givens 旋转矩阵R(p,q,θ)是正交阵,其中


    Givens 旋转矩阵R

原理:

  • Jacobi 方法用平面旋转对实对称矩阵 A 做一系列旋转相似变换,从而将A约化为对角阵,进而求出特征值与特征向量。
  • R A RT 与A元素之间的关系:



    为使R A RT 为对角矩阵,可选择θ为:


当A为n阶实对称矩阵时,设A有非对角元,apq ≠ 0 ,设Givens 旋转矩阵R(p,q,θ)为:


令C = R A RT,则有:

若令C的非对角元素cpq = cqp = 0,则:

C与A的元素满足下列关系:



说明经旋转变换C = R A RT后,C的对角线元素平方和比A的对角线元素平方和增加了2apq2。而C的非对角元素平方和比A的非对角元素平方和减少了2apq2。如果不断的变换下去,则最后非对角元素可趋于0,即可通过一系列旋转变换,使A与一对角阵相似。

注意:某步化为0的元素在后续的步骤中可能又非0。但只要不断重复化0过程,则当K→∞时,非对角元素必趋于0。

基本思想:

将实对称矩阵A经一系列正交相似变换约化为一个近似的对角阵,从而该对角阵的对角元就是A的近似特征值,由各个正交变换矩阵乘积的转置可得对应的特征向量。


旋转阵Rk+1(k=0,1,2,...)的确定:

θ的计算

特征向量的计算


说明H的第i列就是A对应λi的标准正交特征向量的近似值。

Ak+1的计算

计算步骤

Jacobi法的收敛性

C代码实现

基于前面的C实现矩阵数据结构与计算里构造的矩阵数据结构与相应函数,用C实现了雅可比(Jacobi)迭代法求实对称矩阵的特征值与特征向量。

github源码 文件夹为Matrix,主要有两个文件:Matrix.c 、Matrix.h

设要求的矩阵为n阶的实对称矩阵,则相应的参数如下:

  • 设定最多的迭代次数为n*n*30,若迭代次数超过限制则退出迭代。
  • 设定精度要求为1e-10,若精度符合要求,也不再迭代。
  • 计算后得到的结果为n+1 X n 的矩阵对象,其中第一行为特征向量,每一个特征向量对应的下面的剩余的列为其特征向量。

相应代码如下:

//雅克比(Jacobi)方法实现实对称矩阵的特征值和特征向量的求解
//返回矩阵第一行为特征值,特征值下面的列为对应的特征向量
Matrix *getSymmetricMatrixEigen(Matrix *m){
    Matrix *resultm = NULL;
    Matrix *tempm = NULL;
    int nCount = 0,i,j;
    if(isSymmetricMatrix(m) == 0) return NULL;
    tempm = copyMatrix(m);
    if(!tempm) return NULL;
    resultm = creatIdentitySecondOrderMatrix(m->dshape);
    if(!resultm) return NULL;
    while(1){
        double dbMax = *(tempm->array + 1);
        int nRow = 0;
        int nCol = 1;
        for(i=0;i<tempm->dshape.shape[2];i++){ //在矩阵非对角线上找到最大的元素
            for(j=0;j<tempm->dshape.shape[3];j++){
                if(i != j){
                    double d = fabs(*(tempm->array + i*tempm->dshape.shape[3] + j)); 
                    if(d > dbMax){
                        dbMax = d;
                        nRow = i;
                        nCol = j;
                    }
                }
            }
        }
        if(dbMax < 1e-10) break; //精度符合要求,不再迭代
        if(nCount > tempm->dshape.shape[3] * tempm->dshape.shape[3] * 30) break; //迭代次数超过限制
        nCount++;
        double dbApp = *(tempm->array + nRow*tempm->dshape.shape[3] + nRow);
        double dbApq = *(tempm->array + nRow*tempm->dshape.shape[3] + nCol);
        double dbAqq = *(tempm->array + nCol*tempm->dshape.shape[3] + nCol);
        
        //计算旋转角度
        double dbAngle = 0.5*atan2(-2*dbApq,dbAqq-dbApp);
        double dbSinTheta = sin(dbAngle);
        double dbCosTheta = cos(dbAngle);
        double dbSin2Theta = sin(2*dbAngle);
        double dbCos2Theta = cos(2*dbAngle);
        
        *(tempm->array + nRow*tempm->dshape.shape[3] + nRow) = dbApp*dbCosTheta*dbCosTheta + 
            dbAqq*dbSinTheta*dbSinTheta + 2*dbApq*dbCosTheta*dbSinTheta;
        *(tempm->array + nCol*tempm->dshape.shape[3] + nCol) = dbApp*dbSinTheta*dbSinTheta + 
            dbAqq*dbCosTheta*dbCosTheta - 2*dbApq*dbCosTheta*dbSinTheta;
        *(tempm->array + nRow*tempm->dshape.shape[3] + nCol) = 0.5*(dbAqq-dbApp)*dbSin2Theta + dbApq*dbCos2Theta;
        *(tempm->array + nCol*tempm->dshape.shape[3] + nRow) = *(tempm->array + nRow*tempm->dshape.shape[3] + nCol);
        
        for(i=0;i<tempm->dshape.shape[3];i++){
            if((i!=nCol)&&(i!=nRow)){
                int u = i*tempm->dshape.shape[3] + nRow; // p
                int w = i*tempm->dshape.shape[3] + nCol; //q
                dbMax = *(tempm->array + u);
                *(tempm->array + u) = *(tempm->array + w) * dbSinTheta + dbMax * dbCosTheta; 
                *(tempm->array + w) = *(tempm->array + w) * dbCosTheta - dbMax * dbSinTheta;
            }
        }
        for(j=0;j<tempm->dshape.shape[3];j++){
            if((j!=nCol)&&(j!=nRow)){
                int u = nRow*tempm->dshape.shape[3] + j; //p
                int w = nCol*tempm->dshape.shape[3] + j; //q
                dbMax = *(tempm->array + u);
                *(tempm->array + u) = *(tempm->array + w) * dbSinTheta + dbMax * dbCosTheta; 
                *(tempm->array + w) = *(tempm->array + w) * dbCosTheta - dbMax * dbSinTheta;
            }
        }
        
        //计算特征向量
        for(i=0;i<resultm->dshape.shape[3];i++){
            int u = i*resultm->dshape.shape[3] + nRow; // p
            int w = i*resultm->dshape.shape[3] + nCol; //q
            dbMax = *(resultm->array + u);
            *(resultm->array + u) = *(resultm->array + w) * dbSinTheta + dbMax*dbCosTheta; 
            *(resultm->array + w) = *(resultm->array + w) * dbCosTheta - dbMax*dbSinTheta;
        }
    }
    Matrix *eigenVal = (Matrix *)malloc(sizeof(Matrix));
    if(!eigenVal) return NULL;
    eigenVal->dshape.shape[0] = 0;
    eigenVal->dshape.shape[1] = 0;
    eigenVal->dshape.shape[2] = 0;
    eigenVal->dshape.shape[3] = tempm->dshape.shape[3];
    eigenVal->length = tempm->dshape.shape[3];
    eigenVal->size = eigenVal->length;
    eigenVal->array = (double *)malloc(eigenVal->size*sizeof(double));
    if(!eigenVal->array){
        free(eigenVal);
        return NULL;
    }
    for(i=0;i<resultm->dshape.shape[3];i++){
        *(eigenVal->array + i) = *(tempm->array + i*tempm->dshape.shape[3] + i);
    }
    spliceSecondOrderMatrixRow(eigenVal,resultm);
    destroyMatrix(tempm);
    destroyMatrix(resultm);
    return eigenVal;
}

实测

注意,待求的矩阵必须是实对称矩阵。
testMatrix.c文件:

#include <stdio.h>
#include <malloc.h>
#include "Matrix.h"

int main(void){
    Matrix *m = NULL;
    Matrix *m2 = NULL;

    int a[]={0,0,3,3};
    double data[] = {1,1,1,1,2,10,1,10,100};

    Dshape dshape;
    initDshape(&dshape,a);
    
    m = creatMatrixFromDatas(data,9,dshape);
    printarray(m);
    printf("\n");

    m2 = getSymmetricMatrixEigen(m);
    printarray(m2);
    printf("\n");
    
    destroyMatrix(m);
    destroyMatrix(m2);
    return 0;
}

编译:

gcc Matrix.c Matrix.h testMatrix.c -o testMatrix

执行testMatrix,输出:


运行结果

即矩阵m的特征向量为
λ1 = 0.0946051, 对应的特征向量为:
[0.70746, -0.703906, 0.063376] T
λ2 = 1.8834,对应的特征向量为:
[0.706669, 0.703135, -0.0788656] T
λ3 = 101.022,对应的特征向量为:
[0.0109521, 0.10058, 0.994869] T

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容