Наиболее рапространённым методом решения систем линейных уравнений является метод Гаусса с частичным выбором ведущего элемента по столбцам. Первый вариант этого метода здесь реализован в виде класса SLU_Gauss, написанного на основе двух фортран-программ DECOMP и SOLVE из книги "Машинные методы математических вычислений" (Дж.Форсайт, М.Малькольм, К.Моулер). Этот класс позволяет вычислить определитель матрицы, оценить её число обусловленности, решить систему уравнений. Для того, чтобы найти обратную матрицу нужно n раз применить метод solve к соответствующему столбцу единичной матрицы, при этом будет получаться столбец обратной матрицы. typedef nat int nat;
class SLU_Gauss
{
const nat n;
DynArray<nat> ipvt;
DynArray2<double> a;
double cond;
// Запрет конструктора копии и оператора присваивания:
SLU_Gauss ( SLU_Gauss & );
void operator = ( SLU_Gauss & );
public:
SLU_Gauss ( nat k, const double * const * a );
bool solve ( const double * b, double * x ) const; // b[n], x[n]
bool solve ( const double * const * a, const double * b, double * x ) const;
double condition () const { return cond; }
double determinant () const;
};
Конструктор класса приводит исходную матрицу к треугольному виду и приблизительно вычисляет число обусловленности, которое здесь определено, как произведение 1-нормы матрицы A ( вычисляется явно ) на 1-норму матрицы A-1 ( оценивается приближённо ). Если число обусловленности очень большое, т.е. cond + 1 равен cond, то решать такую систему смысла нет и в этом случае методы solve возвращают значение false. Заметим, что в качестве аргументов для методов solve могут быть указатели на один и тот же массив ( b и x ). Метод solve с двумя параметрами просто решает систему уравнений, а метод solve с тремя параметрами ( первый из которых - это указатель на исходную матрицу ) вначале вызывает solve с двумя параметрами, а затем уточняет результат уменьшая квадратичную норму невязки. В моих экспериментах заметный эффект от уточнения наблюдался в редких случаях. Второй вариант реализации метода Гаусса в отличии от первого не использует операторы new-delete: bool slu_gauss ( ArrRef2<double> data ); // несколько правых столбцов bool slu_gauss ( ArrRef2<double> data, double * x ); // один правый столбец Здесь квадратная матрица и столбцы свободных членов записаны в один двухмерный массив data, который в процессе вычислений изменяется, и на месте правых столбцов будут находится столбцы с решениями. Исходники находятся в файле mathem.cpp. Следущий вариант метода Гаусса реализован в виде шаблонов:
//*************************** 19.02.2017 ******************************//
//
// Метод исключений Гаусса.
// Выбор ведущего элемента по строкам.
// nRow, nCol - размеры матрицы.
// index - массив для индексов выбранных столбцов.
// mRow, mCol - размеры подматрицы, где выбираются ведущие элементы.
//
//*************************** 19.02.2017 ******************************//
template <class Matrix>
bool _doGaussRow ( Matrix & data, nat nRow, nat nCol, nat index[], nat mRow, nat mCol )
{
if ( nRow == 0 || nCol <= nRow ) return false;
if ( mRow > nRow ) mRow = nRow;
if ( mCol > nCol ) mCol = nCol;
if ( mRow > mCol ) return false;
// Прямой ход
nat i, j, k;
for ( k = 0; k < mRow; ++k )
{
// Поиск максимального по модулю члена в k-ой строке
nat m = 0;
double * rk = & data[k][0];
double max = fabs ( rk[0] );
for ( i = 1; i < mCol; ++i )
{
const double t = fabs ( rk[i] );
if ( max < t ) max = t, m = i;
}
if ( max == 0 ) return false;
index[k] = m;
// Нормализация строки
const double p = 1. / rk[m];
for ( i = 0; i < nCol; ++i ) rk[i] *= p;
rk[m] = 1;
// Вычитание строк
for ( j = k+1; j < nRow; ++j )
{
double * rj = & data[j][0];
const double t = rj[m];
for ( i = 0; i < nCol; ++i )
{
if ( fabs ( rj[i] -= rk[i] * t ) < 1e-290 ) rj[i] = 0;
}
}
}
// Обратная подстановка
for ( j = mRow; --j > 0; )
{
const double * rj = & data[j][0];
for ( i = 0; i < j; ++i )
{
double * ri = & data[i][0];
const double t = ri[index[j]];
for ( k = 0; k < nCol; ++k ) ri[k] -= rj[k] * t;
}
}
return true;
}
template <class T> class IMatrix;
template <class T> inline
bool doGaussRow ( IMatrix<T> & data, nat nRow, nat nCol, nat * index, nat mRow, nat mCol )
{
return _doGaussRow ( data, nRow, nCol, index, mRow, mCol );
}
template <class T> class ArrRef2;
template <class T> inline
bool doGaussRow ( ArrRef2<T> & data, nat nRow, nat nCol, nat * index, nat mRow, nat mCol )
{
return _doGaussRow ( data, nRow, nCol, index, mRow, mCol );
}
Исходники находятся в файле LinAlg.h. Наверх |