矩阵的特征值和特征向量的雅克比算法C/C++实现

2023-05-16

矩阵的特征值和特征向量是线性代数以及矩阵论中非常重要的一个概念。在遥感领域也是经常用到,比如多光谱以及高光谱图像的主成分分析要求解波段间协方差矩阵或者相关系数矩阵的特征值和特征向量。

根据普通线性代数中的概念,特征值和特征向量可以用传统的方法求得,但是实际项目中一般都是用数值分析的方法来计算,这里介绍一下雅可比迭代法求解特征值和特征向量。

雅克比方法用于求实对称阵的全部特征值、特征向量。

对于实对称阵 A,必有正交阵 U,使

U TA U = D

其中 D 是对角阵,其主对角线元 li A 的特征值. 正交阵 U 的第 j 列是 A 的属于 li 的特征向量。

原理:Jacobi 方法用平面旋转对矩阵 A 做相似变换,化A 为对角阵,进而求出特征值与特征向量。

既然用到了旋转,这里就介绍一下旋转矩阵。

对于 p q,下面定义的 n 阶矩阵Upq 平面旋转矩阵。


容易验证 Upq是正交阵。对于向量xUpq x 相当于把坐标轴OxpOxq 于所在的平面内旋转角度 j .

变换过程: 在保证相似条件下,使主对角线外元素趋于零!

n 阶方阵A = [aij],  对 A 做下面的变换:

A1= UpqTAUpq,                         

          A1 仍然是实对称阵,因为,UpqT =Upq-1,知A1A 的特征值相同。

 

前面说了雅可比是一种迭代算法,所以每一步迭代时,需要求出旋转后新的矩阵,那么新的矩阵元素如何求,这里给出具体公式如下:


由上面的一组公式可以看到:

(1)矩阵A1 的第p 行、列与第 q 行、列中的元素发生了变化,其它行、列中的元素不变。

(2)p、q分别是前一次的迭代矩阵A的非主对角线上绝对值最大元素的行列号

(3) j是旋转角度,可以由下面的公式计算:


归纳可以得到雅可比迭代法求解矩阵特征值和特征向量的具体步骤如下:

(1) 初始化特征向量为对角阵V,即主对角线的元素都是1.其他元素为0。

(2)A的非主对角线元素中,找到绝对值最大元素 apq

(3) 用式(3.14)计算tan2j,求 cosj, sinj 及矩阵Upq .

(4) 用公式(1)-(4)求A1;用当前特征向量矩阵V乘以矩阵Upq得到当前的特征向量V。

(5) 若当前迭代前的矩阵A的非主对角线元素中最大值小于给定的阈值e时,停止计算;否则, 令A = A1 , 重复执行(2) ~ (5)。 停止计算时,得到特征值 li≈(A1) ij ,i,j= 1,2,…,n.以及特征向量V。

(6) 这一步可选。根据特征值的大小从大到小的顺序重新排列矩阵的特征值和特征向量。

 

到现在为止,每一步的计算过程都十分清楚了,写出代码也就不是难事了,具体代码如下:

/**
* @brief 求实对称矩阵的特征值及特征向量的雅克比法 
* 利用雅格比(Jacobi)方法求实对称矩阵的全部特征值及特征向量 
* @param pMatrix				长度为n*n的数组,存放实对称矩阵
* @param nDim					矩阵的阶数 
* @param pdblVects				长度为n*n的数组,返回特征向量(按列存储) 
* @param dbEps					精度要求 
* @param nJt					整型变量,控制最大迭代次数 
* @param pdbEigenValues			特征值数组
* @return 
*/
bool CPCAAlg::JacbiCor(double * pMatrix,int nDim, double *pdblVects, double *pdbEigenValues, double dbEps,int nJt)
{
	for(int i = 0; i < nDim; i ++) 
	{   
		pdblVects[i*nDim+i] = 1.0f; 
		for(int j = 0; j < nDim; j ++) 
		{ 
			if(i != j)   
				pdblVects[i*nDim+j]=0.0f; 
		} 
	} 

	int nCount = 0;		//迭代次数
	while(1)
	{
		//在pMatrix的非对角线上找到最大元素
		double dbMax = pMatrix[1];
		int nRow = 0;
		int nCol = 1;
		for (int i = 0; i < nDim; i ++)			//行
		{
			for (int j = 0; j < nDim; j ++)		//列
			{
				double d = fabs(pMatrix[i*nDim+j]); 

				if((i!=j) && (d> dbMax)) 
				{ 
					dbMax = d;   
					nRow = i;   
					nCol = j; 
				} 
			}
		}

		if(dbMax < dbEps)     //精度符合要求 
			break;  

		if(nCount > nJt)       //迭代次数超过限制
			break;

		nCount++;

		double dbApp = pMatrix[nRow*nDim+nRow];
		double dbApq = pMatrix[nRow*nDim+nCol];
		double dbAqq = pMatrix[nCol*nDim+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);

		pMatrix[nRow*nDim+nRow] = dbApp*dbCosTheta*dbCosTheta + 
			dbAqq*dbSinTheta*dbSinTheta + 2*dbApq*dbCosTheta*dbSinTheta;
		pMatrix[nCol*nDim+nCol] = dbApp*dbSinTheta*dbSinTheta + 
			dbAqq*dbCosTheta*dbCosTheta - 2*dbApq*dbCosTheta*dbSinTheta;
		pMatrix[nRow*nDim+nCol] = 0.5*(dbAqq-dbApp)*dbSin2Theta + dbApq*dbCos2Theta;
		pMatrix[nCol*nDim+nRow] = pMatrix[nRow*nDim+nCol];

		for(int i = 0; i < nDim; i ++) 
		{ 
			if((i!=nCol) && (i!=nRow)) 
			{ 
				int u = i*nDim + nRow;	//p  
				int w = i*nDim + nCol;	//q
				dbMax = pMatrix[u]; 
				pMatrix[u]= pMatrix[w]*dbSinTheta + dbMax*dbCosTheta; 
				pMatrix[w]= pMatrix[w]*dbCosTheta - dbMax*dbSinTheta; 
			} 
		} 

		for (int j = 0; j < nDim; j ++)
		{
			if((j!=nCol) && (j!=nRow)) 
			{ 
				int u = nRow*nDim + j;	//p
				int w = nCol*nDim + j;	//q
				dbMax = pMatrix[u]; 
				pMatrix[u]= pMatrix[w]*dbSinTheta + dbMax*dbCosTheta; 
				pMatrix[w]= pMatrix[w]*dbCosTheta - dbMax*dbSinTheta; 
			} 
		}

		//计算特征向量
		for(int i = 0; i < nDim; i ++) 
		{ 
			int u = i*nDim + nRow;		//p   
			int w = i*nDim + nCol;		//q
			dbMax = pdblVects[u]; 
			pdblVects[u] = pdblVects[w]*dbSinTheta + dbMax*dbCosTheta; 
			pdblVects[w] = pdblVects[w]*dbCosTheta - dbMax*dbSinTheta; 
		} 

	}

	//对特征值进行排序以及重新排列特征向量,特征值即pMatrix主对角线上的元素
	std::map<double,int> mapEigen;
	for(int i = 0; i < nDim; i ++) 
	{   
		pdbEigenValues[i] = pMatrix[i*nDim+i];

		mapEigen.insert(make_pair( pdbEigenValues[i],i ) );
	} 

	double *pdbTmpVec = new double[nDim*nDim];
	std::map<double,int>::reverse_iterator iter = mapEigen.rbegin();
	for (int j = 0; iter != mapEigen.rend(),j < nDim; ++iter,++j)
	{
		for (int i = 0; i < nDim; i ++)
		{
			pdbTmpVec[i*nDim+j] = pdblVects[i*nDim + iter->second];
		}

		//特征值重新排列
		pdbEigenValues[j] = iter->first;
	}

	//设定正负号
	for(int i = 0; i < nDim; i ++) 
	{
		double dSumVec = 0;
		for(int j = 0; j < nDim; j ++)
			dSumVec += pdbTmpVec[j * nDim + i];
		if(dSumVec<0)
		{
			for(int j = 0;j < nDim; j ++)
				pdbTmpVec[j * nDim + i] *= -1;
		}
	}

	memcpy(pdblVects,pdbTmpVec,sizeof(double)*nDim*nDim);
	delete []pdbTmpVec;

	return 1;
}


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

矩阵的特征值和特征向量的雅克比算法C/C++实现 的相关文章

随机推荐

  • Linux下如何设置程序的开机自启动功能

    文章目录 Linux下设置程序开机自启动1 etc rc local2 etc profile d 3 使用 crontab 方式4 使用 systemd 服务测试代码 Linux下设置程序开机自启动 在最后 xff0c 我放了我测试时候要
  • vnc连接不上,vnc连接不上是为什么?原因详解

    vnc连接不上的原因 xff0c 服务器作为网站建设的常用设备 xff0c 在服务器运行过程中起到举足轻重的作用 用户在选择服务器是常用的方式有服务器租用 虚拟主机租用以及服务器托管 xff0c 通过进行文件以及数据的下载 上传等实现网站的
  • window10安装vnc无法使用,window10安装vnc无法使用的原因和解决办法

    window10安装vnc无法使用的原因 xff0c 服务器作为网站建设的常用设备 xff0c 在服务器运行过程中起到举足轻重的作用 用户在选择服务器是常用的方式有服务器租用 虚拟主机租用以及服务器托管 xff0c 通过进行文件以及数据的下
  • 企业信息化技术架构展望

    企业用户慢慢的从前期选用一些同行业成功的项目案例来复制 xff0c 或者选用一些知名的咨询公司提出的行业内通用方案 xff0c 转型为自己主动认真思考什么是自己需求 xff0c 什么是真正适合自己的信息化之路 做为企业信息技术架构设计 xf
  • Errors were encountered while processing: google-chrome-stable

    转自 xff1a http omtlab com errors were encountered while processing google chrome stable Many people facing this issue whi
  • virtualbox 命令

    VBoxManage命令详解 xff08 一 xff09 本人对vboxmange命令按我个人的理解作了解释 xff0c 由于本人水平有限难免有错误的地方 xff0c 希望大家帮我指正 VBoxManage v version 显示virt
  • 基于Springboot的物业管理系统_代码

    下载地址 1 1 课题背景 目的及意义 1 1 1 课题背景 互联网 43 改变着我们的生活 xff0c 在传统的社区物业服务领域 xff0c 在服务业主 提升社区服务 质量方面 xff0c 如何与互联网融合 xff1f 顶级互联网技术团队
  • eclips运行generatorConfig.xml文件生成代码

    描述 xff1a 如何通过eclips工具来运行 generatorConfig xml 文件来自动生成代码并获取数据 xff08 类似于mybaits逆向生成 xff09 xff1f mybatis generator generate
  • Spring源码(4)Context篇之AbstractApplicationContext(下)

    上一篇 Spring源码 4 Context篇之AbstractApplicationContext xff08 上 xff09 讲解了Spring的AbstractApplicationContext类refresh 方法 xff0c 前
  • java多线程 一个生产者和多个消费者

    生产者和消费者模式是面试时很容易被问到的一类题 xff0c 在平常的开发中也经常碰到 xff0c 比如在网游开发中 xff1a 用一个线程把收到的字节数据封装起来写到一个队列中 xff0c 然后用一个或多个线程从该队列中把数据读取出来再分发
  • Python最强装逼神技!微信远程控制电脑,想让你电脑关机就关机!

    今天带给大家一个非常有意思的 python 程序 xff0c 基于 itchat 实现微信控制电脑 你可以通过在微信发送命令 xff0c 来拍摄当前电脑的使用者 xff0c 然后图片会发送到你的微信上 甚至你可以发送命令来远程关闭电脑 程序
  • JAVA 在linux下面生成验证码异常问题

    应用部署到Linux下 xff0c 结果首页登录的验证码显示不出来 该验证码是用java的图片包来处理图片的 xff0c 后台日志报错 xff1a java lang NoClassDefFoundError sun awt X11Grap
  • 【PowerShell 一天一练】 3. 调用winrar解压

    如果你安装了 winrar 且安装在对应下面的路径 xff0c 可以尝试一下下面的代码 xff08 事实上我也是 copy 并修改自网上的代码 xff09 unrar 61 c Program files winrar unrar exe
  • C/C++中二维数组和指针关系分析

    在C c 43 43 中 xff0c 数组和指针有着密切的关系 xff0c 有很多地方说数组就是指针式错误的一种说法 这两者是不同的数据结构 其实 xff0c 在C c 43 43 中没有所谓的二维数组 xff0c 书面表达就是数组的数组
  • 四叉树空间索引原理及其实现

    今天依然在放假中 xff0c 在此将以前在学校写的四叉树的东西拿出来和大家分享 四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构 它将已知范围的空间等分成四个相等的子空间 xff0c 如此递归下去 xff0c 直至树的层次达到一定
  • DirectXShaderCompiler mac编译

    Directxshader compiler mac编译 1 前置条件 Please make sure you have the following resources before building GitPython Version
  • intel -tbb 源码cmake构建

    cmake minimum required VERSION 3 0 0 FATAL ERROR set CMAKE CXX STANDARD 17 project tbb CXX add library tbb SHARED void c
  • iOS编译openmp

    1 下载openmp源码 https github com llvm llvm project releases download llvmorg 14 0 6 openmp 14 0 6 src tar xz 2 下载ios toolch
  • mysql存储过程及拼接字符串的用法

    DROP PROCEDURE IF EXISTS insert historytable DELIMITER CREATE PROCEDURE insert historytable BEGIN 定义判断变量 DECLARE 1 id va
  • 矩阵的特征值和特征向量的雅克比算法C/C++实现

    矩阵的特征值和特征向量是线性代数以及矩阵论中非常重要的一个概念 在遥感领域也是经常用到 xff0c 比如多光谱以及高光谱图像的主成分分析要求解波段间协方差矩阵或者相关系数矩阵的特征值和特征向量 根据普通线性代数中的概念 xff0c 特征值和