學過線性代數的朋友都知道有多種方法能用來解線性方程組,今天我給大家介紹的方法是,列主元高斯消元法及其算法實現
如何解線性方程組?
相信大家在初中就學過解方程組,如下面做這個題目
我們求解的時候,就是用消元的方法,即通過兩個式子相減,可以消去一個未知數,進行兩次就可以得到兩個只含相同未知數的方程,這個時候再將這兩個式子相減,又可以消去未知數,接下來的步驟就是求出一個未知數,然後代入先前的方程,可以求另外一個,然後再次回代,就可以求出來三個未知數了,更多未知數的方程也是可以通過類似的方法去求解的,但是一個方程組可以求解的條件是:未知數的數量不大於方程組方程的個數,因此後面的算法若是方程數小於未知數是無法求解的
爲什麼要用列主元消元法
在線性代數裏面,想必課本上面有一個可以直接求解線性方程組的方法:克拉默法則,大家想詳細瞭解的可以到這個網頁看看link,既然克拉默法則也可以直接求解,那爲什麼還要用你這個消元法呢?這就牽扯到一個效率問題了,它雖然是求解線性方程組的直接方法,但卻是不實用的方法!爲什麼這麼說呢,一個n階線性方程組,需要計算n+1個n階行列式,即使在不計加減的情況下,其時間複雜度至少爲n!(n2-1),一個算法的時間複雜度如果達到了n!級別,就不能說是一個好的算法了,何況還是n!(n2-1),而用列主元高斯消元法,n階的線性方程組,第一次需要將第一列的n-1個元素消去,這要進行n-1次除法以及(n-1)n次乘法(因爲是增廣矩陣,所以共有n+1列,除去第一列,每列有n個元素需要做乘法),第二則分別是n-2和(n-2)(n-1),然後將其消元爲一個上三角矩陣,回代的過程,求解第k個未知量,每求解一個未知量就需要一次除法和n-k次乘法,經過求和得到下圖結果(不知道圖片怎麼調整,大夥將就將就)
上面說的是第一個原因,還有第二個原因,就是可以防止在消元的過程中,數據溢出!這裏我們要清楚一個概念,什麼是列主元?列主元便是在這一列裏面,其絕對值最大的那個元素,那我們爲什麼要選列主元呢?就是爲了防止數據溢出,若按照普通的消元方法,若你用來消元的位置上的這個係數很小,而有一個被消元的係數很大,比如一個是0.00000001,一個是99999,那麼這樣消元,後一個數除以前一個數的結果就溢出了
關於高斯消元法我們就說到這裏,接下來開始上代碼!
初始化線性方程組
double** init_Matrix(int r, int c)
{
double** p = new double* [r];
int d = c + 1;
for (int i = 0; i < r; i++)
{
p[i] = new double[d];
memset(p[i], 0, sizeof(double) * d);
}
cout << "請輸入線性方程組對應的增廣矩陣:" << endl;
for (int i = 0; i < r; i++)
{
for (int j = 0; j < d;j++)
{
cin >> p[i][j];
}
}
return p;
}
這裏我們將用戶輸入的線性方程組對應的增廣矩陣進行初始化,函數返回一個二級指針(動態分配的二維數組的地址)
尋找每一列的主元素,返回其行下標
int Find_coMax(int co_index, int r, int c, double **a)
{
if (co_index>=c||co_index<0)
{
fputs("查找位置不合理!\n", stderr);
return -1;
}
int max = co_index;
for (int i = co_index; i < c-1; i++)
{
if (fabs(a[i][co_index])<fabs(a[i+1][co_index]))
{
max = i + 1;
}
}
return max;
}
這裏找列主元,就是要找到絕對值最大的那一個,因此需要用到fabs()函數求絕對值
將列主元所在的那一行與當前行進行交換
void swap_ro(int co_swap, int co_need, int c, double **a)
{
if (co_swap==co_need)//無須交換
{
return;
}
else
{
double tmp = 0;
for (int i = co_swap; i <c+1 ; i++)
{
tmp = a[co_swap][i];
a[co_swap][i] =a[co_need][i] ;
a[co_need][i] = tmp;
}
}
}
注:當前行的意思是,需要用這一行的第一個不爲0的元素與其他行進行消去的行
開始消元
void chang_index_co(int row_index, int r,int col, double** a)
{
double c = 0;
for (int i = row_index; i < r-1; i++)
{
c = 0;
c = -(a[i + 1][row_index] / a[row_index][row_index]);
for (int j = row_index; j < col+1; j++)
{
a[i + 1][j] += a[row_index][j] * c;
}
}
}
因爲要使當前行的第一個不爲0的元素都消去,我們乘上去的c每消一次元都要進行初始化
開始回代計算各個未知數
void calculate(int r, int c, double** a, double* p)
{
int k = r - 1, n = c - 1;
double tmp, q;
for (int i = k; i >= 0; i--)
{
tmp = 0, q = 0;
for (int j = n; j > i; j--)
{
q += a[i][j] * p[j];
}
p[i] = (a[i][c] - q) / a[i][i];
}
}
解第i個未知數時,它後面的n-i個未知數都已經求出來了(因爲轉化成上三角矩陣,是從最後的元素往前面求解的),所以要見這n-1個解乘上其相應的係數進行求和,然後再用方程的值做差,再除以第i個未知數的係數即可
進行一次完整的列主元高斯消元解方程組(將前面的函數綜合運用)
void Gauss_Maxtrix()
{
cout << "請輸入方程組的變量個數和方程個數:" << endl;
int row = 0, col = 0;
cin >> col >> row;
if (col>row)
{
fputs("變量個數多於方程個數,無法求解!\n", stderr);
return;
}
double** a = init_Matrix(row, col);
int lin = 0;
int Trow = row > col ? col : row;
for (int i = 0; i <Trow ; i++)
{
lin = Find_coMax(i, row, col, a);
if (lin==-1)
{
fputs("參數不合理!\n", stderr);
return;
}
swap_ro(i, lin, col, a);
chang_index_co(i, row, col,a);
}
double* p = new double[col];
memset(p, 0, sizeof(double) * col);
calculate(row,col,a,p);
cout << "方程組的近似解爲:" << endl;
for (int i = 0; i < col; i++)
{
cout << p[i] << endl;
}
for (int i = 0; i < row; i++)
{
delete []a[i];
}
delete []a;
delete []p;
}
大家在求解完以後,還是要養成釋放動態內存的好習慣
完整代碼
#include<iostream>
#include<cmath>
using namespace std;
/*
測試數據
2 2
2 3 5
1 -1 0
3 3
3 2 -3 -2
1 1 1 6
1 2 -1 2
*/
double** init_Matrix(int r, int c)
{
double** p = new double* [r];
int d = c + 1;
for (int i = 0; i < r; i++)
{
p[i] = new double[d];
memset(p[i], 0, sizeof(double) * d);
}
cout << "請輸入線性方程組對應的增廣矩陣:" << endl;
for (int i = 0; i < r; i++)
{
for (int j = 0; j < d;j++)
{
cin >> p[i][j];
}
}
return p;
}
//當前列
int Find_coMax(int co_index, int r, int c, double **a)
{
if (co_index>=c||co_index<0)
{
fputs("查找位置不合理!\n", stderr);
return -1;
}
int max = co_index;
for (int i = co_index; i < c-1; i++)
{
if (fabs(a[i][co_index])<fabs(a[i+1][co_index]))
{
max = i + 1;
}
}
return max;
}
//co_need 值最大主元所在的行下標 co_swap最上面的行下標
void swap_ro(int co_swap, int co_need, int c, double **a)
{
if (co_swap==co_need)//無須交換
{
return;
}
else
{
double tmp = 0;
for (int i = co_swap; i <c+1 ; i++)
{
tmp = a[co_swap][i];
a[co_swap][i] =a[co_need][i] ;
a[co_need][i] = tmp;
}
}
}
void chang_index_co(int row_index, int r,int col, double** a)
{
double c = 0;
for (int i = row_index; i < r-1; i++)
{
c = 0;
c = -(a[i + 1][row_index] / a[row_index][row_index]);
for (int j = row_index; j < col+1; j++)
{
a[i + 1][j] += a[row_index][j] * c;
}
}
}
void calculate(int r, int c, double** a, double* p)
{
int k = r - 1, n = c - 1;
double tmp, q;
for (int i = k; i >= 0; i--)
{
tmp = 0, q = 0;
for (int j = n; j > i; j--)
{
q += a[i][j] * p[j];
}
p[i] = (a[i][c] - q) / a[i][i];
}
}
void Gauss_Maxtrix()
{
cout << "請輸入方程組的變量個數和方程個數:" << endl;
int row = 0, col = 0;
cin >> col >> row;
if (col>row)
{
fputs("變量個數多於方程個數,無法求解!\n", stderr);
return;
}
double** a = init_Matrix(row, col);
int lin = 0;
int Trow = row > col ? col : row;
for (int i = 0; i <Trow ; i++)
{
lin = Find_coMax(i, row, col, a);
if (lin==-1)
{
fputs("參數不合理!\n", stderr);
return;
}
swap_ro(i, lin, col, a);
chang_index_co(i, row, col,a);
}
double* p = new double[col];
memset(p, 0, sizeof(double) * col);
calculate(row,col,a,p);
cout << "方程組的近似解爲:" << endl;
for (int i = 0; i < col; i++)
{
printf("%f\t",p[i]);
}
cout<<endl;
for (int i = 0; i < row; i++)
{
delete []a[i];
}
delete []a;
delete []p;
}
int main(void) {
Gauss_Maxtrix();
system("pause");
return 0;
}
大家還有什麼更好的算法歡迎跟我一起探討~