利用列主元高斯消元法求線性方程組及其算法實現

學過線性代數的朋友都知道有多種方法能用來解線性方程組,今天我給大家介紹的方法是,列主元高斯消元法及其算法實現
如何解線性方程組?
相信大家在初中就學過解方程組,如下面做這個題目

我們求解的時候,就是用消元的方法,即通過兩個式子相減,可以消去一個未知數,進行兩次就可以得到兩個只含相同未知數的方程,這個時候再將這兩個式子相減,又可以消去未知數,接下來的步驟就是求出一個未知數,然後代入先前的方程,可以求另外一個,然後再次回代,就可以求出來三個未知數了,更多未知數的方程也是可以通過類似的方法去求解的,但是一個方程組可以求解的條件是:未知數的數量不大於方程組方程的個數,因此後面的算法若是方程數小於未知數是無法求解的
爲什麼要用列主元消元法
在線性代數裏面,想必課本上面有一個可以直接求解線性方程組的方法:克拉默法則,大家想詳細瞭解的可以到這個網頁看看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;
}

大家還有什麼更好的算法歡迎跟我一起探討~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章