jocabi方法解决对称矩阵特征值问题
1.特征向量相关公式
对于给定方阵 存在非零向量和标量
满足 ,则称 x 为 A 的特征向量,为 的特征值
从空间角度表示在该特定线性变换下特征向量方向的输入保持方向不变,只进行的伸缩
对于得到的n组特征值我们可以表示为:
n组特征向量(列向量)表示为
则
得到公式 ,若为可逆矩阵则
经过适当的变换就可以得到特征值构成的对角矩阵
2. Jocabi方法迭代
对于对称矩阵 ,提取部分元素 旋转矩阵
可得到
接下来公式密集,可能有算错的地方
实际上旋转矩阵是
则对于 整体其他行列还有
上述两式
要消去 这类非对角线的元素 就需要巧妙的设置(注意分子为0的情况讨论,如果使用cmath 的atan2(y,x)大致不需要)
然后将更新为即赋值 通过不断迭代就可以消去除对角线外的元素
总体上说如果将轮迭代合并得到式子
可得到特征向量 ,计算中我们可以初始化 为单位矩阵
得到 便于编程
可得到特征向量的证明如下
比如
得到 由此不难推得
合并得到式子
的更新方式
3. 代码设计
主体思路
while (epoch < MaxEpoch) {
//循环次不超过最大次数的时候
MaxElem(row, col, E);//找到最大元素的行和列,E为其绝对值
if (E < Eps)break; //误差在一定范围内跳出
Givens(row, col); //利用上述旋转法旋转
epoch++;
}
完全代码如下
#pragma once
#include<vector>
#include<cmath>
/*
jacobi方法求对称矩阵的特征值和特征向量
Dim: 输入的维度
MaxEpoch: 如果达不到指定误差范围,最多多少次退出
Eps: 设定误差,如果达到误差则退出
Mat: 输入的方阵,经过分解后对角线上是特征值
Vec: 特征向量
*/
class Jacobi {
private:
int Dim;
int MaxEpoch;
double Eps;
std::vector<std::vector<double>> Mat;
std::vector<std::vector<double>> Vec;
private:
void MaxElem(int&, int&, double&);
void Givens(int, int);
public:
Jacobi() = default;
Jacobi(std::vector <std::vector<double>>&, double, int);
~Jacobi() = default;
std::vector<std::vector<double>> EigenVec();
std::vector<std::vector<double>> EigenVal();
};
Jacobi::Jacobi(std::vector<std::vector<double>>& mat, double eps, int MaxN) {
if (mat.size() != mat[0].size())throw("Dim");
Eps = eps;
MaxEpoch = MaxN;
Dim = int(mat.size());
Mat = mat;
for (int i = 0; i < Dim; i++) {
for (int j = 0; j < Dim; j++) {
//确保矩阵是对称矩阵
if (i != j && mat[i][j] != mat[j][i])throw("symmtry");
}
}
Vec = std::vector<std::vector<double>>
(Dim, std::vector<double>(Dim, 0));
for (int i = 0; i < Dim; i++)Vec[i][i] = 1;
int epoch = 0;
int row = 0;
int col = 0;
double E = 0;
while (epoch < MaxEpoch) {
MaxElem(row, col, E);
if (E < Eps)break;
Givens(row, col);
epoch++;
}
}
void Jacobi::MaxElem(int& row, int& col, double& E) {
//非对角线最大的元素
float MaxE = fabs(Mat[0][1]);
row = 0; col = 1;
for (int i = 0; i < Dim; i++) {
for (int j = 0; j < Dim; j++) {
float e = fabs(Mat[i][j]);
if (i != j && e > MaxE) {
MaxE = e;
row = i;
col = j;
}
}
}
E = MaxE;
}
void Jacobi::Givens(int p, int q) {
double y = 2 * Mat[p][q];
double x = Mat[p][p] - Mat[q][q];
double theta = 0.5 * atan2(y, x);
double sint = sin(theta);
double cost = cos(theta);
double sin2t = sin(2.0 * theta);
double cos2t = cos(2.0 * theta);
double pp = Mat[p][p];
double qq = Mat[q][q];
double pq = Mat[p][q];
Mat[p][p] = pp * (cost * cost) + qq * (sint * sint) + pq * sin2t;
Mat[q][q] = pp * (sint * sint) + qq * (cost * cost) - pq * sin2t;
Mat[q][p] = Mat[p][q] = 0.5 * (qq - pp) * sin2t + pq * cos2t;
for (int i = 0; i < Dim; i++) {
if (i != p && i != q) {
double pi = Mat[p][i];
double qi = Mat[q][i];
Mat[p][i] = cost * pi + sint * qi;
Mat[q][i] = - sint * pi + cost * qi;
Mat[i][p] = Mat[p][i];
Mat[i][q] = Mat[q][i];
}
}
for (int k = 0; k < Dim; k++) {
double Ekp = Vec[k][p];
double Ekq = Vec[k][q];
Vec[k][p] = Ekp * cost + Ekq * sint;
Vec[k][q] = -Ekp * sint + Ekq * cost;
}
}
std::vector<std::vector<double>>
Jacobi::EigenVec() {
return Vec;
}
std::vector<std::vector<double>>
Jacobi::EigenVal(){
return Mat;
}