Метод Гаусса для вирішення систем лінійних рівнянь

На головну >>>
Метод Гаусса для вирішення систем лінійних рівнянь

Тут описаний алгоритм розв'язання системи лінійних рівнянь за допомогою так званого методу Гаусса. Програму ви можете скачати розділі програми . Алгоритм реалізований на мові С.

Нехай у нас є система N лінійних рівнянь

a11x1 + a12x2 + a13x3 + ... a1NxN = b1
a21x1 + a22x2 + a23x3 + ... a2NxN = b2
a31x1 + a32x2 + a33x3 + ... a3NxN = b3
...
aN1x1 + aN2x2 + aN3x3 + ... aNNxN = bN

де xi - невідомі, aij - коефіцієнти при невідомих, bi - вільні члени в рівняннях, i, j пробігають значення від 1 до N.

Мета завдання - знаючи aij і bi знайти xi.

Суть методу Гаусса полягає в тому, що за допомогою деяких операцій вихідну систему рівнянь можна звести до більш простій системі. Ця проста система має трикутний вигляд:

a11x1 + a12x2 + a13x3 + ... a1NxN = b1 a22x2 + a23x3 + ... a2NxN = b2 a33x3 + ... a3NxN = b3 ... ... aNNxN = bN

Особливість цієї системи - в рядках з номером i все коефіцієнти aij при j <i дорівнюють нулю.

Якщо ми змогли привести нашу систему рівнянь до такого трикутного вигляду, то вирішити рівняння вже просто. З останнього рівняння знаходимо xN = bN / aNN. Далі підставляємо його в передостаннє рівняння і знаходимо з нього xN-1. Підставляємо обидва знайдених рішення в наступне з кінця рівняння і знаходимо xN-2. І так далі, поки не знайдемо x1, на чому рішення закінчується. Така процедура називається зворотної прогоном.

Тепер перейдемо до питання як же домогтися того, щоб система стала трикутної.

З лінійної алгебри (див. Наприклад, Крутицкая Н.І., Тихонравов А.В., Шишкін А.А., Аналітична геометрія і лінійна алгебра з додатками) відомо що якщо до деякої рядку системи рівнянь додати будь-яку лінійну комбінацію будь-яких інших рядків цієї системи, то рішення системи не зміниться. Під лінійною комбінацією рядків розуміється сума рядків, кожна з яких у множаться на деяке число (в принципі, будь-який).

Потрібно, щоб у другому рядку вийшло рівняння, в якій відсутній член при x1. Додамо до цього рядка перший рядок, помножену на деяке число M.

(A11x1 + a12x2 + a13x3 + ... a1NxN = b1) * M +
a21x1 + a22x2 + a23x3 + ... a2NxN = b2

отримаємо

(A11 * М + a21) x1 + ... = b1 * M + b2

Для того, щоб член при x1 дорівнював нулю, потрібно, щоб M = - a21 / a11. Виконавши цю операцію, вийшло рівняння запишемо замість другого і приступимо до третього рівняння. До нього ми додамо перше рівняння, помножене на M = - a31 / a11 і теж отримаємо нуль замість члена при x1. Таку операцію потрібно виконати над усіма іншими рівняннями. В результаті отримаємо систему такого виду:

a11x1 + a12x2 + a13x3 + ... a1NxN = b1 a22x2 + a23x3 + ... a2NxN = b2 a32x2 + a33x3 + ... a3NxN = b3 ... aN2x2 + aN3x3 + ... aNNxN = bN

Після цього будемо позбуватися від членів при x2 в третьому, четвертому, N-му рівнянні. Для цього потрібно до рівняння з j-м номером додати 2-е рівняння, помножене на M = - aj2 / a22. Виконавши цю операцію над усіма іншими рівняннями, отримаємо систему де немає членів з x2 в рівняннях з номером більше 2.

І так далі ... Проробивши це для третього члена, четвертого ... до тих пір, поки не закінчаться рівняння, отримаємо в підсумку систему трикутного виду.

Нижче наведено текст програми.

// HOWTO solve system of linear equations // ax = b // where a [N] [N] is square matrix, // b [N] and x [N] are columns // data are stored in input text file: // input file format: // a_11 a_12 a_13 ... a_1N b_1 // a_21 a_22 a_23 ... a_2N b_2 // ... // a_N1 a_N2 a_N3 ... a_NN b_N #include <stdio.h> #include <process.h> float ** a, * b, * x; int N; char filename [256]; FILE * InFile = NULL; void count_num_lines () {// count number of lines in input file - number of equations int nelf = 0; // non empty line flag do {nelf = 0; while (fgetc (InFile)! = '\ n' &&! feof (InFile)) nelf = 1; if (nelf) N ++; } While (! Feof (InFile)); } Void freematrix () {// free memory for matrixes int i; for (i = 0; i <N; i ++) {delete [] a [i]; } Delete [] a; delete [] b; delete [] x; } Void allocmatrix () {// allocate memory for matrixes int i, j; x = new float [N]; b = new float [N]; a = new float * [N]; if (x == NULL || b == NULL || a == NULL) {printf ( "\ nNot enough memory to allocate for% d equations. \ n", N); exit (-1); } For (i = 0; i <N; i ++) {a [i] = new float [N]; if (a [i] == NULL) {printf ( "\ nNot enough memory to allocate for% d equations. \ n", N); }} For (i = 0; i <N; i ++) {for (j = 0; j <N; j ++) {a [i] [j] = 0; } B [i] = 0; x [i] = 0; }} Void readmatrix () {int i = 0, j = 0; // read matrixes a and b from input file for (i = 0; i <N; i ++) {for (j = 0; j <N; j ++) {fscanf (InFile, "% f", & a [i] [ j]); } Fscanf (InFile, "% f", & b [i]); }} Void printmatrix () {// print matrix "a" int i = 0, j = 0; printf ( "\ n"); for (i = 0; i <N; i ++) {for (j = 0; j <N; j ++) {printf ( "% + f * X% d", a [i] [j], j); } Printf ( "=% f \ n", b [i]); }} Void testsolve () {// test that ax = b int i = 0, j = 0; printf ( "\ n"); for (i = 0; i <N; i ++) {float s = 0; for (j = 0; j <N; j ++) {s + = a [i] [j] * x [j]; } Printf ( "% f \ t% f \ n", s, b [i]); }} Void printresult () {int i = 0; printf ( "\ n"); printf ( "Result \ n"); for (i = 0; i <N; i ++) {printf ( "X% d =% f \ n", i, x [i]); }} Void diagonal () {int i, j, k; float temp = 0; for (i = 0; i <N; i ++) {if (a [i] [i] == 0) {for (j = 0; j <N; j ++) {if (j == i) continue; if (a [j] [i]! = 0 && a [i] [j]! = 0) {for (k = 0; k <N; k ++) {temp = a [j] [k]; a [j] [k] = a [i] [k]; a [i] [k] = temp; } Temp = b [j]; b [j] = b [i]; b [i] = temp; break; }}}}} Void cls () {for (int i = 0; i <25; i ++) printf ( "\ n"); } Void main () {int i = 0, j = 0, k = 0; cls (); do {printf ( "\ nInput filename:"); scanf ( "% s", filename); InFile = fopen (filename, "rt"); } While (InFile == NULL); count_num_lines (); allocmatrix (); rewind (InFile); // read data from file readmatrix (); // check if there are 0 on main diagonal and exchange rows in that case diagonal (); fclose (InFile); printmatrix (); // process rows for (k = 0; k <N; k ++) {for (i = k + 1; i <N; i ++) {if (a [k] [k] == 0) {printf ( "\ nSolution is not exist. \ n "); return; } Float M = a [i] [k] / a [k] [k]; for (j = k; j <N; j ++) {a [i] [j] - = M * a [k] [j]; } B [i] - = M * b [k]; }} Printmatrix (); for (i = N-1; i> = 0; i -) {float s = 0; for (j = i; j <N; j ++) {s = s + a [i] [j] * x [j]; } X [i] = (b [i] - s) / a [i] [i]; } InFile = fopen (filename, "rt"); readmatrix (); fclose (InFile); printmatrix (); testsolve (); printresult (); freematrix (); }

Оскільки весь метод вже описаний, коротко опишу, як ця програма отримує рівняння на вхід, що видає в результаті рішення.

Щоб скористатися цією програмою, потрібно запустити скомпільований виконуваний файл. В першу чергу програма запитає, звідки їй брати коефіцієнти для рівнянь. Створіть в будь-якому текстовому редакторі (але тільки не в Word-е а, наприклад в notepad-е) файл, де напишіть коефіцієнти рівнянь через підрядник через пробіл, приблизно так:

a11 a12 a13 ... a1N b1 a21 a22 a23 ... a2N b2 a31 a32 a33 ... a3N b3 aN1 aN2 aN3 ... aNN bN

наприклад:

2 -3 1 5
-1 -1 2 0
1 2 -1 3

Цей файл необхідно створити в тій директорії, де лежить програма, інакше вона його не знайде. В результаті роботи програми, вона видасть результат, щось на кшталт:

X0 = 3.000000
X1 = 1.000000
X2 = 2.000000

Це і є рішення системи рівнянь, тобто набір невідомих Х.

Коротко опишемо, для чого служить така велика кількість підпрограм в даній програмі:

  • void count_num_lines () - підраховує кількість рівнянь в системі
  • void allocmatrix () - виділяє пам'ять для масивів для зберігання коефіцієнтів рівнянь, вільних членів і рішення
  • void readmatrix () - прочитує з файлу коефіцієнти і вільні члени в масиви
  • void printmatrix () - роздруковує систему рівнянь
  • void diagonal () - робить так, щоб на головній діагоналі не було нулів, щоб не довелося одного разу ділити на нуль в процесі приведення матриці до трикутного вигляду
  • void testsolve () - підставляє рішення в систему і роздруковує поруч те, що виходить в лівій частині рівняння і для порівняння друкує поруч вільні члени, ті, що в правій частині рівняння; отримані два стовпці повинні збігатися, якщо рівняння вирішені правильно
  • void printresult () - роздруковує вийшов стовпець рішень
  • void freematrix () - звільняє пам'ять, яка була виділена раніше
  • void cls () - стирає екран на початку роботи програми
  • void main () - основна функція з якої послідовно викликаються всі перераховані вище функції, і проходить процес приведення системи рівнянь до трикутного вигляду і зворотна прогін.

Програма забезпечена коментарями, що полегшує її розуміння.

Сподіваюся, що вам стало все зрозуміло.


На головну >>>