寫在前面,本文暫時是針對於有唯一解的非齊次線性方程組。
代碼比較複雜,不是難,在文末,我把 “ 矩陣變換成三角矩陣 ” 的功能封裝成了一個函數,不想看過程的可以直接使用。是非面向對象的。求逆矩陣也是類似方法。
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;
}
}