Rozwiązywanie układów równań liniowych metodą Gaussa.
Transkrypt
Rozwiązywanie układów równań liniowych metodą Gaussa.
Algorytmy, struktury danych i techniki programowania Piotr Wróblewski Rozwiązywanie układów równań liniowych metodą Gaussa Potrzeba rozwiązywania układów równań liniowych zachodzi w wielu dziedzinach, szczególnie technicznych. Biorąc pod uwagę, że w samym rozwiązywaniu układów równań nie ma nic odkrywczego (uczono nas już tego w szkole podstawowej!), cenne wydaje się dysponowanie procedurą komputerową, która wykona za nas tę żmudną pracę. Aby komputer mógł rozwiązać dany układ równań, musimy go uprzednio zapisać w postaci rozszerzonej, tzn. nie eliminując współczynników równych zero i pisząc zmienne w określonej kolejności. To wszystko ma na celu prawidłowe skonstruowanie macierzy rozszerzonej układu. Układ równań: 5x + z = 9 x- z+y=6 2x - y + z = 0 musi zatem zostać przedstawiony jako: 5x + 0y + 1z = 9 1x + 1y - 1z = 6 2x - 1y + 1z = 0 co pozwoli na zapisanie całości w postaci macierzowej: 5 0 1 x 9 1 1 − 1 y = 6 2 − 1 1 z 0 Wymnożenie tych macierzy powinno spowodować powrót do klasycznej, czytelnej postaci. Zaletą reprezentacji macierzowej jest możliwość zapisania wszystkich współczynników liczbowych w jednej tablicy N × (N+1) i operowania nimi podczas rozwiązywania układu. Operacje na tej macierzy będą odbiciem przekształceń dokonywanych na równaniach (np. w celu eliminacji zmiennych, dodawania równania stronami itd.). Z uwagi na łatwość implementacji programowej bardzo szeroko rozpowszechnioną metodą rozwiązywania układów równań liniowych jest tzw. eliminacja Gaussa. Przebiega ona zasadniczo w dwóch etapach: sprowadzania macierzy układu do tzw. macierzy trójkątnej, wypełnionej zerami poniżej przekątnej, oraz redukcji wstecznej, mającej na celu wyliczenie wartości poszukiwanych zmiennych. W pierwszym etapie eliminujemy zmienną x z wszystkich oprócz pierwszego wiersza (poprzez klasyczne dodawanie wiersza bieżącego, pomnożonego przez współczynnik, który spowoduje eliminację). W etapie drugim postępujemy identycznie ze zmienną y i wierszem 2. w celu ostatecznego otrzymania macierzy trójkątnej. Popatrzmy na przykładzie: -eliminacja x z wierszy 2. i 3. (efekt dodawania wierszy jest pokazany w etapie następnym): Algorytmy, struktury danych i techniki programowania *(-0,2) 5x + 0y + 1z = 9 1x + 1y - 1z = 6 2x - 1y + 1z = 0 Piotr Wróblewski *(-0,4) -eliminacja y z wierszy 1. i 3. (w pierwszym nie ma już nic do zrobienia): 5x + 0y + 1z = 9 0x + 1y – 1,2z = 4,2 0x - 1y + 0,6z = -3,6 *1 -otrzymujemy ostatecznie macierz trójkątną: 5x + 0y + 1z = 9 0x + 1y – 1,2z = 4,2 -0x + 0y – 0,6z = 0,6 Mając macierz w takiej postaci, można już pokusić się o wyliczenie zmiennych (redukcja wsteczna, idziemy do ostatniego do pierwszego wiersza układu): z = -0,6/0,6 = -1 y = 1,2z + 4,2 = 3 x = (9-z)/5 = 2 Metoda nie jest zatem skomplikowana, choć jej zapis w C++ może się wydać początkowo nieczytelny. Jedyną niebezpieczną operacją metody eliminacji Gaussa jest … eliminacja zmiennych, która czasami może prowadzić do dzielenia przez zero (jeśli na etapie i eliminowana zmienna w danym równaniu nie występuje). Biorąc jednak pod uwagę, że zamiana wierszy miejscami nie wpływa na rozwiązanie układu, niebezpieczeństwo dzielenia przez zero może być łatwo oddalone poprzez taki właśnie wybieg. Oczywiście, zamiana wierszy może okazać się niemożliwa ze względu na niespełnienie warunku, jakim jest znalezienie poniżej wiersza i takiego wiersza, który ma konfliktową zmienną różną od zera. W takim przypadku układ równań nie ma rozwiązania, co też jest pewną informacją dla użytkownika. Oto pełna treść programu wykonującego eliminację Gaussa, wraz z danymi przykładowymi: const int N=3; double x[N]; // wyniki double a[N][N+1]= { {5, 0, 1, {1, 1, -1, {2, -1, 1, }; double as[N][N+1]= { {1 , 1, 1, {0 , 1, 1, 9}, 6}, 0} 9}, 6}, Algorytmy, struktury danych i techniki programowania {0, 1, 1, Piotr Wróblewski 4} }; int gauss(double a[N][N+1], double x[N]) { int max; double tmp; for(int i=0; i<N; i++) // eliminacja { max=i; for(int j=i+1; j<N; j++) if(fabs(a[j][i])>fabs(a[max][i])) max=j; //fabs(x)=|x|, wartość bezwzględna dla danych double for(int k=i; k<N+1; k++) // zamiana wierszy wartościami { tmp=a[i][k]; a[i][k]=a[max][k]; a[max][k]=tmp; } if(a[i][i]==0) return 0; // Układ sprzeczny! for(j=i+1; j<N; j++) for(k=N; k>=i; k--) // mnożenie wiersza j przez współczynnik "zerujący": a[j][k]=a[j][k]-a[i][k]*a[j][i]/a[i][i]; } } // redukcja wsteczna for(int j=N-1; j>=0; j--) { tmp=0; for(int k=j+1; k<=N; k++) tmp=tmp+a[j][k]*x[k]; x[j]=(a[j][N]-tmp)/a[j][j]; } return 1; // wszystko w porządku! int main() { if(!gauss(a, x)) cout << "Układ (1) jest sprzeczny!\n"; else { cout << "Rozwiązanie:\n"; for(int i=0;i<N;i++) cout << "x["<<i<<"]="<<x[i] << endl; } if(!gauss(as, x)) cout << "Układ (2) jest sprzeczny!\n"; }