Метод Гаусса — Зейделя решения системы линейных уравнений

Материал из Википедии — свободной энциклопедии
Это старая версия этой страницы, сохранённая BOOMER 74 (обсуждение | вклад) в 10:53, 3 марта 2013 (отмена правки 52913825 участника 188.187.84.14 (обс)). Она может серьёзно отличаться от текущей версии.
Перейти к навигации Перейти к поиску

Метод Гаусса—Зейделя[1] является классическим итерационным методом решения системы линейных уравнений.

Постановка задачи

Возьмём систему: , где

Или

И покажем, как её можно решить с использованием метода Гаусса-Зейделя.

Метод

Чтобы пояснить суть метода, перепишем задачу в виде:

Здесь в -м уравнении мы перенесли в правую часть все члены, содержащие , для . Эта запись может быть представлена:

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

Итерационный процесс в методе Гаусса-Зейделя строится по формуле после выбора соответствующего начального приближения .

Метод Гаусса-Зейделя можно рассматривать как модификацию метода Якоби. Основная идея модификации состоит в том, что новые значения используются здесь сразу же по мере получения, в то время как в методе Якоби они не используются до следующей итерации:

где

Таким образом, i-тая компонента -го приближения вычисляется по формуле:

Условие сходимости

Приведём достаточное условие сходимости метода.

Теорема.
Пусть , где – матрица, обратная к . Тогда при любом выборе начального приближения :
  1. метод Гаусса-Зейделя сходится;
  2. скорость сходимости метода равна скорости сходимости геометрической прогрессии со знаменателем ;
  3. верна оценка погрешности: .

Условие окончания

Условие окончания итерационного процесса Зейделя при достижении точности в упрощённой форме имеет вид:

Более точное условие окончания итерационного процесса имеет вид

и требует больше вычислений. Хорошо подходит для разреженных матриц.

Пример алгоритма на с++

 // Условие сходимости
bool converge(double *xk, double *xkp)
{
    for (int i = 0; i < n; i++) 
    {
        if (fabs(xk[i] - xkp[i]) >= eps) 
            return false;
    }
    return true;
}

/*
    Ход метода, где:
    a[n][n] - Матрица коэффициентов
    x[n], p[n] - Текущее и предыдущее решения
    b[n] - Столбец правых частей
    Все перечисленные массивы вещественные и
    должны быть определены в основной программе,
    также в массив x[n] следует поместить начальное
    приближение столбца решений (например, все нули)
*/

do
{
    for (int i = 0; i < n; i++)
    {
        double var = 0;
        for (int j = 0; j < n; j++)
            if (j != i) var += (a[i][j] * x[j]);
        p[i] = x[i];
        x[i] = (b[i] - var) / a[i][i];
    }
}
while (!converge(x, p));

Пример алгоритма на pascal (для кв. системы)

type ar2d = array [1..50, 1..50] of double;
     ar1d = array [1..50] of double;

procedure seidel(n: byte; e: extended; a: ar2d; b: ar1d; x: ar1d);
var i, j: longint;
    s, v, m: double;
begin
  // Проверка на совместность
  for i := 1 to n do
    begin
      s := 0;
      for j := 1 to n do
        if j <> i then s := s + abs(a[i, j]);
      if s >= abs(a[i, i]) then
        begin
          writeln('SLAE is inconsistent');
          exit
        end;
    end;
  
  // Сам алгоритм
  repeat
    m := 0;
    for i := 1 to n do
      begin
        s := 0;
        for j := 1 to n do
          if i <> j then s := s + a[i, j] * x[j];
        v := x[i];
        x[i] := (b[i] - s) / a[i, i];
        m:=abs(x[i])-abs(v);
      end;
  until m < e;
  
  // Вывод корней
  writeln('roots: ');
  for i := 1 to n do
    writeln('x[', i, ']= ', x[i]:0:4);
end;

Разбор алгоритма

Суть в том, что стандартная система вида

(где a - вектор коэффициентов, b - вектор свободных членов, а c - вектор неизвестных) преобразуется так, чтобы их могла решать машина.

Перед первой итерацией, вектор (массив) C должен быть инициализирован какими-либо элементами. Тут мнения разделились: одни считают, что предпочтительнее использовать вектор свободных членов (b), другие - нули. От этого выбора зависит только количество итераций (циклов), за которое будет получен ответ с заданной точностью корней (т.е. после каждой итерации Ваши корни будут всё ближе и ближе подбираться к истинным значениям), поэтому для того, чтобы решить, когда пора останавливаться, нужно сравнить текущий вектор С с предыдущим, критерий обычно называют "эпсилон" (и записывают как eps).

#include <math.h>
int main() {
int itr, k, m=3;
float a[m][m], b[m], c[m], c_old[m], temp, eps=0.000001;
// Тут надо вставить код формирования массивов A, B и записать что-нибудь в C
do   // Поехали решать систему ;)
{
    itr++; // Это просто счётчик итераций
    for (k=0; k<m; k++)
        c_old[k]=c[k];
    c[0]=(b[0]-a[0][1]*c[1]-a[0][2]*c[2])/a[0][0];
    c[1]=(b[1]-a[1][0]*c[0]-a[1][2]*c[2])/a[1][1];
    c[2]=(b[2]-a[2][0]*c[0]-a[2][1]*c[1])/a[2][2];
    temp=0;
    for (k=0; k<m; k++)
        temp+=fabs(c_old[k] - c[k]);   // Это мы так вычисляем критерий
} while (temp > eps);
return 0; }

Метод решения СЛАУ был придуман Якоби, но он говорил, что нельзя смешивать старые С и новые, т.е. на i-том шаге вычисляется iС по i-1, т.е. по предыдущим. Зейдель с Гауусом показали, что нарушение этого правила не только не приведет к ошибке при нахождении корней, но и может уменьшить количество потребовавшихся итераций для нахождения решения с заданной точностью.

В случае, когда порядок системы большой (много уравнений), человеку становится тяжело выводить каждое уравнение, поэтому он задаёт решение хитрым образом. Если присмотреться к уравнениям, то можно узреть закономерность: для вычисления Cx мы отнимаем от Bx сумму всех (кроме одного) Axy*Cx (т.е. Y пробегает от 0 до m-1, перескакивая через X) и делим всё это на Bxx. В свете последних открытий код выше нужно переписать так:

#include <math.h>
int main() {
int itr, k, l, m=3;
float a[m][m], b[m], c[m], c_old[m], temp, var, eps=0.000001;
// Тут надо вставить код формирования массивов A, B и записать что-нибудь в C
do
{
    itr++;
    temp=0;
    for (k=0; k<m; k++)
    {
        c_old[k]=c[k];
        var=0;
        for (l=0; l<m; l++)
            if (k!=l) var+=a[k][l]*c[l];
        c[k]=(b[k]-var)/a[k][k];
        temp+=fabs(c_old[k]-c[k]);
    }
} while (temp > eps);
return 0; }

Теперь порядок системы (количество уравнений в ней) зависит только от значения переменной m. Компьютер для того и был создан, чтобы делать за людей монотонную работу. :)

Примечания

  1. Людвиг Зейдель (18211896) — немецкий астроном и математик, Карл Фридрих Гаусс (17771855) — немецкий математик, астроном и физик

См. также