Алгоритм решения СЛАУ методом Гаусса C++
Автор: SysaninФев 16
Попросили как то найти алгоритм решения СЛАУ (системы линейных алгебраических уравнений) методом Гаусса на pascal'e или хотя бы на каком другом языке. Но все алгоритмы были оформлены: а ля решения по лабораторным работам, естественно за звонкую монету. В итоге недавно мне самому понадобилось написать лабу на C++ по этому алгоритму. Вот и решил выложить.
Сам алгоритм математического решения приводить не буду (там всякие прямые прогонки, обратные), это легко гуглится при желании. Тока скажу саму суть алгоритма: составляем квадратную матрицу n*n из коэфицентов уравнений. Далее приводим матрицу к треугольному типу. Я делал это путем деления коэфицентов 1,2...(n-1) -го из первого уравнения на соответствующие коэфиценты 2,3..n-го уравнений. Полученное частное я умножал на первое уравнение и отнимал от текущего (2,3..n-го). Если на диагонали лежал нулевой элемент, то я менял строку местами со следующий у которой был не нулевой элемент на диагонали.
После такой прогонки мы получаем треугольную матрицу. Единственное, что не всякую матрицу можно привести к треугольному виду. В частном случае возможны нулевые элементы на главной диагонали. Тогда решать методом Гаусса нельзя.
Вот собственно икод, сдобренный комментариями. Комментарии /**/ содержат руководство к тому что вам надо дописать.
// меняем местами строки в массиве с коэфицентами уравнения
void exchange(double **mas, int razmer,int from, int to){
for(int i=0; i < razmer; i++){
mas[from][i] = mas[from][i] + mas[to][i];
mas[to][i] = mas[from][i] - mas[to][i];
mas[from][i] = mas[from][i] - mas[to][i];
}
}
// определяем ближайшую строку с не 0 диоганалью вниз
int get_n_no_empty(double **a, int razmer, int begin){
for(int i=begin+1; i < razmer; i++){
if(a[i][i] != 0){
return i;
}
}
cout << "Данная система не решается методом Гауса" << endl;
getch();
exit(1);
}
// функция ждет пока юзер прочтет и нажмет на кнопку, не обязательная функция
void pause(){
cout << "Нажмите любую кнопку для продолжения" << endl;
getch();
}
Непосредственный код для выполнения:
double **a, // матрица с коэфицентами
*x, // х
*b; // свободные члены
/* Здесь переменной razmer необходимо присвоить размер матрицы */
// создание динамических переменных
b = new double[razmer];
x = new double[razmer];
a = new double*[razmer];
for(int i=0; i < razmer; i++){
a[i] = new double[razmer];
}
/* Здесь нам надо присвоить значения матрице а, и заполнить свободные члены b */
// начинаем решать)
// частный случай, если размер матрицы == 1
if(razmer == 1){
if(a[0][0] != 0){
cout << "x=" << b[0]/a[0][0] << endl;
}else{
cout << "Х любое" << endl;
}
getch();
exit(0);
}
// прямой проход
// идем слево на право по коэфицентам х
for(int k=0; k < razmer-1; k++){
// вычитаем по строкам
for(int m=k+1; m < razmer; m++){
// если на диагонали элемент = 0, то поменяем местами строки
if(a[m][m] == 0) exchange(a, razmer, m, get_n_no_empty(a, razmer, m));
double koeficent=a[m][k]/a[0][k];
//вычисление новых коэфицентов уравнения
b[m] = b[m] - b[0] * koeficent;
for(int z=0; z < razmer; z++){
a[m][z] = a[m][k] - a[0][k] * koeficent;
}
}
}
// ищем решения
for(int m=razmer-1; m >= 0; m--){
double sum=0;
// идем по строке спаво налево, считая сумму корень*коэфицент, до текущего корня
for(int i=razmer-1; i > m; i--){
sum += x[i] * a[m][i];
}
x[m] = (b[m] - sum)/a[m][m];
}
// вывод решений
cout << "###Решения:" << endl;
for(int i=0; i < razmer; i++){
cout << "x[" << i << "]=" << x[i] << endl;
}
pause();
Для проверки полученных значений можно использовать этот код
for(int m=0; m < razmer; m++){
double sum=0;
for(int i=0; i < razmer; i++){
sum += a[m][i] * x[i];
}
cout << m << ":" << (sum - b[m]) << endl;
}
pause();
Для корректной работы подключите следующие модули:
#include <conio.h>
#include <tchar.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
6 коммент.
Пишет Loopy | дата: 25 февраля 2009 в 10:25
Лучше для такких штук юзать классы( эт на будущее ), а вообще чтобы не произошло того что диагональный элемент равен 0 и ошибка округления была меньше лучше юзать алгоритм с выбором гланого элемента хотябы по строке, тогда решения не будет существовать только в случае невырожденности матрицы. Ну или строку
if(a[m][m] == 0) exchange(a, razmer, m, get_n_no_empty(a, razmer, m));
заменить на
if(abs(a[m][m]) <E) exchange(a, razmer, m, get_n_no_empty(a, razmer, m));
где E выбирается в зависимости от порядка входных данных.;)
а вообще переходи на std классы.
Пишет Sysanin | дата: 25 февраля 2009 в 12:16
ну классы да:) тока я еще плохо вкурил модель проектирования на них
а на счет алгоритма - ты прав, но как бы мне надо придерживаться методички:) хотя замену надо будет сделать.
Пишет Sysanin | дата: 25 февраля 2009 в 14:46
тока, наверно, вместо abs(a[m][m]), надо fabs, abs же тока для целых чисел…
Пишет Loopy | дата: 26 февраля 2009 в 21:38
посмотри math.h там она перегружена для разных типов;)
Пишет Sysanin | дата: 26 февраля 2009 в 22:05
хм… значит в старой версии билдера она была не перегруженной, мб вобщем
Пишет bazz | дата: 26 апреля 2010 в 15:18
// прямой проход
// идем слево на право по коэфицентам х
for(int k=0; k < razmer-1; k++){
// вычитаем по строкам
for(int m=k+1; m < razmer; m++){
// если на диагонали элемент = 0, то поменяем местами строки
if(a[m][m] == 0) exchange(a, razmer, m, get_n_no_empty(a, razmer, m));
double koeficent=a[m][k]/a[k][k];
//вычисление новых коэфицентов уравнения
b[m] = b[m] - b[k] * koeficent;
for(int z=0; z < razmer; z++){
a[m][z] = a[m][k] - a[k][z] * koeficent;
}
}
}