一、算法原理
- 算法涉及数据:
矩阵V:存储特征向量。
矩阵A:表示需要求特征向量的实对称矩阵。 - 算法过程:
- (1)初始化V为对角矩阵,即主对角线的元素是1,其他元素都为0。
- (2)在A的非主对角线元素中,找到绝对值最大的元素 Apq ,即使用p、q代表A中绝对值最大元素的下标。
- (3)计算tan2φ、cosφ、sinφ以及矩阵U。其计算公式为:
我们遍历数组可以求得最大值Apq,即可得到下标值p和q。上式中apq即Apq,而aqq和qpp分别为App和Aqq,我们有p、q、A所以这些量都很好取得。对于矩阵U,四个特殊位置的值为三角函数值,其他对角线地方为1,非对角线地方为0。只有三角函数值的计算方法我们不知道了,这将在代码中展示。 - (4)Jacobi算法是一种迭代算法,每一次迭代会求出矩阵B,然后使用B作为新的A即目标矩阵继续进行迭代。矩阵B的计算方法如下:
使用矩阵B更新A后,我们使用特征向量矩阵V乘以矩阵U得到新的特征向量,使用这个新的特征向量更新V。 - (5)遍历矩阵A求出非对角线元素中的最大值k,如果k小于算法设定的阈值e,则停止计算,否则重复执行2-5。
- (6)当停止计算时,得到特征值L和特征向量V。可以根据特征值的大小顺序重新排列矩阵的特征值和特征向量。
二、算法实现
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)
{
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;
int w = i*nDim + nCol;
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;
int w = nCol*nDim + j;
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;
int w = i*nDim + nCol;
dbMax = pdblVects[u];
pdblVects[u] = pdblVects[w]*dbSinTheta + dbMax*dbCosTheta;
pdblVects[w] = pdblVects[w]*dbCosTheta - dbMax*dbSinTheta;
}
}
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;
}
三、算法原理