Компания ALT Linux
Опубликован: 07.03.2015 | Доступ: свободный | Студентов: 2136 / 487 | Длительность: 24:14:00
Лекция 6:

Статические и динамические матрицы

Задача 6.10. Решить систему линейных алгебраических уравнений.

При решении этой задачи напишем универсальную функцию решения системы линейных алгебраических уравнений методом Гаусса, а в функции main() просто вызовем эту функцию. Вспомним метод Гаусса.

Пусть дана система линейных алгебраических уравнений (СЛАУ) с n неизвестными

\left\{\begin{array}{ll}
a_{00}x_0+a_{01}x_1+...+a_{0n-1}x_{n-1}&=b_0,\\
a_{10}x_0+a_{11}x_1+...+a_{1n-1}x_{n-1}&=b_1,\\
\hdotsfor{2}\\
a_{n-10}x_0+a_{n-11}x_1+...+a_{n-1n-1}x_{n-1}&=b_{n-1}
\end{array}\right. ( 6.1)

Обозначим через

A=\left(\begin{matrix}
a_{00}&a_{01}&...&a_{0n-1}\\
a_{10}&a_{11}&...&a_{1n-1}\\
...&...&...&...\\
a_{n-10}&a_{n-11}&...&a_{n-1n-1}
\end{matrix}\right)
матрицу коэффициентов системы (6.1), через b=\left(\begin{matrix}b_0\\b_1\\...\\b_{n-1}\end{matrix}\right)- столбец её свободных членов, и через x=\left(\begin{matrix}x_0\\x_1\\...\\x_{n-1}\end{matrix}\right)- столбец из неизвестных (искомый вектор). Тогда система (6.1) может быть записана в виде матричного уравнения Ax = b.

Наиболее распространённым приёмом решения систем линейных уравнений является алгоритм последовательного исключения неизвестных — метод Гаусса.

При решении систем линейных алгебраических уравнений этим методом всевозможные преобразования производят не над уравнениями системы (6.1), а над так называемой расширенной матрицей системы, которая получается путём добавления к основной матрице A столбца свободных членов b.

Первый этап решения системы уравнений, называемый прямым ходом метода Гаусса, заключается в приведении расширенной матрицы (6.2) к треугольному виду. Это означает, что все элементы матрицы (6.2) ниже главной диагонали должны быть равны нулю.

A'=\left(\begin{matrix}
a_{00}&a_{01}&...&a_{0n-1}&b_0\\
a_{10}&a_{11}&...&a_{1n-1}&b_1\\
...&...&...&...&...\\
a_{n-10}&a_{n-11}&...&a_{n-1n-1}&b_{n-1}
\end{matrix}\right) ( 6.2)

На первом этапе необходимо обнулить элементы 0-го столбца расширенной матрицы

A^{'}=\left(\begin{matrix}
a_{00}&a_{01}&a_{02}&...&a_{0n-1}&b_0\\
0&a_{11}&a_{12}&...&a_{1n-1}&b_1\\
0&0&a_{22}&...&a_{2n-1}&b_2\\
0&0&0&...&a_{3n-1}&b_3\\
...&...&...&...&...&...\\
0&0&0&...&a_{n-1n-1}&b_{n-1}
\end{matrix}\right) ( 6.3)

Для этого необходимо из каждой строки (начиная с первой) вычесть нулевую, умноженную на некоторое число M. В общем виде этот процесс можно записать так:

1-я строка = 1-я строка – M\times 0-я строка

2-я строка = 2-я строка – M\times 0-я строка

...

i-я строка = i-я строка – M\times 0-я строка

...

n - 1-я строка = n - 1-я строка – M\times 0-я строка

Понятно, что преобразование элементов первой строки будет происходить по формулам:

a_{10}=a_{10}-Ma_{00}\\
a_{11}=a_{11}-Ma_{01}\\
 …  \\
a_{1i}=a_{1i}-Ma_{0i}\\
 … \\
a_{1n-1}=a_{1n-1}-Ma_{0n-1}\\
b_1=b_1-Mb_0

Так как целью данных преобразований является обнуление первого элемента строки, то M выбираем из условия: a_{10}=a_{10}-Ma_{00}=0. Следовательно, M=\frac{a_{10}}{a_{00}}.

Элементы второй строки и коэффициент M можно рассчитать аналогично:

a_{20}=a_{20}-Ma_{00}\\
a_{21}=a_{21}-Ma_{01}\\
…\\
a_{2i}=a_{2i}-Ma_{0i}\\
… \\
a_{2n-1}=a_{2n-1}-Ma_{0n-1}\\  
b_2=b_2-Mb_0$\\
a_{20}=a_{20}-Ma_{00}=0 \Rightarrow M=\frac{a_{20}}{a_{00}$.

Таким образом, преобразование элементов i–й строки будет происходить следующим образом:

a_{i0}=a_{i0}-Ma_{00}\\  
a_{i1}=a_{i1}-Ma_{01}\\ 
…\\ 
a_{ii}=a_{ii}-Ma_{0i}\\
 …\\  
a_{in-1}=a_{in-1}-Ma_{0n-1}\\
b_i=b_i-Mb_0.

Коэффициент M для i–й строки выбирается из условия a_{i0}=a_{i0}-Ma_{00}=0 и равен M=\frac{a_{i0}}{a_{00}.

После проведения подобных преобразований для всех строк матрица (6.2) примет вид

A'=\left(\begin{matrix}a_{00}&a_{01}&...&a_{0n-1}&b_0\\0&a_{11}&...&a_{1n-1}&b_1\\0&a_{21}&...&a_{2n-1}&b_2\\...&...&...&...&...\\0&a_{n-11}&...&a_{n-1n-1}&b_{n-1}\end{matrix}\right)

Блок-схема обнуления первого столбца матрицы приведена на рис. 6.10.

Очевидно, что если повторить описанный выше алгоритм для следующих столбцов матрицы (6.2), то в результате будет получена матрица (6.3). Алгоритм этого процесса изображён на рис. 6.11.

Блок-схема обнуления первого столбца матрицы

Рис. 6.10. Блок-схема обнуления первого столбца матрицы
Блок-схема алгоритма преобразования расширенной матрицы к треугольному виду

Рис. 6.11. Блок-схема алгоритма преобразования расширенной матрицы к треугольному виду

Заметим, что если в матрице (6.2) на главной диагонали встретится элемент a_{k,k}, равный нулю, то расчёт коэффициента M=\frac{a_{ik}}{a_{kk}} для k-й строки будет невозможен. Избежать деления на ноль можно, избавившись от нулевых элементов на главной диагонали. Для этого перед обнулением элементов в k–м столбце необходимо найти в нём максимальный по модулю элемент (среди расположенных ниже a_{k,k}), запомнить номер строки, в которой он находится, и поменять её местами с k-й. Алгоритм, отображающий эти преобразования, приведён на рис. 6.12.

В результате выполнения прямого хода метода Гаусса матрица (6.2) преобразуется в матрицу (6.3), а система уравнений (6.1) будет иметь следующий вид:

\left\{\begin{array}{rl}
a_{00}x_0+a_{01}x_1+a_{20}x_2+...+a_{0n-1}x_{n-1}&=b_0,\\
a_{11}x_1+a_{21}x_2+...+a_{1n-1}x_{n-1}&=b_1,\\
a_{22}x_2+...+a_{2n-1}x_{n-1}&=b_2,\\
...\\
a_{n-1n-1}x_{n-1}&=b_{n-1}
\end{array}\right. ( 6.4)

Решение системы (6.4) называют обратным ходом метода Гаусса.

Последнее (n - 1)-е уравнение системы (6.4) имеет вид: a_{n-1n-1}x_{n-1}=b_{n-1}. Тогда, если a_{n-1n-1}\neq 0, то x_{n-1}=\frac{b_{n-1}}{a_{n-1n-1}}. В случае, если aa_{n-1n-1}=0,, и b_{n-1}=0, то система (6.4), а следовательно, и система (6.1) имеют бесконечное множество решений.

При a_{n-1n-1}=0 и b_{n-1}\neq 0 система (6.4), а значит и система (6.1), решения не имеет. Предпоследнее (n-2)-е уравнение системы (6.4) имеет вид a_{n-2n-2}x_{n-2}+a_{n-2n-1}x_{n-1}=b_{n-2}.

Блок-схема алгоритма перестановки строк расширенной матрицы

Рис. 6.12. Блок-схема алгоритма перестановки строк расширенной матрицы
Блок-схема алгоритма обратного хода метода Гаусса

Рис. 6.13. Блок-схема алгоритма обратного хода метода Гаусса

Значит, x_{n-2}=\frac{b_{n-2}-a_{n-2n-1}x_{n-1}}{a_{n-2n-2}}.

Следующее (n - 3)-е уравнение системы (6.4) будет выглядеть так:

a_{n-3n-3}x_{n-3}+a_{n-3n-2}x_{n-2}+a_{n-3n-1}x_{n-1}=b_{n-3}.

Отсюда имеем

x_{n-3}=\frac{b_{n-3}-a_{n-3n-2}x_{n-2}-a_{n-3n-1}x_{n-1}}{a_{n-3n-3}},x_{n-3}=
\frac{b_{n-3}-\sum\limits_{j=n-2}^{n-1}{a_{n-3j}x_j}}{a_{n-3n-3}}.

Таким образом, формула для вычисления i-го значения x будет иметь вид:x_i=\frac{b_i-\sum\limits_{j=i+1}^{n-1}{a_{ij}x_j}}{a_{ii}}.

Алгоритм, реализующий обратный ход метода Гаусса, представлен в виде блок-схемы на рис. 6.13.

Объединив блок-схемы, изображённые на рис. 6.11,рис. 6.12 и рис. 6.13, получим общую блок-схему метода Гаусса (рис. 6.14). Блоки 2-6 содержат последовательный ввод данных, где n — это размерность системы линейных алгебраических уравнений, а сама система задаётся в виде матрицы коэффициентов при неизвестных A и вектора свободных коэффициентов b. Блоки 7-18 предусматривают прямой ход метода Гаусса, а блоки 23-27 — обратный. Для вывода результатов предусмотрено несколько блоков вывода. Если результат проверки условий 19 и 20 положительный, то выдаётся сообщение о том, что система имеет бесконечное множество решений (блок 21). Если условие 19 выполняется, а 20 — нет, то появляется сообщение о том, что система не имеет решений (блок 22). Сами же решения системы уравнений, представленные вектором x, вычисляются (блоки 23–26) и выводятся экран/печать (блок 27) только в случае невыполнения условия.

Теперь алгоритм решения СЛАУ, представленный на рис. 6.14, разобьём на главную функцию main() и функцию решения СЛАУ методом Гаусса. В функции main() будет находиться ввод исходных данных, обращение к функции SLAU и вывод вектора решения. Функция SLAU предназначена для решения системы линейных алгебраических уравнений методом Гаусса.

При написании функции следует учитывать следующее: в методе Гаусса изменяются матрица коэффициентов и вектор правых частей. Поэтому, для того чтобы их не испортить, в функции SLAU матрицу коэффициентов и вектор правых частей необходимо скопировать во внутренние переменные, и в функции обрабатывать внутренние переменные-копии.

Функция SLAU возвращает значение 0, если решение найдено, -1 — если система имеет бесконечное множество решений, -2 — если система не имеет решений.

Ниже приведено решение задачи 6.10 с подробными комментариями.

#include <iostream>
#include <math.h>
using namespace std;
int SLAU( double ** matrica_a, int n, double *massiv_b, double *x )
//Функция SLAU возвращает значение типа int: 0, если решение найдено, _1 — если система имеет
//бесконечное множество решений, _2 — если система не имеет решений.
//Формальные параметры функции: n — размерность системы,
//matrica_a — матрица коэффициентов СЛАУ,
//massiv_b — вектор правых частей, x — решение СЛАУ, передаются как указатели.
{
	int i, j, k, r;
	double c,M, max, s;
	//Матрица a — копия матрицы коэффициентов, массив b — копия вектора правых частей.
	double **a, *b;
	a=new double * [ n ]; //Выделение памяти для a и b.
	for ( i =0; i<n; i++)
		a [ i ]=new double [ n ];
	b=new double [ n ];
	//В a записываем копию матрицы коэффициентов, в b копию вектора правых частей.
	for ( i =0; i<n; i++)
		for ( j =0; j<n; j++)
			a [ i ] [ j ]=matrica_a [ i ] [ j ];
			for ( i =0; i<n; i++)
				b [ i ]=massiv_b [ i ];
	//Прямой ход метода Гаусса: приводим матрицу a (копию матрицы коэффициентов СЛАУ)
	//к диагональному виду.
	for ( k=0;k<n; k++)
	{ //Поиск максимального по модулю элемента в k-м столбце.
		max=fabs ( a [ k ] [ k ] );
		r=k;
		for ( i=k+1; i<n; i++)
			if ( fabs ( a [ i ] [ k ] )>max)
			{
				max=fabs ( a [ i ] [ k ] );
				r= i;
			}
		for ( j =0; j<n; j++) //Меняем местами k-ю и r-ю (строку, где находится
		{ //максимальный по модулю элемент) строки.
			c=a [ k ] [ j ];
			a [ k ] [ j ]=a [ r ] [ j ];
			a [ r ] [ j ]= c;
		}
		c=b [ k ];
		b [ k ]=b [ r ];
		b [ r ]= c;
		for ( i=k+1; i<n; i++) //Приведение матрицы к диагональному виду.
		{
			for (M=a [ i ] [ k ] / a [ k ] [ k ], j=k; j<n; j++)
				a [ i ] [ j ]-=M*a [ k ] [ j ];
				b [ i ]-=M*b [ k ];
		}
	}
	//Обратный ход метода Гаусса.
	if ( a [ n-1 ] [ n-1]==0) //Если последний диагональный элемент равен 0 и
		if ( b [ n-1]==0) //последний коэффициент вектора свободных членов равен 0,
			return -1; //то система имеет бесконечное множество решений
		else return -2; //последний коэффициент вектора свободных членов не равен 0,
		//система решений не имеет.
	else //Последний диагональный элемент не равен 0, начинается обратный ход метода Гаусса.
	{
		for ( i=n-1; i >=0; i --)
		{
			for ( s =0, j= i +1; j<n; j++)
				s+=a [ i ] [ j ] * x [ j ];
			x [ i ]=( b [ i ]- s ) / a [ i ] [ i ];
		}
		return 0;
	}
}
int main ( )
{
	int result, i, j,N;
	double **a, *b, *x;
	cout<<" N = "; //Ввод размерности системы.
	cin>>N;
	a=new double * [N ]; //Выделение памяти для матрицы правых частей и вектора свободных
	членов.
	for ( i =0; i<N; i++)
		a [ i ]=new double [N ];
	b=new double [N ];
	x=new double [N ];
	cout<<"Ввод матрицы A "<<endl; //Ввод матрицы правых частей
	for ( i =0; i<N; i++)
		for ( j =0; j<N; j++)
			cin>>a [ i ] [ j ];
	cout<<"Ввод вектора B "<<endl; //и вектора свободных членов.
	for ( i =0; i<N; i++)
		cin>>b [ i ];
	//Вызов функции решения СЛАУ методом Гаусса. По значению result можно судить, сколько
	//корней имеет система. Если result=0, то система имеет единственное решение, result= -1 -
	//система имеет бесконечное множество решений, result=-2 — система не имеет решений.
	result=SLAU( a,N, b, x );
	if ( result ==0)
	{ //Вывод массива решения.
		cout<<" MassivX "<<endl;
		for ( i =0; i<N; i++)
			cout<<x [ i ]<<" \t ";
		cout<<endl;
	}
	else if ( result ==-1)
		cout<<"Бесконечное множество решений\n ";
			else if ( result ==-2)
				cout<<"Нет решений\n ";
}
Блок-схема алгоритма решения СЛАУ методом Гаусса

Рис. 6.14. Блок-схема алгоритма решения СЛАУ методом Гаусса
Сергей Радыгин
Сергей Радыгин

Символы кириллицы выводит некорректно. Как сделать чтобы выводился читабельный текст на русском языке?

Тип приложения - не Qt,

Qt Creator 4.5.0 основан на Qt 5.10.0. Win7.

 

Юрий Герко
Юрий Герко

Кому удалось собрать пример из раздела 13.2 Компоновка (Layouts)? Если создавать проект по изложенному алгоритму, автоматически не создается  файл mainwindow.cpp. Если создавать этот файл вручную и добавлять в проект, сборка не получается - компилятор сообщает об отсутствии класса MainWindow. Как правильно выполнить пример?