【更新】C语言:通过矩阵高斯消元解n元一次方程组

【创作灵感】

近日学到矩阵与行列式,发现解n元一次方程组居然可以化成如此简介的形式,于是便想到了将其程序化,通过简单的C语言编程实现输入n*(n+1)矩阵来解决n元一次方程组问题,鉴于为小实验,代码中n限定在[1,7]的范围内,有需要的uu也可以自行修改数据。(完全自行思考、完全按照高斯消元原理写的,嫌烦还请别看)

【相关链接】

上篇链接:C语言:通过矩阵高斯消元解n元一次方程组

免费下载链接:更新C语言:通过矩阵高斯消元解n元一次方程组

注:完整代码中的注释也比较完整哦,理解能力强的uu们可以直接看代码~

目录

一、主要内容介绍

1.从上往下消元

2.从下往上消元

3.自定义AvoidZero函数

二、运行效果展示

1.三元一次方程组

2.一元一次方程

3.二元一次方程组

4.四元一次方程组

三、完整代码献上


 

一、主要内容介绍

根据高斯消元的完整原理进行编写。

1.从上往下消元

先确定将“下三角”哪一列消为0,原则上为从第一列到第N-1列,所以这里为第一重循环,设循环参数为j。再每一次j循环都需要消去坐标为(j,j)下方的数据,故每次需要N-j-1次k循环。每次k循环又一整行每个元素都要倍乘相加减,故每个k循环内又有N+1个m循环。

我们在消元过程中习惯将该列为0的项放在后面,而非0的项放在前面,故这里使用自定义AvoidZero函数来实现这种行变换,扫描的思路和快速排序双探头扫描差不多。

于是我们便有:

//先从上往下消
	for (int j = 0; j < N - 1; j++)//j是第二维度
	{//j是当前需要消除的列数
		AvoidZero(N, j);//把当前列中的0移到最下方(整行移动)
		//完成了矩阵的交换

		for (int k = j+1; k < N; k++)//k是第一维度
		{
			if (Number[j][j] != 0)//注意不能除以0
			{
				double Rate = Number[k][j] / Number[j][j];//计算比率
				_Bool AllZero = 1;

				for (int m = 0; m <= N; m++)//每个元素都要消
				{
					Number[k][m] -= (Rate * Number[j][m]);
					if (Number[k][m] != 0 && m != N)
						AllZero = 0;//如果有非零项则说明有解
				}

				if (AllZero)
				{
					printf("\033[31mThe system of equations has no solution!\033[0m\n");
					HasSolution = 0;
					return;
				}
			}
		}
	}

2.从下往上消元

本质上与上一点算法相同,只是坐标做了逆运算,改下坐标、去掉AvoidZero就行了(注意,从下往上消元的时候绝对不可以用AvoidZero,不然很可能破坏下三角。

于是我们便有:

//接下来从下往上消元
	for (int k=N-1;k>0;k--)//固定需要消哪一列
		for (int i = k-1; i >= 0; i--)//第一维度	//注意不能多,否则会出现严重bug
		{
			//此时分母应该是Number[k][k],分子应该是Number[i][k]
			if (Number[i][k] != 0)//注意不能除以0
			{
				double Rate = Number[i][k] / Number[k][k];
				for (int j = 0; j <= N; j++)
					Number[i][j] -= (Rate*Number[k][j]);
			}
		}

3.自定义AvoidZero函数

使用两根指针(注意是物理意义上的指针,比喻作用),一根从上往下扫描,记为指针A,另一根从下往上扫描,记为指针B,指针A遇到0就停下来,记住对应的位置A',指针B遇到非0停下来,记录对应的位置B'。如果A'<B',就交换两行所有的数据,否则两指针所扫描区域已经交叉扫描,结束。

于是我们便有:

void AvoidZero(int N, int List)
{/*用于避免所需要消除的第一行第一位为0*/
	int Loca_Up = List;//向下移动的扫描箭头
	int Loca_Down = N - 1;//向上移动的扫描箭头
	while (Loca_Up < Loca_Down)//注意扫描停止的判断条件,与快速排序的扫描类似
	{
		while (Number[Loca_Up][List] != 0 && Loca_Up < N-1)//向下扫描直到找到0
			Loca_Up++;

		while (Number[Loca_Down][List] == 0 && Loca_Down > 0)//向上扫描直到找到非0
			Loca_Down--;

		if (Loca_Up < Loca_Down)//如果0在上非0在下,则调换位置
			for (int m=0;m<=N;m++)//行交换
				Swap(&Number[Loca_Up][m], &Number[Loca_Down][m]);
		else break;//交换后这一列中为0的行全部排在下方,而这一列上方的行该数据均不为0
	}
	return;
}

二、运行效果展示

1.三元一次方程组

x[1]-2x[2]=-9

x[2]-x[3]=3

x[1]+2x[3]=47

0a990cae058545d58004c83731f57a80.png

2.一元一次方程

2x[1]=1

b6c5763e65dc43dca10fa9eb832b2b70.png

3.二元一次方程组

x[2]=3

x[1]+2x[2]=3

8c8807e0f5dc49ad817f7600154b3af3.png

4.四元一次方程组

x[1]+x[2]+x[3]+x[4]=5

x[1]+2x[2]-x[3]+4x[4]=-2

2x[1]-3x[2]-x[3]-5x[4]=-2

3x[1]+x[2]+2x[3]+11x[4]=0

ae0fa12c10df4592a4ad042607d16881.png

三、完整代码献上

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
///注意:全代码采用矩阵高斯消元完整原理进行求解,嫌烦勿看
float _Number[7][8];//读取数据使用的中介数组
double Number[7][8];//储存矩阵的二维数组
double Answer[7] = { 0,0,0,0,0,0,0 };//记录最终答案的一维数组
_Bool HasSolution = 1;//用于记录方程组是否有解,在未判断出来的情况下默认为有解

void AvoidZero(int N, int List);//声明矩阵行变换之交换的void型函数,避免高斯消元从上往下消元的过程中出现0为分母的情况
void Solve(int N);//声明用于对矩阵求解的void型函数
void Swap(double* a, double* b);//声明用于实现数组内数据交换的void型函数,简洁易读

int main(void)
{//主函数
	int n;//用户需要输入的表示矩阵大小的参数,大小为n*(n+1)
	int i, j;//循环参数

	InputSizeError://如果输入的n不在[1,7]范围内则跳回这里

	printf("\033[35m>\033[32mPlease input the size:\033[0m");//提示用户输入方程组的阶数
	scanf("%d", &n);//读取用户输入的n
	if (n >= 1 && n <= 7)//确保输入的数据在1-7(表示矩阵大小)
	{
		printf("\033[35m>\033[32mSample Input Format:\n\033[33m");
		for (i = 0; i < n; i++)
		{
			for (j = 0; j <= n; j++)
				printf("* ");
			printf("\n");
		}
		printf("\033[35m>\033[32mPlease input the matrix:\033[0m\n");
		for (i = 0; i < n; i++)
			for (j = 0; j <= n; j++)
			{
				scanf("%f", &_Number[i][j]);//完成对数据的读取
				Number[i][j] = (double)_Number[i][j];//将读取到的float型数据转化为double类型储存,提高精度
			}//因为不知道为什么直接用scanf("%f",&Number[i][j]);无法直接读取double类型的数据

		Solve(n);//调用先前声明的void型函数进行解方程

		if (HasSolution)//如果经过判断HasSolution仍然为1,则说明该方程组有解
			for (i = 0; i < n; i++)//循环输出结果
				printf("X[%d]=\t%8.4f\n", i + 1, Answer[i]);
		else  printf("\033[31mThe system of equations has no solution!\033[0m\n");//否则直接输出“The system of equations has no solution!”提示用户
	}
	else
	{//提示用户输入的矩阵的大小不符合要求,并让其重新输入
		printf("\033[35m>\033[31mThe size you input is not allowed!\n\033[35m>\033[31mPlease input from 1 to 7.\n\033[0m");
		goto InputSizeError;//跳转回去要求用户重新输入阶数n
	}
	return 0;
}

void AvoidZero(int N, int List)
{/*用于避免所需要消除的第一行第一位为0*/
	int Loca_Up = List;//向下移动的扫描箭头
	int Loca_Down = N - 1;//向上移动的扫描箭头
	while (Loca_Up < Loca_Down)//注意扫描停止的判断条件,与快速排序的扫描类似
	{
		while (Number[Loca_Up][List] != 0 && Loca_Up < N-1)//向下扫描直到找到0
			Loca_Up++;

		while (Number[Loca_Down][List] == 0 && Loca_Down > 0)//向上扫描直到找到非0
			Loca_Down--;

		if (Loca_Up < Loca_Down)//如果0在上非0在下,则调换位置
			for (int m=0;m<=N;m++)//行交换
				Swap(&Number[Loca_Up][m], &Number[Loca_Down][m]);
		else break;//交换后这一列中为0的行全部排在下方,而这一列上方的行该数据均不为0
	}
	return;
}

void Solve(int N)
{//消元调用的函数
	//先从上往下消
	for (int j = 0; j < N - 1; j++)//j是第二维度
	{//j是当前需要消除的列数
		AvoidZero(N, j);//把当前列中的0移到最下方(整行移动)
		//完成了矩阵的交换

		for (int k = j+1; k < N; k++)//k是第一维度
		{
			if (Number[j][j] != 0)//注意不能除以0
			{
				double Rate = Number[k][j] / Number[j][j];//计算比率
				_Bool AllZero = 1;

				for (int m = 0; m <= N; m++)//每个元素都要消
				{
					Number[k][m] -= (Rate * Number[j][m]);
					if (Number[k][m] != 0 && m != N)
						AllZero = 0;//如果有非零项则说明有解
				}

				if (AllZero)
				{
					printf("\033[31mThe system of equations has no solution!\033[0m\n");
					HasSolution = 0;
					return;
				}
			}
		}
	}
	
	//接下来从下往上消元
	for (int k=N-1;k>0;k--)//固定需要消哪一列
		for (int i = k-1; i >= 0; i--)//第一维度	//注意不能多,否则会出现严重bug
		{
			//此时分母应该是Number[k][k],分子应该是Number[i][k]
			if (Number[i][k] != 0)//注意不能除以0
			{
				double Rate = Number[i][k] / Number[k][k];
				for (int j = 0; j <= N; j++)
					Number[i][j] -= (Rate*Number[k][j]);
			}
		}

	for (int i = 0; i < N; i++)
		if (Number[i][i] != 0)//注意不能除以0
			Answer[i] = Number[i][N] / Number[i][i];
		else HasSolution = 0;//出现除以0得到解的“解”仍然为无解
	return;
}

void Swap(double* a, double* b)
{//实现数据交换的函数,使得其他部分简洁易懂
	double temp = *a;
	*a = *b;
	*b = temp;
}

最后送给码友们一张图:

33928cb3bd794cfca4a034dabc5962fe.jpeg

结尾:有问题的欢迎提出指正~

 

好的,我可以为您介绍一下 C 语言实现高斯消元线性方程组的方法。 首先,我们需要定义一个二维数组来存储系数矩阵和常数向量,例如: ``` float A[N][N+1]; ``` 其中,N 表示方程组的未知数个数。这个数组的第 i 行第 j 列表示系数矩阵中第 i 行第 j 列的素,而最后一列则是常数向量。 接下来,我们需要进行高斯消元的主要操作。具体步骤如下: 1. 针对每一列,找到该列中绝对值最大的素,并将该素所在的行与当前行交换。 2. 对于第 i 行,将其第 i 个素除以该行第 i 列的系数,以使得第 i 个素变为 1。 3. 对于第 j 列(j>i),将第 j 行减去第 i 行的系数乘以第 j 列的素,以使得第 j 列的第 i 个素变为 0。 4. 重复步骤 1-3,直到所有的未知数都求出来。 最后,我们可以通过回代法计算出每个未知数的值,具体方法如下: 1. 从最后一行开始,计算出该行未知数的值。 2. 对于第 i 行(i<n),计算出第 i 个未知数的值,即将第 i 行第 i+1 至第 n 个未知数的值带入方程中,计算出第 i 个未知数的值。 3. 重复步骤 1-2,直到所有的未知数都求出来。 以上就是 C 语言实现高斯消元线性方程组的基本步骤。需要注意的是,在实际应用中,可能会遇到系数矩阵不满足条件、有多或无等问题,需要根据具体情况进行特殊处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青が絶える(青绝悲)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值