用C语言编写解线性方程组的程序
作者是萌新,有错误的地方可以指出。轻喷。。。
- 1.初衷
- 2.思路
- 3.代码
- 4.总结
初衷
觉得手动算运算量大。。,加上不会使用matlab之类的软件。。所以就自己写了一个玩玩。
思路
用于解线性方程组的算法有许多种,而我只了解 Gauss-Jordan 与Cramer。所以自然要在这上面做文章。虽然Gauss-Jordan法最终没能完成了,但是感觉思路可行(毕竟化成了阶梯型),于是记录下来。
在学习了相关知识后,我得知在求解一个线性方程组之前可用由其系数构成的行列式的值来判定该方程组有无解。所以,在求解进行线性方程组的之前,先直接进行有无解的判断,程序的效率会更高。
当然,除了用系数行列式的值来判断以外,还可以用矩阵的秩来判断。但是,作者认为,用行列式的值判断更为简单粗暴。
现在问题来了,行列式求值又该如何实现呢?好在作者8个月前就已经写出了求行列式值的程序,所以直接将以前的代码整合入新程序就OK了。(别问我为什么8个月过后我的水平还是这个鸟样。。泪目。)
0.代数余子式及递归(算是核心了吧)
关于求行列式值的算法也较多,作者在多次亲测(失败)后,最终选定的是运用代数余子式的相关知识及函数的递归来处理。具体相关的代码如下:`
float function_0(int n,float A[30][30],float *total)
{
int a, b,c,d,C,D,i,p; //n是行列式的阶数,A[30][30]是用来传递所输入数组的形参。
float B[30][30];
for(a=0,b=0;b<=n-1;b++) // 取第一排下标为1~n的数
{
if((2+b)%2!=0)
{
p=(-1);
}
else p=1; //判断奇偶性,从而得知其代数余子式的符号。
for(i=0,D=0,d=0;d<=n-1;d++,D++)
{
if(d==0)
{
D-=1;
continue; //两个IF用来正确的取余子式
}
for(C=0,c=0;c<=n-1;c++,C++)
{
if(c==b)
{
C-=1;
continue; //两个IF用来正确的取余子式
}
if(D==0)
{
B[D][C]=A[d][c]*A[a][b]*p;//将第一行的元素分别乘其余子式的第一行,并存入新数组
i++; //计数
}
else
{
B[D][C]=A[d][c];
i++; //计数
/*这里需要着重解释。因为作者这份代码可读性确实差,而且时隔八个月。。。作者自己读起来都费劲。。
首先,我们知道,当一个常数K乘以一个行列式时,假如将K放进行列式中,则该行列式的某一行(列)*K.其他行(列)的数皆不变。我们知道,在数组中,0为开始,所以在二维数组B[][]中,B[0][0]是第一行第一个元素,故有以上的判断条件。*/
}
}
}
if(i==4)
{
(*total)+=(B[0][0]*B[1][1]-B[0][1]*B[1][0]);
/*这里再作着重解释。这个算法的核心是将每一行的元素(从第一行开始)乘入下一行(其相关的代数余子式)由此一步一步往下乘,直至最后变成一个二阶行列式,便可直接求解,运用指针能在函数中通信的特点可以完美传递这个total(总和)*/
}
else
{
function_0(n-1,B,total); /*这里还得解释,这个递归是关键,传递的是新数组,由此才能做到一步一步往下乘,而函数递归的特点则刚刚好能逐行逐行的处理,逐个逐个元素的处理(除非是最后的二阶行列式)。所以最关键的还是这个递归,当初作者并不想使用递归,奈何能力有限,还是使用了递归。*/
}
}
return 0;
} //一言以蔽之,这种算法就是将一个N阶的行列式化成了无数个二阶行列式相加。
以上,便是关于行列式最主要的算法。我知道写的烂而且可读性差,,,要不然我自己看着也不会头疼了。。。。。。“一日不见如隔三秋”。
1 Gauss-Jordan(失败)
高斯消元法主要是使用各行(列)之间倍加,倍乘等方法来达到消元目的,再配合初等行变换等方法可以将原矩阵消元成阶梯型矩阵。由此可知,高斯消元法的核心便是消元。如:
写成矩阵AX=B的形式则如下:
继续处理可得:
因此,在程序中处理时也可按部就班的处理。
具体代码如下(这是个未完成的代码,在将矩阵化阶梯型后作者弃坑了。。。不过感觉思路还行,所以码上。)
//部分代码省略
void function_1(int a,int b,int *Count_coo ,float For[30][30]) //定义函数。
{
float For_1[30][30];
float For_2[30][30];
int zero=0,No_zero=0,c,d,e;
int i=0,f=0,g=0;
g=*Count_coo;
for(d=0;d<1;d++)
for(c=0;c<a;c++)
{
if(For[c][g]==0)
{
for(e=0;e<b;e++)
For_1[zero][e]=For[c][e]; //将首元素为0的行存入新数组中
zero++; //记录首元素为0的行数
}
else
{
for(e=0;e<b;e++)
For_2[No_zero][e]=For[c][e]; //将首元素为非0的行存入新数组中
No_zero++; //记录首元素为非0的行数
}
}
if(zero==No_zero||zero==1)
function_2(a,b,For[30][30]);
for(i=0;i<No_zero-1;i++)
for(e=0;e<b;e++)
{
For_1[zero+i][e]=For_2[i][e]-For_2[i+1][e]*For_2[i][0]/For_2[i+1][0]; //逐级化简,消元,并存入For_1数组,也是消元的核心算法。
if(For_1[0][e]==0)
f++; //判断第一行是否消元到位。
}
for(e=0;e<b;e++)
For_1[zero+i][e]=For_2[No_zero-1][e];
//将最后一行的元素存入For_1数组。
*Count_coo++;
if(f==b-2)
{
function_2(a,b,For_1); //这里解释一下,Function_2不存在的,哈哈哈哈。
}
else
{
function_1(a,b,Count_coo,For_1); //再次调用消元函数,直至最简形式。
}
return 0;
}
//省略部分代码
2 Cramer(成功)
克拉默法则是直接用简单粗暴的计算来直接求解。(作者认为,在手动计算时,运用克拉默法则的计算量十分大,特别是当数据量大的时候。然而,我之所以将其视为雪中送炭,是因为它只需要用到行列式就OK,行列式上文已给出,所以这里就不给了,只给出另外一个用来处理数据的函数。)
float function_1(int a ,int b,float D, float For[30][30])
{
float For_1[30][30];
float For_2[30];
float Det_OR;
int c,d,e,i;
int n=0;
while(n<=a-1)
Det_OR=0;
for(c=0;c<a;c++)
for(d=0;d<b-1;d++)
{
For_1[c][d]=For[c][d];
}
for(i=0;i<a;i++)
For_1[i][n]=For[i][b-1];
}
if(a==2)
{
Det_OR=For_1[0][0]*For_1[1][1]-For_1[0][1]*For_1[1][0];
}
function_0(a,For_1,&Det_OR); //上文的那个求解行列式的函数
For_2[n]=Det_OR/D; //求解
printf("X%d =%f\n",n+1,For_2[n]);//打印
n++;
}
return 0;
}
总结
总之呢,就是个玩一玩的程序,写这个博客的主要原因是记录下来自己的思路啊,什么的并分享。觉得蛮有意思,可惜毛病很多。
另外,很重要的一点,这个程序应该是不能计算带根号啊之类的数据。反正是1.0版本嘛。
另外,要完整源代码的可以私我。。。。