1. 程式人生 > >矩陣的特徵值和特徵向量的雅克比演算法C/C++實現

矩陣的特徵值和特徵向量的雅克比演算法C/C++實現

矩陣的特徵值和特徵向量是線性代數以及矩陣論中非常重要的一個概念。在遙感領域也是經常用到,比如多光譜以及高光譜影象的主成分分析要求解波段間協方差矩陣或者相關係數矩陣的特徵值和特徵向量。

根據普通線性代數中的概念,特徵值和特徵向量可以用傳統的方法求得,但是實際專案中一般都是用數值分析的方法來計算,這裡介紹一下雅可比迭代法求解特徵值和特徵向量。

雅克比方法用於求實對稱陣的全部特徵值、特徵向量。

對於實對稱陣 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;
}


相關推薦

線性代數---矩陣---特徵值特徵向量

轉自:https://jingyan.baidu.com/article/27fa7326afb4c146f8271ff3.html 一、特徵值和特徵向量的定義 首先讓我們來了解一下特徵值和特徵向量的定義,如下: 特徵子空間基本定義,如下:

矩陣特徵值特徵向量

介紹特徵向量和特徵值在計算機視覺和機器學習中有許多重要的應用。眾所周知的例子是PCA(主成分分析)進行降維或人臉識別是特徵臉。特徵向量和特徵值的一個有趣應用在我的另一篇有關誤差橢圓的博文中提到。此外,特徵值分解形成協方差矩陣幾何解釋的基礎。在這篇文章中,我將簡單的介紹這個數

c語言實現求一個矩陣特徵值特徵向量

前言       求矩陣的特徵值,主要是用的QR分解,在我的有一次部落格裡,我已經詳細地給出了計算的過程,大家有興趣可以去看下,經過幾天的鑽研,終於完成了整個的eig演算法。下面我將把我的整個程式碼附上,有不懂的可以問我,歡迎一起討論學習!        這是對上一次

Jacobi迭代求矩陣特徵值特徵向量+C程式碼

Jacobi計算過程如下: 1. 選擇矩陣A非對角元中最大值A[i][j],運用公式 tan 2O = 2*A[i][j] / (A[i][i] -A[j][j]) 獲得選擇平面矩陣J,使J * A *J'=A1 2. 計算得到A1後,重複第1歩,計算A2 =J2 *A1

[matlab] eig函式求解矩陣特徵值特徵向量

在MATLAB中,計算矩陣A的特徵值和特徵向量的函式是eig(A),常用的呼叫格式有 5種: (1) E=eig(A):求矩陣A的全部特徵值,構成向量E。 (2) [V,D]=eig(A):求矩陣A的全部特徵值,構成對角陣D,並求A的特徵向量構成 V的列

矩陣特徵值特徵向量雅克演算法C/C++實現

矩陣的特徵值和特徵向量是線性代數以及矩陣論中非常重要的一個概念。在遙感領域也是經常用到,比如多光譜以及高光譜影象的主成分分析要求解波段間協方差矩陣或者相關係數矩陣的特徵值和特徵向量。根據普通線性代數中的概念,特徵值和特徵向量可以用傳統的方法求得,但是實際專案中一般都是用數值分

矩陣特徵分解介紹及雅克(Jacobi)方法實現特徵值特徵向量的求解(C++/OpenCV/Eigen)

對角矩陣(diagonal matrix):只在主對角線上含有非零元素,其它位置都是零,對角線上的元素可以為0或其它值。形式上,矩陣D是對角矩陣,當且僅當對於所有的i≠j, Di,j= 0. 單位矩陣就是對角矩陣,對角元素全部是1。我們用diag(v)表示一個對角元素由向量v

基於eigen計算矩陣特徵值特徵向量

有點小瑕疵,就是讀取檔案和生成檔案的位置!備註下本文是在Ubuntu系統下構建的,一些地方可能存在著差異。 1 首先是在如下的檔案下建立data.txt  這個檔案與源程式的檔案在同一個目錄下,其中data.txt的資料及其格式如下:  2 程式 #i

【線性代數】矩陣特徵分解、特徵值特徵向量(eigen-decomposition, eigen-value & eigen-vector)

就像我們可以通過質因數分解來發現整數的一些內在性質一樣(12 = 2 x 2 x 3),我們也可以通過分解矩陣來發現表示成陣列元素時不明顯的函式性質。 矩陣分解有種方式,常見的有 特徵分解 SVD 分解 三角分解 特徵分解 特徵分解是使用最廣泛的

雅可比演算法求方陣的全部特徵值特徵向量

ValVect.h #include <iostream> class ValVect { public : ValVect(void); void clear(void); //~ValVect(void); public : void rdOrM

關於特徵值特徵向量的意義

看K-L變換的時候對特徵值和特徵向量的作用有點搞不清,複習了一下計算過程後,整理思路如下: 求特徵值和特徵向量的過程: 1.計算行列式|λE-A|=0, 求出λ; 2.將λ帶入矩陣方程(λE-A)x=0,解出列向量x(特徵向量) 由於Ax = λx,故可以用λ和x表示A,從而達到壓縮資訊的目

線性代數之——特徵值特徵向量

線性方程 \(Ax=b\) 是穩定狀態的問題,特徵值在動態問題中有著巨大的重要性。\(du/dt=Au\) 的解隨著時間增長、衰減或者震盪,是不能通過消元來求解的。接下來,我們進入線性代數一個新的部分,基於 \(Ax=\lambda x\),我們要討論的所有矩陣都是方陣。 1. 特徵值和特徵向量 幾乎所有

花了10分鐘,終於弄懂了特徵值特徵向量到底有什麼意義

轉自 http://k.sina.com.cn/article_6367168142_17b83468e001005yrv.html 有振動 就有特徵值 今天,超模君看到了一句神翻譯: 嚇得超模君馬上放下手中的蘋果手機,來碼字了!之前有模友說想知道矩陣的特徵值和

講道理 | 特徵值特徵向量意義

在剛開始學的特徵值和特徵向量的時候只是知道了定義和式子,並沒有理解其內在的含義和應用,這段時間整理了相關的內容,跟大家分享一下; 首先我們先把特徵值和特徵向量的定義複習一下: 定義: 設A是n階矩陣,如果數λ和n維非零向量x使關係式 ……(1) 成立,那麼,這樣的

矩陣特徵值特徵向量及其求解

如果把矩陣看成運動,描述運動最重要的引數當屬運動的速度和方向。為了幫助大家理解,我們可以形象地認為:特徵值就是運動的速度,特徵向量就是運動的方向。 如果把矩陣看成運動,描述運動最重要的引數當屬運動的速

主成分分析PCA以及特徵值特徵向量的意義

定義: 主成分分析(Principal Component Analysis,PCA), 是一種統計方法。通過正交變換將一組可能存在相關性的變數轉換為一組線性不相關的變數,轉換後的這組變數叫主成分。PCA的思想是將n維特徵對映到k維上(k<n),這k維是全新的正交特徵

線性代數筆記22——特徵值特徵向量

特徵向量   函式通常作用在數字上,比如函式f作用在x上,結果得到了f(x)。線上性代數中,我們將x擴充套件到多維,對於Ax來說,矩陣A的作用就像一個函式,輸入一個向量x,通過A的作用,得到向量Ax。對多數向量x而言,經過Ax的轉換後將得到不同方向的向量,但總有一些特殊的向量,它的方向和Ax方向相同,即Ax

C++ Eigen庫計算矩陣特徵值特徵向量

C++Eigen庫程式碼 #include <iostream> #include <Eigen/Dense> #include <Eigen/Eigenvalues> using namespace Eigen

特徵值特徵向量的幾何物理意義

我們知道,矩陣乘法對應了一個變換,是把任意一個向量變成另一個方向或長度都大多不同的新向量。在這個變換的過程中,原向量主要發生旋轉、伸縮的變化。如果矩陣對某一個向量或某些向量只發生伸縮變換,不對這些向量產生旋轉的效果,那麼這些向量就稱為這個矩陣的特徵向量,伸縮的比例就是特徵

線性代數:特徵值特徵向量

線性代數:特徵值對於解方程組有什麼作用?特徵值特徵向量可以解2次型對稱矩陣的特徵值和特徵向量Let M be a square matrix. Let λ be a constant and e a nonzero column vector with the same nu