数值分析(二):C++实现三对角线方程组的追赶法

这次来实现三对角线方程组的追赶法,追赶法的本质还是高斯消元法,而且是没选主元的高斯消元法,只是因为Ax=b中系数矩阵A非常特殊,所以就可以采用相对特殊的方法来解方程组。同样,按照常规的步骤,先分析什么是追赶法,再给出追赶法的数学步骤,最后用C++实现这种算法。
(一)追赶法的功能和步骤
明确好目的,正所谓磨刀不误砍柴工,做一件事情事先规划好,那重要性真的是不言而喻。在一些实际问题中,对角占优的三对角线方程组很常见,如热传导方程,形式如下图1:
数值分析(二):C++实现三对角线方程组的追赶法_第1张图片
应用追赶法能解这种三对角线方程组还有很严苛的条件,那就是对角占优,主对角线元素的绝对值要最大,大到什么程度呢?大到绝对值比旁边两条次对角线的值的绝对值之和还要大,这样才能用追赶法来接三对角线方程组,苛刻的条件如下图2:
数值分析(二):C++实现三对角线方程组的追赶法_第2张图片
所以,追赶法就是用来解这类线性方程组的。
追赶法的步骤:追赶法的本质就是没有选主元的高斯消元法,当然系数矩阵***A***都这么特殊了,再用原始的高斯消元法就实在是浪费空间和时间了,特殊的特性就应该用起来,就像做数学证明题时一样,要把事物本身的性质尽可能都用上。实现追赶法最有趣的不是数学方法,而是编程实现,因为之前的系数矩阵***A***是低阶的稠密矩阵,所以编程采用的数据结构就是二维的矩阵,或者说是二重指针,但是实现追赶法,没必要用到二重指针,只要一维数组就够用了,因为就三对角线上有元素,所以一维数组的大小是**n+2(n-1)***,即**3n-1***就够了,然后最重要的就是将系数矩阵***A**(i,j)***元素同一维数组***B[]***的位置***k***的元素相对应,将这层关系理清楚了,那么追赶法就很容易编程实现了,二者的对应关系如下图3。
图3 系数矩阵和一维数组对应关系
所以,数据结构确定,且对应关系理清之后,就又是没选主元的高斯消元法了。数学语言的步骤如下图4。
数值分析(二):C++实现三对角线方程组的追赶法_第3张图片

(二)代码实现
笔者分别将追赶法写成了函数和类两种方式,仔细介绍函数double trde(int n, double A, double b)***,函数就是一个处理器,或是黑匣子,或说是类似映射一样的东西,输入参数,计算处理后得到我们想要的输出,这里参数就是系数矩阵的阶数***n***,以及储存有系数矩阵元素的一维数组***A***(当然这里以指针的形式传输),以及常数向量***b***,输出就是得到解向量,同样是指针形式。代码如下:

double* trde(int n, double* A, double* b)
{
	int k, j, m; m = 3 * n - 2;
	double s;
	for (k = 0; k < n - 1; k++)
	{
		j = 3 * k; s = A[j];
		if (fabs(3) + 1.0 == 1.0)
		{
			cout << "主对角元素是0,方程无解!!" << endl;
			return b;
		}
		A[j + 1] = A[j + 1] / s;//一下这四行是归一化和消元,对角线下的元素没必要进行操作
		b[k] = b[k] / s;//因为后面的回代没用到这些数据
		A[j + 3] = A[j + 3] - A[j + 2] * A[j + 1];
		b[k + 1] = b[k + 1] - A[j + 2] * b[k];
	}
	s = A[3 * n - 3];
	if (fabs(s) + 1.0 == 1.0)
	{
		cout << "消元后,主对角元素是0,无解!!" << endl;
		return b;
	}
	b[n - 1] = b[n - 1] / s;
	for (k = n - 2; k >= 0; k--)//回代求解方程,相比之下很简单了,累积求和都没必要的
		b[k] = b[k] - b[k + 1] * A[3 * k + 1];
	return b;
}

主函数的调用:

int main()
{
	int n = 5;
	double A[13]{ 2,-1,-1,2,-1,-1,2,-1 ,-1,2,-1 ,-1,2 };
	double b[5]{ 1,0,0,0,0 };
	double *d;
	d = trde(n, A, b);
	for (int i = 0; i < n; i++)
	cout << d[i] << "   ";
	//这里直接将解显示在黑框框里了
	system("pause");
	return 0;
}

运行结果,原线性方程组,以及输出的解,如图5,6所示。
数值分析(二):C++实现三对角线方程组的追赶法_第4张图片
数值分析(二):C++实现三对角线方程组的追赶法_第5张图片
最后把类的形式给出来,和全选主元高斯消元法的形式是一样的,input(),输入txt文件名,然后进行操作,a_trde(),输出结果保存在txt文件里,自己命名,接下来是完整的追赶法的类的源码。

//追赶求解三对角方程组
#include  
#include  
#include  
using namespace std;
class  trde
{
private:
	int n;
	double *b, *d;
public:
	trde(int nn)
	{
		n = nn;
		b = new double[3 * n - 2];    //动态分配内存空间,一维数组,存放系数矩阵A的元素
		d = new double[n];//一维数组存放常数向量b的元素
	}
	void input();  //从文件读入存放三对角矩阵的向量B以及常数向量D
	void a_trde();      //执行追赶法
	void output();     //输出结果到文件并显示
	~trde()
	{
		delete[] b;
		delete[] d;
	}
};
void trde::input()      //从文件读入存放三对角矩阵的向量B以及常数向量D
{
	int  i;
	char str1[20];
	cout << "\n输入文件名:  ";
	cin >> str1;
	ifstream  fin(str1);
	if (!fin)
	{
		cout << "\n不能打开这个文件 " << str1 << endl; exit(1);
	}
	for (i = 0; i<3 * n - 2; i++)  fin >> b[i];  //读入三对角矩阵A中的非0元素
	for (i = 0; i> d[i];       //读入常数向量d
	fin.close();
}
void trde::a_trde()        //执行追赶法,核心的函数
{
	int k, j, m;
	double s;
	m = 3 * n - 2;//第一步,对于k从0~n-2,执行归一和消元:
	for (k = 0; k <= n - 2; k++)
	{
		j = 3 * k; s = b[j];
		if (fabs(s) + 1.0 == 1.0)
		{
			cout << "\n程序工作失败!无解. " << endl;
			return;
		}

		b[j + 1] = b[j + 1] / s;//系数矩阵的归一化
		d[k] = d[k] / s;//常数向量的归一化
		b[j + 3] = b[j + 3] - b[j + 2] * b[j + 1];//系数矩阵的消元
		d[k + 1] = d[k + 1] - b[j + 2] * d[k];//消元过程常数向量的变化
	}//第二步:进行回代,倒序进行求解
	s = b[3 * n - 3];
	if (fabs(s) + 1.0 == 1.0)
	{
		cout << "\n程序工作失败!无解. " << endl;
		return;
	}
	d[n - 1] = d[n - 1] / s;
	for (k = n - 2; k >= 0; k--)
		d[k] = d[k] - b[3 * k + 1] * d[k + 1];
}
void trde::output()//结果输出,自己命名txt文件,解向量保存在文件里
{
	int i;
	char str[20];
	cout << "解保存在txt文件里,输入文件名!" << endl;
	cin >> str;
	ofstream fout(str);
	if (!fout)
	{
		cout << "不能打开这个文件!!" << str << endl;
		exit(1);
	}
	for (i = 0; i < n; i++)
	{
		fout << d[i] << " ";
		cout << d[i] << " ";
	}
	fout << endl; cout << endl;
	fout.close();
}
//接下来是主函数的调用
/*
int main()      //主函数
{
	trde  c(5);
	c.input();  //从文件读入存放三对角矩阵的向量B以及常数向量D
	c.a_trde();        //执行追赶法
	c.output();        //输出结果到文件并显示
	system("pause");
	return 0;
}
*/

上周六一看数值分析老师的进度,吓一跳,原来自己已经落后这么多了(捂脸哭),所以又学习编程实现解三对角线方程组的追赶法,再补充一篇,自己本没有时间观念,但是也算体会到了动漫团队持续一周更新一集动漫的艰辛,若是让我每一周都学习并用C++实现一个算法,那我肯定坚持不住了。佩服那些坚持周更的作者们,真是不容易,正好前阵子看了鸣人的原画师作者黄成希的一席演讲,真的很不容易啊,那样的劳动强度,为我们展示了如此精彩的动漫热血视频,人家用十年时间成为了最优秀的原画师,想想我十年能够成为最优秀的某某某,还真是羞愧难当啊,只能去追求美好生活了,哈哈~

你可能感兴趣的:(数值分析)