zoukankan      html  css  js  c++  java
  • 使用MTL库求解矩阵特征值和特征向量

    关于矩阵的特征值和特征向量求解,大部分的数学运算库都进行了提供,下面是使用MTL库的接口进行封装。

    #include <mtl/matrix.h>
    #include <mtl/mtl.h>
    #include <mtl/dense1D.h>
    #include <mtl/utils.h>
    #include <mtl/lu.h>
    
    /*! 对角阵 */
    typedef mtl::matrix <double, mtl::diagonal<>, mtl::dense<>, mtl::row_major>::type DiagMatrix;
    /*! 对称阵 */
    typedef mtl::matrix <double, mtl::symmetric<mtl::lower>, mtl::packed<mtl::external>, mtl::column_major>::type SymmMatrix;
    /*! 标准矩阵,数值存在矩阵中 */
    typedef mtl::matrix <double, mtl::rectangle<>, mtl::dense<>, mtl::row_major>::type Matrix;
    /*! 标准矩阵,数值存在外部 */
    typedef mtl::matrix <double, mtl::rectangle<>, mtl::dense<mtl::external>, mtl::row_major>::type ExtMatrix;
    /*! 标准向量,数值存在矩阵中 */
    typedef mtl::dense1D<double> Vector;
    /*! 标准向量,数值存在外部 */
    typedef mtl::external_vec<double> ExtVector;
    
    /**
    * @brief 求实对称矩阵的特征值及特征向量
    * @param MMatrix	所求的矩阵
    * @param EigenValues	矩阵的特征值
    * @param EigenVectors	矩阵的特征向量(按照列来存),如果特征值的序号为1,那么对应的特征向量为第1列
    * @param Percent	矩阵的特征值百分比
    * @param AccPercent	矩阵的特征值累积百分比
    * @param deps		累计误差
    * @return 返回值小于0表示超过迭代jt次仍未达到精度要求,返回值大于0表示正常返回 
    */ 
    bool GetMatrixEigen(Matrix MMatrix, Vector &EigenValues, Matrix &EigenVectors, Vector *Percent = NULL, Vector *AccPercent = NULL, double deps = 0.00000001);
    
    下面是求解矩阵特征值和特征向量的实现:

    /**
    * @brief 求实对称矩阵的特征值及特征向量的雅格比法 
    * 利用雅格比(Jacobi)方法求实对称矩阵的全部特征值及特征向量 
    * @param a		长度为n*n的数组,存放实对称矩阵,返回时对角线存放n个特征值 
    * @param n		矩阵的阶数 
    * @param v		长度为n*n的数组,返回特征向量(按列存储) 
    * @param eps	控制精度要求 
    * @param jt		整型变量,控制最大迭代次数 
    * @return 返回false表示超过迭代jt次仍未达到精度要求,返回true表示正常返回 
    */ 
    bool Eejcb(double a[], int n, double v[], double eps, int jt)
    { 
    	int i,j,p,q,u,w,t,s,l; 
    	double fm,cn,sn,omega,x,y,d; 
    
    	l=1; 
    	//初始化特征向量矩阵使其全为0 
    	for(i=0; i<=n-1; i++) 
    	{   
    		v[i*n+i] = 1.0; 
    		for(j=0; j<=n-1; j++) 
    		{ 
    			if(i != j)   
    				v[i*n+j]=0.0; 
    		} 
    	} 
    
    	while(true)   //循环 
    	{   
    		fm = 0.0; 
    		for(i=0; i<=n-1; i++)   //找出,矩阵a(特征值),中除对角线外其他元素的最大绝对值 
    		{ 
    			//这个最大值是位于a[p][q] ,等于fm 
    			for(j=0; j<=n-1; j++) 
    			{ 
    				d = fabs(a[i*n+j]); 
    
    				if((i!=j) && (d> fm)) 
    				{ 
    					fm = d;   
    					p = i;   
    					q = j; 
    				} 
    			} 
    		} 
    
    		if(fm < eps)     //精度复合要求 
    			return true; //正常返回 
    
    		if(l > jt)       //迭代次数太多 
    			return false;//失败返回 
    
    		l ++;       //   迭代计数器 
    		u = p*n + q; 
    		w = p*n + p;   
    		t = q*n + p;   
    		s = q*n + q; 
    		x = -a[u]; 
    		y = (a[s]-a[w])/2.0;		//x y的求法不同 
    		omega = x/sqrt(x*x+y*y);	//sin2θ 
    
    		//tan2θ=x/y = -2.0*a[u]/(a[s]-a[w]) 
    		if(y < 0.0) 
    			omega=-omega; 
    
    		sn = 1.0 + sqrt(1.0-omega*omega);   
    		sn = omega /sqrt(2.0*sn);		//sinθ 
    		cn = sqrt(1.0-sn*sn);			//cosθ 
    
    		fm = a[w];   //   变换前的a[w]   a[p][p] 
    		a[w] = fm*cn*cn + a[s]*sn*sn + a[u]*omega; 
    		a[s] = fm*sn*sn + a[s]*cn*cn - a[u]*omega; 
    		a[u] = 0.0; 
    		a[t] = 0.0; 
    
    		//   以下是旋转矩阵,旋转了了p行,q行,p列,q列 
    		//   但是四个特殊点没有旋转(这四个点在上述语句中发生了变化) 
    		//   其他不在这些行和列的点也没变 
    		//   旋转矩阵,旋转p行和q行 
    		for(j=0; j<=n-1; j++) 
    		{ 
    			if((j!=p) && (j!=q)) 
    			{ 
    				u = p*n + j; 
    				w = q*n + j; 
    				fm = a[u]; 
    				a[u] = a[w]*sn + fm*cn; 
    				a[w] = a[w]*cn - fm*sn; 
    			} 
    		} 
    
    		//旋转矩阵,旋转p列和q列 
    		for(i=0; i<=n-1; i++) 
    		{ 
    			if((i!=p) && (i!=q)) 
    			{ 
    				u = i*n + p;   
    				w = i*n + q; 
    				fm = a[u]; 
    				a[u]= a[w]*sn + fm*cn; 
    				a[w]= a[w]*cn - fm*sn; 
    			} 
    		} 
    
    		//记录旋转矩阵特征向量 
    		for(i=0; i<=n-1; i++) 
    		{ 
    			u = i*n + p;   
    			w = i*n + q; 
    			fm = v[u]; 
    			v[u] =v[w]*sn + fm*cn; 
    			v[w] =v[w]*cn - fm*sn; 
    		} 
    	} 
    
    	return true; 
    }
    
    bool GetMatrixEigen(Matrix MMatrix, Vector &EigenValues, Matrix &EigenVectors, 
    					Vector *Percent, Vector *AccPercent, double deps)
    {
    	int nDem = MMatrix.nrows();
    	double *mat = new double[nDem*nDem];	//输入矩阵,计算完成之后保存特征值
    	double *eiv = new double[nDem*nDem];	//计算完成之后保存特征向量
    
    	for (int i=0; i<nDem; i++)				//给输入矩阵和输出特征向量的矩阵赋值初始化
    	{
    		for (int j=0; j<nDem; j++)
    		{
    			mat[i*nDem+j] = MMatrix(i,j);
    			eiv[i*nDem+j] = 0.0;
    		}
    	}
    
    	bool rel = Eejcb(mat, nDem, eiv, deps, 100);	//计算特征值和特征向量
    	if (!rel)
    		return false;
    
    	Vector TmpEigenValues(nDem);		//临时特征值
    	Matrix TmpEigenVectors(nDem,nDem);	//临时特征向量
    
    	for (int i=0; i<nDem; i++)			//赋值
    	{
    		for (int j=0; j<nDem; j++)
    		{
    			TmpEigenVectors(i,j) = eiv[i*nDem+j];
    
    			if (i == j)
    				TmpEigenValues[i] = mat[i*nDem+j];
    		}
    	}
    	delete []mat;
    	delete []eiv;
    
    	double dSumEigenValue = 0.0;		//特征值总和
    	std::map<double, int> mapEigen;
    	for (size_t index=0; index<TmpEigenValues.size(); index++)
    	{
    		mapEigen[TmpEigenValues[index]] = index;
    		dSumEigenValue += TmpEigenValues[index];
    	}
    
    	//对协方差矩阵的特征值和特征向量排序并计算累计百分比和百分比
    	std::map<double, int>::reverse_iterator iter = mapEigen.rbegin();
    	for(size_t ii=0; ii<TmpEigenValues.size(); ii++)
    	{
    		if(iter == mapEigen.rend())
    			break;
    
    		EigenValues[ii] = iter->first;
    		int index = iter->second;	//获取该特征值对应的特征向量对应的列号
    
    		if(Percent != NULL)	//计算百分比以及累积百分比
    		{
    			double dTemp = iter->first / dSumEigenValue;
    			(*Percent)[ii] = dTemp;
    			if(AccPercent != NULL)
    			{
    				if(ii != 0)
    					(*AccPercent)[ii] = (*AccPercent)[ii-1] + dTemp;
    				else
    					(*AccPercent)[ii] = dTemp;
    			}
    		}
    
    		double dMaxValue = ABS(TmpEigenVectors(0, index));
    		int iNum = 0;
    		for(int iRow=0; iRow<nDem; iRow++)	//获取特征向量中绝对值最大的位置
    		{
    			double dTemp = ABS(TmpEigenVectors(iRow, index));
    			if(dMaxValue < dTemp)
    			{
    				dMaxValue = dTemp;
    				iNum = iRow;
    			}
    		}
    
    		bool bIsPositive = false;	//判断最大的特征向量中的值是否为正
    		if(dMaxValue-TmpEigenVectors(iNum, index)<0.000000001)
    			bIsPositive = true;
    
    		for(int iRow=0; iRow<nDem; iRow++)	//确保每一个特征向量中的绝对值最大的都为正
    		{
    			if(!bIsPositive)
    				EigenVectors(iRow, ii) = -TmpEigenVectors(iRow, index);
    			else
    				EigenVectors(iRow, ii) = TmpEigenVectors(iRow, index);
    		}
    
    		iter++;
    	}
    	return true; 
    }
    求解最核心的方法是雅可比方法。关于雅可比方法网上有很多资料,这里不再进行说明。

    参考资料:

    [1]http://osl.iu.edu/research/mtl/mtl2.php3

    [2]https://zh.wikipedia.org/wiki/%E5%8D%A1%E7%88%BE%C2%B7%E9%9B%85%E5%8F%AF%E6%AF%94

    [3]https://zh.wikipedia.org/wiki/%E9%9B%85%E5%8F%AF%E6%AF%94%E7%9F%A9%E9%98%B5

    [4]http://boya.xmu.edu.cn/hhal/numchapt3/num_32.files/frame.htm

  • 相关阅读:
    vue项目index.html缓存
    vue刷新当前页面
    keep-alive
    JS刷算法题:二叉树
    CSS动效集锦,视觉魔法的碰撞与融合(三)
    算法:栈和队列题目集合(一)
    浅谈设计模式(二):装饰器模式|中介模式|原型模式
    聊聊JS的二进制家族:Blob、ArrayBuffer和Buffer
    浅谈设计模式(一):状态模式|外观模式|代理模式
    纵论WebAssembly,JS在性能逆境下召唤强援
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6313952.html
Copyright © 2011-2022 走看看