我听取了我们老师的想法,把原书中的命名改为以意义为中心的,不简化的命名方式。原本函数名为gaussj,现在改为gaussJordan
Gauss-Jordan消去法解的是这个线性方程组集合:(我完全不会LaTex呢,而且鉴于复制贴贴的方便,或者转载分享之后会出现不能显示的问题,我会尽量少用Latex公式的)
[tex]A*(X_0 \cup X_1 \cup X_2 \cup Y) = (b_0 \cup b_1 \cup b_2 \cup 1)[/tex]
如果上面一行的公式显示不出,请看这行:A * (X0 || X1 || X2 || Y) = (b0 || b1 || b2 || 1)
其中A是一个n*n的非奇异矩阵,b是n*m的矩阵。
整个式子相当于拆分成4个子式子:
A * X0 = b0
A * X1 = b1
A * X2 = b2
A * Y = 1
算法的运算思想是:
施行初等变换将A变为单位矩阵,则
Xi = A的逆矩阵 * b
Y = A的逆矩阵
算法的步骤:
(1)对于每一列k:
(2)选主元pivot element(绝对值最大的那个)(为了减小舍入误差),记为a[i][j]
(3)如果选出的主元不在对角线上,我们把它换到对角线上
(4)如果主元在对角线上,做消元。
(5)最后再把交换过的元素交换回来以得到正确的A的逆矩阵。
/**
*@file gaussJordan.h
*/
#ifndef GAUSSJORDAN_H_INCLUDED
#define GAUSSJORDAN_H_INCLUDED
#include "nr3.h"
/**
*@brief Solve linear equations A*X = b using Gauss-Jordan elimination.
*@param a[0..n-1][0..n-1] Input as Matrix A in A * x = b, Output its matrix inverse
*@param b[0..n-1][0..m-1] Input as Matrix b in A * x = b, Output the corresponding set of solution vectors
*Linear equation solution by Gauss-Jordan elimination, equation example as follow,
* A * (X0 || X1 || X2 || Y) = (b0 || b1 || b2 || 1):
*
* ( a00 a01 a02 a03 ) ( (x00) (x01) (x02) (y00 y01 y02 y03) )
* ( a10 a11 a12 a13 ) ( (x10) (x11) (x12) (y10 y11 y12 y13) )
* ( a20 a21 a22 a23 ) * ( (x20) || (x21) || (x22) || (y20 y21 y22 y23) )
* ( a30 a31 a32 a33 ) ( (x30) (x31) (x32) (y30 y31 y32 y33) )
*
* ( (b00) (b01) (b02) (1 0 0 1) )
* ( (b10) (b11) (b12) (0 1 0 0) )
* = ( (b20) || (b21) || (b22) || (0 0 1 0) )
* ( (b30) (b31) (b32) (0 0 0 1) )
*
*@attention A and Y should be square matrixes; b and X should be the column-vector matrix.
*This function simultaneously solves the linear sets:
* A * x0 = b0
* A * x1 = b1
* A * x2 = b2
* A * Y = 1
*
*Input matrix:
* a[0..n-1][0..n-1], b[0..n-1][0..m-1] is input containing
* the m right-hand side vectors
*Output matrix:
* a is replaced by its matrix inverse
* b is replaced by the corresponding set of solution vectors
*/
void gaussJordan(MatDoub_IO &a, MatDoub_IO &b){
Int i, icol, irow, j, k ,l , ll, n = a.nrows(), m = b.ncols();
Doub big, dum, pivinv;
VecInt indxc(n), indxr(n), ipiv(n, 0); //These integer arrays are used for bookkeeping on the pivoting.
for(i = 0; i < n; ++i){
//Main loop over the columns to be reduced
big = 0.0;
for(j = 0; j < n; ++j){
//Outer loop of the search for a pivot element.
if(ipiv[j] != 1){
for(k = 0; k < n; ++k){
if(ipiv[k] == 0){
if(abs(a[j][k]) >= big){
big = abs(a[j][k]);
irow = j;
icol = k;
}//if
}//if
}//for
}//if
} //for
++(ipiv[icol]);
/*
*We now have the pivot element, so we interchange rows, if needed, to put the pivot element on the diagonal.
*The columns are not physically interchanged, only reabeled: indxc[i], the column of the (i+1)-th pivot element,
*is the (i+1)-th column that is reduced, while indxr[i] is the row in which that pivot element was originally located.
*If indxr[i] != indxc[i], there is an implied column interchange. With this form of bookkeeping, the solution b's will
*end up in the correct order, and the inverse matrix will be scrambled by columns.
*/
if(irow != icol){
for(l = 0; l < n; ++l)
SWAP(a[irow][l], a[icol][l]);
for(l = 0; l < m; ++l)
SWAP(b[irow][l], b[icol][l]);
} //if
indxr[i] = irow; //We are now readey to divide the pivot row by the pivot element, located at irow and icol.
indxc[i] = icol;
if(a[icol][icol] == 0.0)
NRthrow("gaussj: Singular Matrix");
pivinv = 1.0/a[icol][icol];
a[icol][icol] = 1.0;
for(l = 0; l < n; ++l)
a[icol][l] *= pivinv;
for(l = 0; l < m; ++l)
b[icol][l] *= pivinv;
for(ll = 0; ll < n; ++ll){
//Next, we reduce the rows
if(ll != icol){
//except for the pivot one, of course.
dum = a[ll][icol];
a[ll][icol] = 0.0;
for(l = 0; l < n; ++l)
a[ll][l] -= a[icol][l] * dum;
for(l = 0; l < m; ++l)
b[ll][l] -= b[icol][l] * dum;
} //if
} //for
} //for
/*
*This is the end of the main loop over columns of the reduction. It only remains to unscramble
*the solution in view of the column interchanges. We do this by interchanging pairs of columns
*in the reverse order that the permutation was built up.
*/
for(l = n-1; l >= 0; --l){
if(indxr[l] != indxc[l])
for(k = 0; k < n; ++k)
SWAP(a[k][indxr[l]], a[k][indxc[l]]);
} //for... And we are done.
} //gaussj
/**
*@brief Overloaded version with no right-hand sides. Replaces a by its inverse.
*@param a[0..n-1][0..n-1] Input as Matrix A in A * x = 0, Output its matrix inverse
*/
void gaussJordan(MatDoub_IO &a){
MatDoub_IO b(a.nrows(), 1, 0.0); //Dummy vector with zero columns.
gaussJordan(a,b);
}
#endif // GAUSSJ_H_INCLUDED
大家如果想要做测试的话,可以试试以下代码:
/**
*@file gaussJordan_test.cpp
*/
#include <iostream>
#include "nr3.h"
#include "gaussJordan.h"
using namespace std;
int main(){
MatDoub_IO a,b;
int rows, cols;
//INPUT DATA:
cout << "Please input rows, cols and the matrix data for A: " << endl;
cin >> rows >> cols;
a.resize(rows, cols);
readMatrix_console(a);
cout << "Please input rows, cols and the matrix data for B: " << endl;
cin >> rows >> cols;
b.resize(rows, cols);
readMatrix_console(b);
//Do the Linear Solve
gaussJordan(a, b);
//PRINT
cout << "Gives the Inverse of A: " << endl;
printMatrix_console(a);
cout << "Gives the solution matrix: " << endl;
printMatrix_console(b);
return 0;
}