楚列斯基分解法、求矩阵范数的C++实现

2023-11-06

这次的作业如下:

随机生成一个 n n n 阶方阵与 n n n 阶向量构成 A x = b Ax=b Ax=b

  • 构建 n n n 阶对称正定矩阵
    1. 使用楚列斯基分解求 x x x
    2. 使用改进的楚列斯基分解求 x x x
  • 构建 n n n 阶对角占优不可约的三对角矩阵
    1. 使用简化计算的 L U LU LU 分解求 x x x
  • 求以上矩阵的条件数
    1. 包括 1 1 1-范数、 2 2 2-范数和 ∞ \infty -范数的条件数

总结一下难点:

  1. 快速生成 n n n 阶对称正定矩阵
  2. 矩阵求逆
  3. 计算矩阵最大特征值

解决过程如下:

快速生成 n n n 阶对称正定矩阵

如果随机生成对称矩阵,再判断是否正定,那么复杂度是很高的。我测试的时候,6阶矩阵已经跑不出来了。

百度找了一个方案:

1.随机生成一个单位正交阵A
2.随机生成一个对角元素均大于0的对角矩阵B
3. C = A B A T C=ABA^T C=ABAT即为一个正定矩阵

这样复杂度为 O ( n 3 ) O(n^3) O(n3) 就可以接受了。

矩阵求逆

在计算矩阵条件数的时候,需要求给定矩阵的逆矩阵。方法如下:

首先,写出增广矩阵 A|E,即矩阵A右侧放置一个同阶的单位矩阵,得到一个新矩阵。
然后进行初等行变换,当把A转换为单位矩阵的时候,右边就是该矩阵的逆矩阵。

这个实现起来很简单,小改一下 Gauss 消元模板就行了。

计算矩阵最大特征值

在求矩阵的 2 2 2-范数的时候,需要计算给定矩阵的最大特征值。

∣ λ E − A ∣ = ∣ λ − a 11 − a 12 − a 13 − a 21 λ − a 22 − a 23 − a 31 − a 32 λ − a 33 ∣ = 0 |\lambda E-A|= \left| \begin{array}{cccc} \lambda-a_{11}& -a_{12} & -a_{13} \\ -a_{21} & \lambda-a_{22} & -a_{23}\\ -a_{31} & -a_{32} & \lambda-a_{33} \end{array} \right| =0 λEA=λa11a21a31a12λa22a32a13a23λa33=0

计算矩阵特征值需要解一个形如上面等式的方程。

这并不容易(但有办法)。在网上查找解决方案的时候,发现有人写了一个 Eigen 库,里面有很多关于矩阵的函数。于是按着教程下载了这个库,去官网查了查使用手册,实现了这个功能。参考链接

eigen3 就是下载好的第三方库,写好 main.cpp 后,在命令行编译:

g++ main.cpp -o main -I .\eigen3 -w

编译时可能会报无数的 warning ,使用 -w 参数忽略警告。然后就可以运行代码了。

下面的代码是使用楚列斯基分解,及优化后的楚列斯基方法求 x x x。并且算出矩阵 1 1 1-范数、 2 2 2-范数和 ∞ \infty -范数的条件数。

测试代码的时候,不要把 n n n 输入太大,否则会有精度问题,或者上溢 double 。 n n n 最好不超过20。

#include<bits/stdc++.h>
#include<Eigen/Dense>
#include<Eigen/Eigenvalues>
using namespace std;
using namespace Eigen;
const double eps=1e-6;
const int N=210;
int n,flag,bg,ed;
double acp[N][N],a[N][N],l[N][N],y[N],x[N],t[N][N],d[N],tmp[N][N];

void init(){ for(int i=1;i<=n;i++) for(int j=1;j<=n+1;j++) a[i][j]=acp[i][j]; }
void pt(double a[N][N]){ for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) printf("%lf%c",a[i][j]," \n"[j==n]); }
void out(){ printf("x = [ "); for(int i=1;i<=n;i++) printf("%lf ",x[i]); printf("]\n"); }

void mul(double a[N][N],double b[N][N]){		//两矩阵相乘
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			tmp[i][j]=0;
			for(int k=1;k<=n;k++)
				tmp[i][j]+=a[i][k]*b[k][j];
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=tmp[i][j];
}

void randinit(){	//随机生成对称正定矩阵
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(j>i) acp[i][j]=l[j][i]=0;
			else acp[i][j]=l[j][i]=(rand()%100+1)/10.0*(rand()&1?-1:1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i==j) a[i][j]=(rand()%10+1);
	mul(acp,a);
	mul(acp,l);
	for(int i=1;i<=n;i++) acp[i][n+1]=(rand()%200)/10.0-10;
}

void Cholesky(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++){
			l[i][j]=a[i][j];
			for(int k=1;k<=j-1;k++) l[i][j]-=l[i][k]*l[j][k];
			if(i==j) l[i][j]=sqrt(l[i][j]);
			else l[i][j]/=l[j][j];
			l[j][i]=l[i][j];
		}
}

void CholeskyAns(){
	for(int i=1;i<=n;i++){	//计算y[i]
		y[i]=a[i][n+1];
		for(int k=1;k<=i-1;k++) y[i]-=l[i][k]*y[k];
		y[i]/=l[i][i];
	} 
	for(int i=n;i>=1;i--){	//计算x[i]
		x[i]=y[i];
		for(int k=i+1;k<=n;k++) x[i]-=l[k][i]*x[k];
		x[i]/=l[i][i];
	}
}

void Cholesky_New(){
	d[1]=a[1][1];
	for(int i=2;i<=n;i++)
		for(int j=1;j<i;j++){
			t[i][j]=a[i][j];
			for(int k=1;k<=j-1;k++) t[i][j]-=t[i][k]*l[j][k];
			t[j][i]=t[i][j];
			l[i][j]=l[j][i]=t[i][j]/d[j];
			d[i]=a[i][i];
			for(int k=1;k<=i-1;k++) d[i]-=t[i][k]*l[i][k];
		}
}

void CholeskyAns_New(){
	for(int i=1;i<=n;i++){
		y[i]=a[i][n+1];
		for(int k=1;k<=i-1;k++) y[i]-=l[i][k]*y[k];
	}
	for(int i=n;i>=1;i--){
		x[i]=y[i]/d[i];
		for(int k=i+1;k<=n;k++) x[i]-=l[k][i]*x[k];
	}
}

double det(){	//计算det
	double ret=1;
	for(int i=1;i<=n;i++){
		int mx=i;
		for(int j=i+1;j<=n;j++) if(a[j][i]>a[mx][i]) mx=j;
		if(abs(a[mx][i])<eps){ flag=true; return 0; }
		if(mx!=i) ret=-ret;
		ret*=a[mx][i];
		for(int j=i;j<=n+1;j++) swap(a[i][j],a[mx][j]);
		for(int j=n+1;j>=i;j--) a[i][j]/=a[i][i];
		for(int k=1;k<=n;k++) if(i!=k)
			for(int j=n+1;j>=i;j--) a[k][j]-=a[k][i]*a[i][j];
	}
	return ret;
}

void getReverse(double a[N][N]){	//矩阵求逆
	for(int i=1;i<=n;i++) for(int j=n+1;j<=n+n;j++) a[i][j]=(i+n==j);
	for(int i=1;i<=n;i++){
		int mx=i;
		for(int j=i+1;j<=n;j++)
			if(a[j][i]>a[mx][i]) mx=j;
		if(abs(a[mx][i])<eps) return (void)(flag=true);
		for(int j=i;j<=n+n;j++) swap(a[i][j],a[mx][j]);
		for(int j=n+n;j>=i;j--) a[i][j]/=a[i][i];
		for(int k=1;k<=n;k++) if(i!=k)
			for(int j=n+n;j>=i;j--) a[k][j]-=a[k][i]*a[i][j];
	}
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=a[i][j+n];
}

double calc(double a[N][N]){		//计算矩阵最大特征值
	double mx=-1e100;
	MatrixXd m(n,n);	
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			m(i,j)=a[i+1][j+1];
	EigenSolver<MatrixXd> es(m,false);
	auto values=es.eigenvalues();
	for(int i=0;i<n;i++)
		mx=max(mx,values[i].real());
	return mx;
}

double rowNorm(double a[N][N]){		//计算无穷范数(行范数)
	double ret=-1;
	for(int i=1;i<=n;i++){
		double t=0;
		for(int j=1;j<=n;j++) t+=abs(a[i][j]);
		ret=max(ret,t);
	}
	return ret;
}

double colNorm(double a[N][N]){		//计算1范数(列范数)
	double ret=-1;
	for(int j=1;j<=n;j++){
		double t=0;
		for(int i=1;i<=n;i++) t+=abs(a[i][j]);
		ret=max(ret,t);
	}
	return ret;
}

double Norm_2(double a[N][N]){		//计算2范数
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) t[j][i]=a[i][j];
	mul(a,t);
	return sqrt(calc(a));
}


int main(){
	srand(time(0));
	puts("input n (2<=n<=20)");
	scanf("%d",&n);
	do{
		flag=false;
		randinit();		//随机生成n阶对称正定矩阵
		det();
	}while(flag);	//确保矩阵非奇异
	init();
	Cholesky(); 		//使用楚列斯基法
	CholeskyAns();
	puts("Cholesky ans is:");	
	out();

	init();
	Cholesky_New();		//改进的楚列斯基法
	CholeskyAns_New();
	puts("Cholesky_New ans is:");
	out();

	//计算条件数
	init();
	double norm_1_num=colNorm(a);	
	double norm_inf_num=rowNorm(a);
	double norm_2_num=Norm_2(a);	
	init();			//计算2范式后会修改a,需要再次初始化
	getReverse(a);
	norm_1_num*=colNorm(a);
	norm_inf_num*=rowNorm(a);
	norm_2_num*=Norm_2(a);
	printf("cond(A)-1 is %lf\n",norm_1_num);	//输出条件数
	printf("cond(A)-2 is %lf\n",norm_2_num);
	printf("cond(A)-inf is %lf\n",norm_inf_num);
}

用简化计算的 L U LU LU 分解求 n n n 阶对角占优不可约的三对角矩阵的时候,也遇到了个小问题:
构建 n n n 阶对角占优矩阵是很容易的,但如何证明它不可约呢。
查到了这样的一个定理:

n 阶复数方阵 A 是不可约的当且仅当与矩阵 A 对应的有向图 S(A) 是强连通的。

由这个定理易得一个结论:

n阶对角占优矩阵不可约,当且仅当它主对角线、低对角线、高对角线上所有元素非零。

这样就可以生成矩阵了。

由于矩阵是三对角矩阵,开 n 2 n^2 n2 的空间是很浪费的,只开 3 n 3n 3n 就够用了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n;
double a[N],b[N],c[N],d[N],e[N],y[N],x[N],f[N];

void randInit(){
	for(int i=1;i<=n;i++){
		f[i]=rand()%200/10.0-10;
		a[i]=rand()%5+1;
		c[i]=rand()%5+1;
		b[i]=a[i]+b[i]+rand()%5+1;
	}
}

void simpleLU(){
	d[1]=c[1]/b[1];
	for(int i=2;i<=n;i++) d[i]=c[i]/(b[i]-a[i]*d[i-1]);		//计算d
	y[1]=f[1]/b[1];	
	for(int i=2;i<=n;i++) y[i]=(f[i]-a[i]*y[i-1])/(b[i]-a[i]*d[i-1]);
	x[n]=y[n];
	for(int i=n-1;i>=1;i--) x[i]=y[i]-d[i]*x[i+1];
}

void out(){
	printf("ans is:\nx = [ ");
	for(int i=1;i<=n;i++) printf("%lf ",x[i]);
	printf("]\n");
}

int main(){
	srand(time(0));
	puts("please input n(1<=n<=1000000):");
	scanf("%d",&n);
	randInit();
	simpleLU();	
	out();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

楚列斯基分解法、求矩阵范数的C++实现 的相关文章

  • UTF8/UTF16 和 Base64 在编码方面有什么区别

    In c 我们可以使用下面的类来进行编码 System Text Encoding UTF8 System Text Encoding UTF16 System Text Encoding ASCII 为什么没有System Text En
  • 属性对象什么时候创建?

    由于属性实际上只是附加到程序集的元数据 这是否意味着属性对象仅根据请求创建 例如当您调用 GetCustomAttributes 时 或者它们是在创建对象时创建的 或者 前两个的组合 在由于 CLR 的属性扫描而创建对象时创建 从 CLR
  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • C++ 求二维数组每一行的最大值

    我已经设法用这个找到我的二维数组的每一行的最小值 void findLowest int A Cm int n int m int min A 0 0 for int i 0 i lt n i for int j 0 j lt m j if
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • HttpClient 像浏览器一样请求

    当我通过 HttpClient 类调用网站 www livescore com 时 我总是收到错误 500 可能服务器阻止了来自 HttpClient 的请求 1 还有其他方法可以从网页获取html吗 2 如何设置标题来获取html内容 当
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 是否有比 lex/flex 更好(更现代)的工具来生成 C++ 分词器?

    我最近将源文件解析添加到现有工具中 该工具从复杂的命令行参数生成输出文件 命令行参数变得如此复杂 以至于我们开始允许它们作为一个文件提供 该文件被解析为一个非常大的命令行 但语法仍然很尴尬 因此我添加了使用更合理的语法解析源文件的功能 我使
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr

随机推荐