c++ 实现计算特征值和特征向量(matlab eig实现)

原文链接

#pragma once

#include <iostream>
#include <vector>
#include <map>
#include <iomanip>

using namespace std;

namespace Algorithm
{
	using namespace std;

	bool My_Jacobi(const vector<vector<double>>& matrix\
		, vector<vector<double>>& eigenvectors\
		, vector<double>& eigenvalues, double precision = 1e-10, int max = 10000)
	{
		int dim = matrix.size();
		//vector<vector<double>> mat(matrix.size(), vector<double>(matrix[0].size(), 0.0));
		vector<vector<double>> mat = matrix;
		eigenvectors = vector<vector<double>>(matrix.size(), vector<double>(matrix[0].size(), 0.0));
		// 初始化特征矩阵
		for (int i = 0; i < dim; i++)
		{
			eigenvectors[i][i] = 1.0;
		}
		eigenvalues = vector<double>(matrix.size());

		int nCount = 0;  //current iteration
		while (1)
		{
			// 搜索矩阵绝对值最大元素及下标
			double dbMax = mat[0][1];
			int nRow = 0;
			int nCol = 1;
			for (int i = 0; i < dim; i++) //row
			{
				for (int j = 0; j < dim; j++)
				{
					double d = fabs(mat[i][j]);
					if ((i != j) && (d > dbMax))
					{
						dbMax = d;
						nRow = i;
						nCol = j;
					}
				}
			}
			cout << dbMax << "," << nRow << "," << nCol << endl;
			// 阈值条件判断
			if (dbMax < precision)     //precision check
				break;
			if (nCount > max)       //iterations check
				break;
			nCount++;
			double dbApp = mat[nRow][nRow];
			double dbApq = mat[nRow][nCol];
			double dbAqq = mat[nCol][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);
			mat[nRow][nRow] = dbApp*dbCosTheta*dbCosTheta + dbAqq*dbSinTheta*dbSinTheta + 2 * dbApq*dbCosTheta*dbSinTheta;
			mat[nCol][nCol] = dbApp*dbSinTheta*dbSinTheta + dbAqq*dbCosTheta*dbCosTheta - 2 * dbApq*dbCosTheta*dbSinTheta;
			mat[nRow][nCol] = 0.5*(dbAqq - dbApp)*dbSin2Theta + dbApq*dbCos2Theta;
			mat[nCol][nRow] = mat[nRow][nCol];
			for (int i = 0; i < dim; i++)
			{
				if ((i != nCol) && (i != nRow))
				{
					dbMax = mat[i][nRow];
					mat[i][nRow] = mat[i][nCol] * dbSinTheta + dbMax*dbCosTheta;
					mat[i][nCol] = mat[i][nCol] * dbCosTheta - dbMax*dbSinTheta;
				}
			}
			for (int j = 0; j < dim; j++) {
				if ((j != nCol) && (j != nRow)) {
					dbMax = mat[nRow][j];
					mat[nRow][j] = mat[nCol][j] * dbSinTheta + dbMax*dbCosTheta;
					mat[nCol][j] = mat[nCol][j] * dbCosTheta - dbMax*dbSinTheta;
				}
			}
			//compute eigenvector
			for (int i = 0; i < dim; i++)
			{
				dbMax = eigenvectors[i][nRow];
				eigenvectors[i][nRow] = eigenvectors[i][nCol] * dbSinTheta + dbMax*dbCosTheta;
				eigenvectors[i][nCol] = eigenvectors[i][nCol] * dbCosTheta - dbMax*dbSinTheta;
			}
		}
		// 特征值排序
		std::map<double, int> mapEigen;
		for (int i = 0; i < dim; i++)
		{
			eigenvalues[i] = mat[i][i];
			mapEigen.insert(make_pair(eigenvalues[i], i));
		}
		double *pdbTmpVec = new double[dim*dim];
		std::map<double, int>::reverse_iterator iter = mapEigen.rbegin();
		for (int j = 0; iter != mapEigen.rend(), j < dim; ++iter, ++j) {
			for (int i = 0; i < dim; i++) {
				pdbTmpVec[i*dim + j] = eigenvectors[i][iter->second];
			}
			eigenvalues[j] = iter->first;
		}
		for (int i = 0; i < dim; i++)
		{
			double dSumVec = 0;
			for (int j = 0; j < dim; j++)
				dSumVec += pdbTmpVec[j * dim + i];
			if (dSumVec < 0)
			{
				for (int j = 0; j < dim; j++)
					pdbTmpVec[j * dim + i] *= -1;
			}
		}
		for (int i = 0; i < dim; i++)
		{
			for (int j = 0; j < dim; j++)
			{
				eigenvectors[i][j] = pdbTmpVec[i * dim + j];
			}
		}
		delete[]pdbTmpVec;
		return true;
	}
};

int main(int argc, char *argv[])
{
	vector<vector<double>> matrix;
	matrix.push_back({ 161.079031576618, 208.250301732992, -64.1043482618227 });
	matrix.push_back({ 208.250301732992, 273.792923146327, -84.8950989429858 });
	matrix.push_back({ -64.1043482618228, -84.8950989429857, 26.9151793309588 });

	//vector<vector<double>> eigenvectors(3, vector<double>(3, 0));
	//vector<double> eigenvalues(4, 0);
	vector<vector<double>> eigenvectors;
	vector<double> eigenvalues;
	double eps = 1e-10;
	int T = 10000;

	bool ret = Algorithm::My_Jacobi(matrix, eigenvectors, eigenvalues, eps, T);

	qDebug() << "matrix";
	for (int i = 0; i < matrix.size(); ++i)
	{
		TemplateFun::printFun(matrix[i].data(), matrix[i].size());
	}

	qDebug() << "eigenvectors";
	for (int i = 0; i < eigenvectors.size(); ++i)
	{
		TemplateFun::printFun(eigenvectors[i].data(), eigenvectors[i].size());
	}

	qDebug() << "eigenvalues";
	TemplateFun::printFun(eigenvalues.data(), eigenvalues.size());

	return 0;
}
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要编写一个C++程序来计算特征值特征向量,可以参考MATLABeig()函数使用的QR法的思想。下面是一个简单的C++程序,用于计算一个3x3的实对称矩阵的特征值特征向量: ```c++ #include <iostream> #include <cmath> using namespace std; const double eps = 1e-8; // 精度要求 void qr(double *a, double *q, double *r, int n) { // 实现QR分解 for (int k = 0; k < n-1; k++) { double norm = 0; for (int i = k; i < n; i++) { norm += a[i*n+k] * a[i*n+k]; } norm = sqrt(norm); if (fabs(norm) < eps) { // 如果矩阵已经是对角矩阵,则退出迭代 break; } double beta = -a[k*n+k] / (norm * (norm + fabs(a[k*n+k]))); double u[n] = {0}; u[k] = a[k*n+k] - norm; for (int i = k+1; i < n; i++) { u[i] = a[i*n+k]; } // 计算Q矩阵 for (int i = 0; i < n; i++) { q[i*n+k] = -beta * u[i]; } q[k*n+k] += 1; // 计算R矩阵 for (int i = k; i < n; i++) { for (int j = k; j < n; j++) { r[k*n+j] += q[i*n+k] * a[i*n+j]; } } // 更新矩阵A for (int i = k+1; i < n; i++) { for (int j = k; j < n; j++) { a[i*n+j] -= q[i*n+k] * r[k*n+j]; } } } } void eig(double *a, double *eigvals, double *eigvecs, int n) { // 实现特征值特征向量计算 double q[n*n] = {0}; double r[n*n] = {0}; for (int i = 0; i < n; i++) { eigvecs[i*n+i] = 1; } // 迭代求解特征值特征向量 for (int i = 0; i < 100; i++) { qr(a, q, r, n); for (int j = 0; j < n; j++) { eigvals[j] = a[j*n+j]; } for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { eigvecs[j*n+k] = 0; for (int l = 0; l < n; l++) { eigvecs[j*n+k] += q[j*n+l] * eigvecs[l*n+k]; } } } double norm = 0; for (int j = 0; j < n; j++) { double diff = fabs(eigvals[j] - a[j*n+j]); if (diff > norm) { norm = diff; } } if (norm < eps) { break; } } } int main() { double a[9] = {3, -1, 0, -1, 2, -1, 0, -1, 3}; double eigvals[3] = {0}; double eigvecs[9] = {0}; eig(a, eigvals, eigvecs, 3); for (int i = 0; i < 3; i++) { cout << "eigenvalue " << i << " = " << eigvals[i] << endl; cout << "eigenvector " << i << " = [" << eigvecs[i*3] << " " << eigvecs[i*3+1] << " " << eigvecs[i*3+2] << "]" << endl; } return 0; } ``` 在这个程序中,qr()函数实现了QR分解,eig()函数实现特征值特征向量计算。程序的主函数中定义了一个3x3的实对称矩阵,并调用eig()函数计算特征值特征向量。程序输出的结果类似于: ``` eigenvalue 0 = 1 eigenvector 0 = [-0.707107 0.000000 0.707107] eigenvalue 1 = 3 eigenvector 1 = [0.000000 -1.000000 0.000000] eigenvalue 2 = 3 eigenvector 2 = [0.707107 0.000000 0.707107] ``` 这个程序仅仅是一个简单的示例,实际上要编写一个高效稳定的特征值计算程序并不容易,需要考虑到矩阵的大小、结构、精度和稳定性等因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值