【创作灵感】
近日学到矩阵与行列式,发现解n元一次方程组居然可以化成如此简介的形式,于是便想到了将其程序化,通过简单的C语言编程实现输入n*(n+1)矩阵来解决n元一次方程组问题,鉴于为小实验,代码中n限定在[1,7]的范围内,有需要的uu也可以自行修改数据。(完全自行思考、完全按照高斯消元原理写的,嫌烦还请别看)
【相关链接】
上篇链接:C语言:通过矩阵高斯消元解n元一次方程组
免费下载链接:更新C语言:通过矩阵高斯消元解n元一次方程组
注:完整代码中的注释也比较完整哦,理解能力强的uu们可以直接看代码~
目录
一、主要内容介绍
根据高斯消元的完整原理进行编写。
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
2.一元一次方程
2x[1]=1
3.二元一次方程组
x[2]=3
x[1]+2x[2]=3
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
三、完整代码献上
#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;
}
最后送给码友们一张图:
结尾:有问题的欢迎提出指正~