矩陣變換---高斯全主元消去法---解線性非齊次方程組

寫在前面,本文暫時是針對於有唯一解的非齊次線性方程組。

代碼比較複雜,不是難,在文末,我把 “ 矩陣變換成三角矩陣 ” 的功能封裝成了一個函數,不想看過程的可以直接使用。是非面向對象的。求逆矩陣也是類似方法。


1. 簡介

下圖是初始時的增廣矩陣,解方程組的關鍵就是將矩陣變換成三角矩陣,於是此方程組的解爲 [ 1, 2, 3, 4 ] ,具體的變換方法就是下面要介紹的高斯全主元消去法。

               


2. 算法思路:

對每一行依次處理

主元:第 i 行的主元是a [ i ] [ i ],主元不能爲零,如果爲零就要找到合適的行與本行交換,產生新的主元。

  • 判斷該行的主元是否爲0,如果是0,需要向下找主元所在列的第一個非零元素,主元所在行與該元素所在行交換,該元素代替成爲新的主元。
  • 把主元化爲1(對該行的所有元素分別除以主元素)
  • 用主元素將主元素所在列的其他元素消成0(假設要用a[0][0]消掉a[1][0],就讓第0行元素乘以負的a[1][0]再加到第1行)
  • 再對下一行用同樣的方法處理,處理完最後一行之後,係數矩陣變成E,此時的b就是解。

     


3. 兩個實例的運行過程:

畫圈的是處理每一行時的主元

 

以下是運行過程:(第一個是增廣矩陣)

0  0  2  1  10
4  1  1  1  13
1  1  1  2  14
1  0  1  0  4
--------------------------
4  1  1  1  13
0  0  2  1  10
1  1  1  2  14
1  0  1  0  4
--------------------------
1  0.25  0.25  0.25  3.25
0  0  2  1  10
1  1  1  2  14
1  0  1  0  4
--------------------------
1  0.25  0.25  0.25  3.25
0  0  2  1  10
0  0.75  0.75  1.75  10.75
0  -0.25  0.75  -0.25  0.75
--------------------------
1  0.25  0.25  0.25  3.25
0  0.75  0.75  1.75  10.75
0  0  2  1  10
0  -0.25  0.75  -0.25  0.75
--------------------------
1  0.25  0.25  0.25  3.25
0  1  1  2.33333  14.3333
0  0  2  1  10
0  -0.25  0.75  -0.25  0.75
--------------------------
1  0  0  -0.333333  -0.333333
0  1  1  2.33333  14.3333
0  0  2  1  10
0  0  1  0.333333  4.33333
--------------------------
1  0  0  -0.333333  -0.333333
0  1  1  2.33333  14.3333
0  0  2  1  10
0  0  1  0.333333  4.33333
--------------------------
1  0  0  -0.333333  -0.333333
0  1  1  2.33333  14.3333
0  0  1  0.5  5
0  0  1  0.333333  4.33333
--------------------------
1  0  0  -0.333333  -0.333333
0  1  0  1.83333  9.33333
0  0  1  0.5  5
0  0  0  -0.166667  -0.666667
--------------------------
1  0  0  -0.333333  -0.333333
0  1  0  1.83333  9.33333
0  0  1  0.5  5
0  0  0  -0.166667  -0.666667
--------------------------
1  0  0  -0.333333  -0.333333
0  1  0  1.83333  9.33333
0  0  1  0.5  5
0  0  0  1  4
--------------------------
1  0  0  0  1
0  1  0  0  2
0  0  1  0  3
0  0  0  1  4
--------------------------
1  0  0  0  1
0  1  0  0  2
0  0  1  0  3
0  0  0  1  4
x0=1
x1=2
x2=3
x3=4

 


4. 代碼的具體實現:

#include<iostream>
using namespace std;
//本程序中所有用到的數組都是動態創建的
//二維矩陣用一維數組存放,所以必須用指針訪問*(p+i)



class Matrix{ //矩陣類
	public:
		Matrix(int dims=2);
		void setMatrix(double * rmatr);//設置矩陣的值,必須要接收參數
		void printM();
		~Matrix();

	protected://用protected不用private保證子類依舊可以訪問,爲了使接下來的子類繼續可以訪問,用公有繼承
		double * MatrixA; //存放一維數組首地址,爲什麼用double
		int index;//矩陣的維數
};

void Matrix::printM(){
	for(int i=0;i<index;i++){//i代表二維數組的行數
		for(int j=0;j<index;j++){
			cout<<*(MatrixA+i*index+j)<<" ";
		}
		cout<<endl;
	}

}
Matrix::Matrix(int dims){//初始化成員數據,聲明時已經給了參數默認值,聲明時就不能再寫了
	MatrixA=new double[index*index];//分配成一維的形式
	index=dims;
}

Matrix::~Matrix(){
	delete[] MatrixA;
}

void Matrix::setMatrix(double * rmatr){
	//MatrixA = rmatr;
	for(int i=0;i<index*index;i++){
		MatrixA[i]=rmatr[i];
	}
}


class Linequ:public Matrix{ //線性方程組類
	private:
		double * solu;//存放解向量x的數組的首地址
		double * sums;//存放b的數組的首地址
	public:
		void setLinequ(double *a,double *b);//設置方程組,參數是幹嘛的,A和b嗎???
		void printL();//顯示
		void Solve();//求解方程組,爲什麼返回值是int
		void showX();//輸出方程的解x
		~Linequ();
		Linequ(int dims=2);//參數爲階數
};

Linequ::~Linequ(){ //清除動態分配的數組
	delete[] sums;
	delete[] solu;
}

//調用子類構造函數時,需要自動調用父類的構造函數
//構造函數的作用就是初始化成員數據
Linequ::Linequ(int dims):Matrix(dims){
	index=dims;
	solu = new double[index];
	sums = new double[index];
}


//線性方程組的係數矩陣是Matrix類所需要的
void Linequ::setLinequ(double *a,double *b){
	setMatrix(a);//初始化A矩陣,因爲已經繼承過來了,不用寫Matrix::
	//sums=b;//只需要指針賦值就可以了
	//不知道爲什麼得循環賦值
	for(int i=0;i<index;i++){
		sums[i]=b[i];
	}
	
}
void Linequ::printL(){//輸出增廣矩陣
	 
	for(int i=0;i<index;i++){
		for(int j=0;j<index;j++){ 
			cout<<*(MatrixA+i*index+j)<<"  ";
			
		}
		cout<<sums[i]<<endl;
	
	}

}
void Linequ::Solve(){//MatrixA是係數矩陣,[MatrixA|sums]是增廣矩陣
	double zhuyuan,a,c;int times;
	double *b = new double[index];

	for(int i=0;i<index;i++){//對每一行處理
		
		if(*(MatrixA+i*index+i)==0){//判斷主元素MatrixA[i][i]是否爲零
			//等於零要與下面的非零行交換
			times=i+1;//times記錄下來下一行行號
			//判斷具體要與哪一行交換
			while(i<index-1    &&   *(MatrixA+(i+1)*index+i)==0        ){//i不是最後一行
				times++;
			}
			for(int j=0;j<index;j++){//交換i行和times行,b爲輔助數組
				b[j]=*(MatrixA+i*index+j);
				*(MatrixA+i*index+j)=*(MatrixA+times*index+j);
				*(MatrixA+times*index+j)=b[j];
			}
			c=sums[i];
			sums[i]=sums[times];sums[times]=c;
		}printL();
	cout<<"--------------------------"<<endl;
		//else{
			//1.  將主元化爲1(對i行所有元素都除以主元的值)
			zhuyuan = *(MatrixA+i*index+i);//先把本輪主元的初始值保存下來,因爲後面主元的值會變爲1,該行其他元素的操作就變成除以1了
			for(int j=0;j<index;j++){
				*(MatrixA+i*index+j)=(*(MatrixA+i*index+j))/zhuyuan;
			}
			sums[i]=sums[i] /zhuyuan; 
			printL();
			cout<<"--------------------------"<<endl;
			//2.  將主元所在列的其他元素都用主元消成0,主元當前是i行i列,也就是接下來被處理的元素列爲i,行不能爲i
			for(int k=0;k<index;k++){//k代表行號,消去係數矩陣中位於[k][i]位置的元素
				if(k!=i){
					a = (*(MatrixA+k*index+i));//用變量a記錄一下[k][i]位置的值
					//將i行元素乘以負的[k][i]再加到k行
					for(j=0;j<index;j++){
						(*(MatrixA+k*index+j))=(*(MatrixA+k*index+j)) + ( (*(MatrixA+i*index+j)) * (-1) * a );
					}
					sums[k]=sums[k] + ( sums[i] * (-1) * a );
				}
			}
	//	}
	printL();
	cout<<"--------------------------"<<endl;
	}
	for(int m=0;m<index;m++){
		solu[m]=sums[m];
	} 
}
void Linequ::showX(){
	for(int i=0;i<index;i++){
		cout<<"x"<<i<<"="<<solu[i]<<endl;
	}

}

void main(){//針對性處理四階矩陣

	//係數矩陣
/*	double a[16]={0.2368,0.2471,0.2568,1.2671,
		0.1968,0.2071,1.2168,0.2271,
		0.1581,1.1675,0.1768,0.1871,
		1.1161,0.1254,0.1397,0.1490};
		double a[16]={2,1,1,1,
		1,2,1,1,
		1,1,2,1,
		1,1,1,2};*/
	double a[16]={0,0,2,1,
		4,1,1,1,
		1,1,1,2,
		1,0,1,0};

	//b向量
	//double b[4]={1.8471,1.7471,1.6471,1.5471};
		//double b[4]={11,12,13,14};
		double b[4]={10,13,14,4};
	Linequ equl(4);//新建一個線性方程組對象,4階,在構造函數中分配了空間
	
	equl.setLinequ(a,b);//設置方程組,即將線性方程租對象具體賦值(用上面的數組)
	
	equl.printL();//將剛剛初始化的這個增廣矩陣輸出
	cout<<"--------------------------"<<endl;
	equl.Solve();
	equl.printL();
	equl.showX();
}

5. 封裝的函數

#include<iostream>
using namespace std;

 void printL(int index,double *A,double*b){
	for(int i=0;i<index;i++){
		for(int j=0;j<index;j++){
			cout<<*(A+i*index+j)<<"  ";
		}
		cout<<b[i]<<endl;
	}
}



void Solve(int index,double*A,double*b){//index是維數
	double zhuyuan,a,c;int times;
	double *t = new double[index];

	for(int i=0;i<index;i++){//對每一行處理
		
		if(*(A+i*index+i)==0){//判斷主元素MatrixA[i][i]是否爲零
			//等於零要與下面的非零行交換
			times=i+1;//times記錄下來下一行行號
			//判斷具體要與哪一行交換
			while(i<index-1    &&   *(A+(i+1)*index+i)==0        ){//i不是最後一行
				times++;
			}
			for(int j=0;j<index;j++){//交換i行和times行,b爲輔助數組
				t[j]=*(A+i*index+j);
				*(A+i*index+j)=*(A+times*index+j);
				*(A+times*index+j)=t[j];
			}
			c=b[i];
			b[i]=b[times];b[times]=c;
		}
		printL(index,A,b);
		cout<<"--------------------------"<<endl;
		//1.  將主元化爲1(對i行所有元素都除以主元的值)
		zhuyuan = *(A+i*index+i);//先把本輪主元的初始值保存下來,因爲後面主元的值會變爲1,該行其他元素的操作就變成除以1了
		for(int j=0;j<index;j++){
			*(A+i*index+j)=(*(A+i*index+j))/zhuyuan;
		}
		b[i]=b[i] /zhuyuan; 
		printL(index,A,b);
		cout<<"--------------------------"<<endl;
		//2.  將主元所在列的其他元素都用主元消成0,主元當前是i行i列,也就是接下來被處理的元素列爲i,行不能爲i
		for(int k=0;k<index;k++){//k代表行號,消去係數矩陣中位於[k][i]位置的元素
			if(k!=i){
				a = (*(A+k*index+i));//用變量a記錄一下[k][i]位置的值
				//將i行元素乘以負的[k][i]再加到k行
				for(j=0;j<index;j++){
					(*(A+k*index+j))=(*(A+k*index+j)) + ( (*(A+i*index+j)) * (-1) * a );
				}
				b[k]=b[k] + ( b[i] * (-1) * a );
			}
		}

	printL(index,A,b);
	cout<<"--------------------------"<<endl;
	}
	 
}


void main(){//實驗數據:A[index*index]={0,0,2,1,4,1,1,1,1,1,1,2,1,0,1,0};b ={10,13,14,4};結果爲{1,2,3,4}

	//動態定義數組
	int index = 4;
	double *A=new double[index*index];
	double *b=new double[index];

	//初始化數組
	cout<<"請輸入係數矩陣A:"<<endl;
	for(int i=0;i<index*index;i++){
		cin>>A[i];
	}
	cout<<"請輸入b:"<<endl;
	for(i=0;i<index;i++){
		cin>>b[i];
	}
	
	printL(index,A,b);//將剛剛初始化的這個增廣矩陣輸出
	cout<<"--------------------------"<<endl;
	
	//調用矩陣變換函數
	Solve(index,A,b); //最後b就是結果

	//輸出結果
	for(i=0;i<index;i++){
		cout<<"x"<<i<<" = "<<b[i]<<endl;
	}
}

 

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