[C] wyznacznik metodą gaussa

0

jest jakiś sposób, aby zabezpieczyć się przed dzieleniem przez zero? chodzi mi o np takie przypadki:

 1 1 1     2 2 2     1 1 0
1 1 1     2 2 2     1 1 2
1 1 1     2 2 2     2 3 1
0

Przypadek 1 i 2 dają dają wartość końcową zero więc to jest bezsensu.
Przypadek 3.
algorytm powinien działać tak:

1 1 0    1 1 0       1 0 1
1 1 2    0 0 2  -->  0 1 1   * -1
2 3 1    0 1 1       0 0 2

Czyli jeśli trafi ci się taki problem to zamieniasz miejscami wiersze lub kolumny zmieniając równocześnie znak wyniku (lub jednego wiersza albo kolumny).

0

nie do końca jest bez sensu, bo robiąc eliminację gaussa jakiejkolwiek macierzy o tych samych elementach mam dzielenie przez zero. w dwóch pierwszych przypadkach można oczywiście sprawdzić czy jakiś wiersz bądź kolumna składają się z takich samych elementów, ale nie rozwiąże do przypadku trzeciego(a szukam jakiejś uniwersalnej metody). mam taki oto kod i nie bardzo mam pomysł gdzie miałaby się ta zamiana kolumn odbywać (stosuję ją tylko dla pierwszego wiersza, a dla reszty jakoś nie ogarniam):

float wyznacznik(macierz_t *macierz, int glowny)
{
    float det;
    int i,j,k,x,ctr=0;
    macierz_t *macierz1;  
    float niezerowy;

    macierz1=kopiuj_macierz(macierz);
    
    niezerowy=macierz1->macierz[0][0];                                  
            
    for(i=0;i<macierz1->lk;i++)                                                  /*zlicza niezerowe elementy pierwszego wiersza*/
    {
        if(macierz1->macierz[0][i]!=0)
        ctr++;
    }
    if(ctr==0 && glowny==0)                                                                  /*jeśli same zera w pierwszym wierszu*/
    {                                                                           /*i jest to pierwsze wywołanie funkcji*/
        printf("Macierz nieodwracalna(wyznacznik 0)");
        exit(EXIT_FAILURE);
    }
 
    if(macierz1->lw==1)
    {
        return(macierz1->macierz[0][0]);                                         /*zwraca element, jeśli macierz jest 1x1*/
    }
    else 
    {
        if(macierz1->macierz[0][0]!=0)                                           /*jeśli pierwszy el mzcierzy jest różny od 0*/
        {
            for(i=0;i<macierz1->lw-1;i++)                                        /*wykorzystując metodę eliminacji gaussa zeruje elementy*/
            {                                                                   /*pod przekątną diagonalną*/
                for(j=i+1;j<macierz1->lw;j++)
                {
                    for(k=i+1;k<macierz1->lk;k++)
                    macierz1->macierz[j][k]-=(macierz1->macierz[j][i]/macierz1->macierz[i][i])*macierz1->macierz[i][k];
                }
            }
    
            det=1;
            for(i=0;i<macierz1->lw;i++) det*=macierz1->macierz[i][i];             /*wyznacznik macierzy równy jest iloczynowi elementów*/
            return(det);                                                        /*na przekątnej diagonalnej*/
        }
        else                                                                    /*jeśli pierszy element jest równy 0*/
        {
            for(j=0;j<macierz1->lk;j++)                                          /*szuka niezerowego elementu w pierwszym wierszu*/
            {
                if(macierz1->macierz[0][j]!=0)
                {
                    niezerowy=macierz1->macierz[0][j];
                    x=j;                                                        /*przypisuje numer kolumny zmiennej x*/
                }
            }
            
            for(i=0;i<macierz1->lw;i++)                                          /*zamienia kolumny*/
            {
                niezerowy=macierz1->macierz[i][0];
                macierz1->macierz[i][0]=macierz1->macierz[i][x];
                macierz1->macierz[i][x]=niezerowy;
            }
        
            for(i=0;i<macierz1->lw-1;i++)                                        /*wykorzystując metodę eliminacji gaussa zeruje elementy*/
            {                                                                   /*pod przekątną diagonalną*/
                for(j=i+1;j<macierz1->lw;j++)
                {
                    for(k=i+1;k<macierz1->lk;k++)
                    macierz1->macierz[j][k]-=(macierz1->macierz[j][i]/macierz1->macierz[i][i])*macierz1->macierz[i][k];
                }
            }
    
            det=1;                                                              /*wyznacznik macierzy równy jest iloczynowi elementów*/
            for(i=0;i<macierz1->lw;i++) det*=macierz1->macierz[i][i];             /*na przekątnej diagonalnej*/
            return(-det);                                                       /*zamieniliśmy jedną kolumnę, więc zmieniamy znak ma*/
        }                                                                       /*przeciwny*/
    }
           
                                                                
}                                                                               
0

może popatrz jak to się robi w "Numerical Recipes".

A tu masz moją wersję. Powinno być ok, ale lepiej sprawdź czy gdzieś się nie walnąłem (bo tego nie testowałem).

double wyznacznik(macierz_t *macierz) {
     assert(macierz->lw == macierz->lk); // musi być kwadratowa

     if(macierz->lw==1) {
          return macierz1->macierz[0][0];
     }

     if(macierz->lw==2) {
          return macierz1->macierz[0][0] * macierz1->macierz[1][1] - macierz1->macierz[1][0] * macierz1->macierz[0][1];
     }

     
     macierz_t *m = kopiuj_macierz(macierz);
     int n = m->lw;
     double det = 1.0;

     for(int i=0; i<n; ++i) {
         if(m->macierz[i][i] == 0.0) {
              int j;
              for(j=i+1; j<n; ++j) // znajdź lepszy wiersz
                   if(m->macierz[j][i]!=0) {
                        double *tmp = m->macierz[i];
                        m->macierz[i] = m->macierz[j]
                        m->macierz[j] = tmp;
                        det = -det;
                        break;
                   }

              if(j==n) { // nie znaleziono zastępstwa więc wynik końcowy to zero
                   usunMacierz(m);
                   return 0;
              }
         }

         for(int j=i+1;j<n; ++j)  {
              // odejmuj od j-tego wiersza odpowiednią wielokrotność i-tego wiersza
              if(m->macierz[j][i] == 0.0)
                   continue;

              double factor = m->macierz[j][i]/m->macierz[i][i];
              m->macierz[j][i] = 0.0; // w sumie ta linijka potrzebna jest tylko podczas debugowania, żeby widzieć, że robi się ok
              for(int k=i+1;j<n; ++j)  
                    m->macierz[j][k]-=factor * m->macierz[i][k];
         }
     } // for i

     for(int i=0;i<n; ++i) 
         det *= m->macierz[i][i];

     usunMacierz(m);
     return det;
}
0

Ok, wielkie dzięki, bo z tej książki niewiele ogarniałem. Zaraz przetestuję

0

hey, mam takie samo zadanie do zrobienia tylko nie wiem jak zacząć, a to nie bardzo dziala możecie przesłać cały kod bardzo bym była wdzięczna za pomoc

0

eliminacja Gaussa-Crouta omija problem z zerami.

1 użytkowników online, w tym zalogowanych: 0, gości: 1