在自然科學和工程技術中很多問題的解決常常歸結爲解線性代數方程組,例如採用差分法或者有限元方法解常微分方程,偏微分方程編制問題等都會導致求解線性代數方程組。
關於線性方程組的數值解法一般分爲兩類:直接法和迭代法。
本博客主要來介紹直接法
直接法來求解線性方程組的方法主要有以下幾種:
1. 高斯消去法。 高斯消去法說白了就是線性代數中所講過的對於矩陣的初等行變換,但是高斯消去法的侷限性很高。
這裏將《數值分析》第五版, 清華大學出版社這一書中所舉的例子寫出來,
對於這一個係數矩陣, 高斯消去法將無法實現。
2. 列主元高斯消去法。由高斯消去法知道,在消元過程中可能出現akk 0但很小時,用其作爲除數,會導致其他元素數量級的嚴重增長和舍入誤差的擴散,最後會使得計算解不可靠。於是便有了列主元高斯消去法。
初等行變換的目的是將矩陣化爲行階梯型矩陣,
列主元高斯消去法,說的簡單一點便是:假定當前遍歷到的行是i, 矩陣的階數是n, 那麼我們需要從i -> n的所有行,找到當前的i行的第一個不爲0所在的列索引j, 判斷在行數 i -> n ,列數爲j上的所有的元素,找到其最大的值,並將最大值所在的行交換到i行。
然後繼續進行行階梯型的變換。
==》 這裏我們來說明一點C語言中本身沒有矩陣的操作,但是提供了二維數組的存儲數據的形式。而在實際內存中,二維數組也是按照1D數組來保存的,且是i行的所有元素寫完之後,纔會去寫i+1行上的元素。且是連續存放的。這裏說一句題外話,在Python的numpy模塊中,其在創建二維數組或者矩陣的時候,行與行之間在內存中是有間隔的,可以在numpy documentation中查到。所以有時候,我們會去主動地把二維數組寫成1維數組。
==》這裏我們關於列主元高斯消去法實現了兩種方式,分別是把係數矩陣寫爲2D,和係數矩陣寫爲1D。
本次程序所計算的實例是:
=
C語言代碼如下所示:
這裏要說明兩點,1. 關於下面函數的參數Arr,在C語言中規定,二維數組的Arr[][]中,必須至少確定出它的列數是多少。所以之力按照我們所舉的例子是3x3階的矩陣,所以這裏確定出了列數。
2. 對應的係數矩陣爲什麼寫成double (*Arr)[3]這種形式,正常情況下,我門寫數組的指針,應該是 double *b, 或者 double *b[],
但是因爲在c語言中,"[]" 的優先級比"*"要高,所以要加上括號來先取指針。
#include <math.h>
/*這裏是來創建高斯消去法的函數, 分別採用兩種形式,分別是2維矩陣和1維矩陣*/
void Gauss_eliminate(double (*Arr)[3], double *b, int N){
/* 這裏的參數說明:
1. Arr 是方程組的係數矩陣, 是一個二維方陣
2. b 是方程組的右端項, 是一個一維數組
3. N 是矩陣的階數*/
// 列主元高斯消去法,需要我們去首先找到列主元
int max_val_index = 0;
double effecd = 0.0;
for(int i=0; i<N; i++){
max_val_index = i;
for(int row_i=i+1; row_i<N; row_i++){
if (abs(Arr[row_i][i]) > abs(Arr[max_val_index][i])){
max_val_index = row_i;
}
}
// 待循環結束,便是找到了主元所在列上的最大值所對應的索引
// 將max_val_index所指示的行交換到主行上去
double temp = 0.0;
for(int d=0; d<N; d++){
temp = Arr[max_val_index][d];
Arr[max_val_index][d] = Arr[i][d];
Arr[i][d] = temp;
}
// 交換結束後,需要對b中對應位置上的元素進行交換
temp = b[max_val_index];
b[max_val_index] = b[i];
b[i] = temp;
// 循環結束,交換也就結束了,
for(int row_i=i+1; row_i<N; row_i++){
// 列是從i列開始的.
effecd = Arr[row_i][i] / Arr[i][i];
for(int col_i=i; col_i<N; col_i++){
Arr[row_i][col_i] -= Arr[i][col_i] * effecd;
}
// 內部循環結束後,當前的行上的所有的元素便處理完成了,此時要處理b上的對應位置的元素
b[row_i] -= b[i] * effecd;
}
}
}
3. 我們這裏還進行了將係數矩陣寫成1D數組的編程
C語言代碼如下:
void Gauss_elimate1D(double *Arr, double *b, int N){
// 首先進行主元上的尋找和交換
int max_val_index = 0;
double effectd = 0.0;
for(int i=0; i<N; i++){
max_val_index = i;
for(int row_i=i+1; row_i<N; row_i++){
if (abs(Arr[row_i*N+i]) > abs(Arr[max_val_index*N+i])){
max_val_index = row_i;
}
}
// 循環結束後,便得到了最大主元所在的行,下面進行交換
double temp = 0.0;
for(int col_i=i; col_i<N; col_i++){
// 這裏的列循環,其實是可以從i開始的,因爲前面的元素都已經被處理成了0元
temp = Arr[max_val_index*N+col_i];
Arr[max_val_index*N+col_i] = Arr[i*N+col_i];
Arr[i*N+col_i] = temp;
}
// 接下來,我們需要來處理對應索引上的b元素
temp = b[max_val_index];
b[max_val_index] = b[i];
b[i] = temp;
// 交換完畢後,接下來,我們該做的是去進行消去了
for(int row_i=i+1; row_i<N; row_i++){
effectd = Arr[row_i*N+i] / Arr[i*N+i];
for(int col_i=i; col_i<N; col_i++){
Arr[row_i*N+col_i] -= effectd * Arr[i*N+col_i];
}
// 對於係數矩陣處理完畢後,我們接下來該是去處理b矩陣了
b[row_i] -= b[i] * effectd;
}
}
}
運行結果見圖:
程序有什麼問題,請留言,大家互相學習。
贊助一點錢給龍豬。