Riduzione Gauss con riordinamento pivotale

di il
9 risposte

Riduzione Gauss con riordinamento pivotale

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define MAXDIM 4
typedef double matrice [MAXDIM][MAXDIM];
typedef double vettore[MAXDIM]; 
void stampavettore(vettore,int);
void riduzionegauss(matrice,vettore,int);
int ricercapivot(matrice,int,int);
double modulo(double); 
void scambio(double&,double&);
void scambiorighe(matrice,vettore,int,int,int);
void soluzione_sistematriangolaresuperiore(matrice,vettore,vettore,int);

int main()
{int n;
do{printf("Inserire la dimensione del sistema lineare Ax=b:\n");scanf("%d",&n);} while(n!=3);
matrice A;
vettore b;
printf("Matrice A={{3,3,3},{1,3,1},{2,-1,3}}\n");
//costruzione matrice A:
for(int i=0;i<n;i++){
	for(int j=0;j<n;j++){
		printf("Inserire l'elemento a[%d][%d] della matrice:\n",i+1,j+1); scanf("%lf",&A[i][j]);
	}
}
//costruzione vettore b(le cui componenti sono le somme degli elementi di ogni riga di A):
	for(int i=0;i<n;i++) {
		for(int j=0;j<n;j++)
	{b[i]+=A[i][j];}
}
printf("Vettore b dei termini noti:\n");
for(int i=0;i<n;i++)
printf("%lf\t",b[i]);
vettore x;
riduzionegauss(A,b,n);
printf("\nMatrice dopo riduzione Gauss:\n");
for(int i=0;i<n;i++){printf("\n");
for(int j=0;j<n;j++){
	printf("%lf\t",A[i][j]);
}
}
printf("\nVettore dopo riduzione Gauss:\n");
for(int i=0;i<n;i++)
printf("%lf\t",b[i]);



return 0;
}
//Metodo della riduzione di Gauss(con riordinamento pivotale)applicato al sistema lineare Ax=b:
void riduzionegauss(matrice a,vettore v,int d)
{
 double m;//coefficienti moltiplicatori di ogni riga della matrice
                            
 int k_pivot; //indice della riga in cui si trova il pivot
	for(int k=0;k<d-1;k++) /*ordine della riduzione delle righe:in particolare,partendo dalla riga successiva alla prima(che è la riga 0)si eseguono operazioni 
                         sulle rimanenti righe,per d-1 volte(e arrivando quindi all'ordine (d-2) compreso)*/
  {k_pivot=ricercapivot(a,d,k);
   if (k_pivot!=k)
   scambiorighe(a,v,d,k,k_pivot); //scambio tra loro delle righe k-esima e k_pivot-esima(cioè quella in cui c'è il pivot)
  	for(int i=k+1;i<d;i++) //si parte considerando la "sottomatrice" della matrice di partenza(per far annullare  gli elementi sotto la k-esima colonna)
 	{m=a[i][k]/a[k][k]; //calcolo del moltiplicatore di ogni k-esima riga(della matrice di partenza e di ogni sottomatrice che si creerà dopo)
 	 for(int j=k+1;j<d;j++)
 	 { 
 	   a[i][j]=a[i][j]-m*a[k][j];/*calcolo dell'elemento a[i][j] della matrice ridotta, al passo k-esimo:gli elementi sotto la prima colonna di ogni
 	                                     sottomatrice dovranno annullarsi*/
	 }
	  v[i]=v[i]-m*v[k]; //calcolo dell'elemento v[i] della colonna dei termini noti ridotta, al passo k-esimo
	 }
  }
 return;
}
//Calcolo dei moduli degli elementi della matrice(per poi trovare il pivot,cioè l'elemento con maggior valore assoluto sulla colonna):
double modulo(double x)
{if(x<0) return -x;
 else return x;
   } 
//Ricerca dell'indice della riga in cui è presente il pivot:
int ricercapivot(matrice a,int d,int k) 
{ double mk,max;
int indice_pivot=k;
for(int i=k+1;i<d;i++){
    max=modulo(a[k][k]);
	mk=modulo(a[i][k]);
	if(mk>max)
	{max=mk;
	indice_pivot=i;
	}
}
return indice_pivot;
}
 //Funzione scambio(per poi scambiare tra loro le componenti delle due righe k-esima e k_pivot-esima della matrice):
void scambio(double& x,double& y){
double aux=x;x=y;y=aux;
return;
}
//Funzione che scambia tra loro le righe k-esima e k_pivot-esima della matrice:
void scambiorighe(matrice a,vettore v,int d,int k,int k_pivot)
{
 for(int j=k;j<d;j++) //scorrendo sulla colonna k-esima,dalla componente k-esima fino all'ultima,si scambiano gli elementi della riga k e della riga k_pivot
 scambio(a[k][j],a[k_pivot][j]);
 scambio(v[k],v[k_pivot]);
 return;
}
//Funzione che risolve il sistema triangolare superiore (ottenuto dopo la riduzione di Ax=b con l'algoritmo di Gauss) tramite l'algoritmo backward:
void soluzione_sistematriangolaresuperiore(matrice a,vettore v,vettore x,int d)
{for(int i=d-1;i>=0;i--) //partendo dall'ultima riga del sistema,si trova la componente x[i] del vettore soluzione x[d] del sistema:si fa poi l'algoritmo backward
 {x[i]=v[i];
   for(int j=i+1;j<d;j++)
   x[i]-=a[i][j]*x[j];
   x[i]/=a[i][i];
 }
return;
}

9 Risposte

  • Re: Riduzione Gauss con riordinamento pivotale

    Buon pomeriggio,sto provando lo stesso codice dell'altra volta,ma per "semplificarmi" le cose,mi sono costruito una matrice con valori già noti(e non random),per il momento. Il codice sbaglia proprio la riduzione di Gauss ma non riesco a capire dove sia l'errore. Perdonatemi di nuovo.
  • Re: Riduzione Gauss con riordinamento pivotale

    Prima di entrare nel merito della questione, vorrei farti un paio di domande:

    - si tratta di C o C++?

    - invece di considerare separatamente una matrice quadrata di NxN elementi e un vettore di N elementi, non sarebbe più comodo lavorare direttamente su una matrice rettangolare di Nx(N+1) elementi?
  • Re: Riduzione Gauss con riordinamento pivotale

    Nippolo ha scritto:


    Prima di entrare nel merito della questione, vorrei farti un paio di domande:

    - si tratta di C o C++?

    - invece di considerare separatamente una matrice quadrata di NxN elementi e un vettore di N elementi, non sarebbe più comodo lavorare direttamente su una matrice rettangolare di Nx(N+1) elementi?
    Allora,si tratta praticamente di C anche se ci ho scritto C++. Il programma è scritto in C.
    Hai ragione,perché affronterei il tutto tramite una singola matrice,ma l'insegnante(sì,è un esercizio che ci ha assegnato il docente all'università,sto studiando Matematica e per me sono i primi mesi che programmo in un qualsiasi linguaggio)ci ha detto di fare così...
  • Re: Riduzione Gauss con riordinamento pivotale

    Comunque,credo di aver capito dove sta l'errore,ma non so come risolverlo. Deve essere sbagliato lo scambio delle righe. Avevo fatto un precedente esercizio,correttamente,in cui si riduceva sempre una matrice,ma senza riordinamento pivotale(più "semplice"). Qui si richiede l'algoritmo di Gauss,completo in tutto e per tutto. Cerco di spiegarmi meglio:non ne trovo molto il senso,ma per scrivere la funzione che scambi le due righe(quella k-esima riferita all'ordine del passo k della riduzione, e quella k_pivot-esima contenente il pivot) l'insegnante vuole che all'interno venga usata un'altra funzione(sempre scritta da noi),denominata "scambio",che appunto scambi le due righe. Richiamata in "scambiorighe" viene più o meno così: Scambio(a[k][j],a[k_pivot][j], preceduto dal for(int j=k;j<n;j++) che fa scorrere le colonne appunto sulla riga,cioè scorre elemento per elemento le due righe e scambia l'elemento al posto j della riga k con l'elemento al posto j della riga k_pivot. E poi deve essere richiamata per scambiare anche i valori del termine noto, Scambio(b[k],b[k_pivot]). Che concettualmente ci sta. Anche il richiamo di una funzione dentro un'altra,fin qui nulla di strano. Ma per scambiare proprio due righe,è qui che mi sto scervellando,anche se sembra(e credo dovrebbe) essere facile. Perdonami comunque ahah
  • Re: Riduzione Gauss con riordinamento pivotale

    MaximusMeridius ha scritto:


    Allora,si tratta praticamente di C anche se ci ho scritto C++. Il programma è scritto in C.
    Scusa, ma in C non esistono i riferimenti, e nella funzione scambio() stai utilizzando il passaggio dei parametri per riferimento.

    MaximusMeridius ha scritto:


    Comunque,credo di aver capito dove sta l'errore,ma non so come risolverlo. Deve essere sbagliato lo scambio delle righe. Avevo fatto un precedente esercizio,correttamente,in cui si riduceva sempre una matrice,ma senza riordinamento pivotale(più "semplice"). Qui si richiede l'algoritmo di Gauss,completo in tutto e per tutto. Cerco di spiegarmi meglio:non ne trovo molto il senso,ma per scrivere la funzione che scambi le due righe(quella k-esima riferita all'ordine del passo k della riduzione, e quella k_pivot-esima contenente il pivot) l'insegnante vuole che all'interno venga usata un'altra funzione(sempre scritta da noi),denominata "scambio",che appunto scambi le due righe. Richiamata in "scambiorighe" viene più o meno così: Scambio(a[k][j],a[k_pivot][j], preceduto dal for(int j=k;j<n;j++) che fa scorrere le colonne appunto sulla riga,cioè scorre elemento per elemento le due righe e scambia l'elemento al posto j della riga k con l'elemento al posto j della riga k_pivot. E poi deve essere richiamata per scambiare anche i valori del termine noto, Scambio(b[k],b[k_pivot]). Che concettualmente ci sta. Anche il richiamo di una funzione dentro un'altra,fin qui nulla di strano. Ma per scambiare proprio due righe,è qui che mi sto scervellando,anche se sembra(e credo dovrebbe) essere facile. Perdonami comunque ahah
    Scusa ma cosa intendi con "riordinamento pivotale"? Il processo di riduzione di una matrice a scalini non penso ammetta chissà quali variazioni!
    In ogni caso domani se ho tempo darò un'occhiata più approfondita al tuo codice.
  • Re: Riduzione Gauss con riordinamento pivotale

    Oddio in teoria è C,ma ha tipo aggiunto alcune cose di C++,è un po' alla cacchio ahahah. Per riordinamento pivotale,scusami,intendo lo scambio tra loro delle due righe,una contenente il pivot e l'altra che è la k-esima,in cui k è il passo della riduzione.E' per fare in modo che l'algoritmo computazionale sia stabile(non ho capito ancora bene il motivo di ciò,ma quel che è sicuro è che almeno in C si fa così,l'elemento non nullo con modulo maggiore deve stare al di sopra degli altri nella stessa colonna:questi altri elementi della colonna poi dovranno essere annullati tramite operazioni sulle righe).Ad esempio, se sotto la prima colonna hai ottenuto tutti zeri,ti trovi al secondo passo(per far annullare i termini sotto l'elemento a22).
  • Re: Riduzione Gauss con riordinamento pivotale

    MaximusMeridius ha scritto:


    Oddio in teoria è C,ma ha tipo aggiunto alcune cose di C++,è un po' alla cacchio ahahah.
    Capisco, ma quel codice in C non potrà mai compilare, dovrai compilare in C++, e a quel punto dovresti includere le apposite librerie, ossia sostituire #include<xxxx.h> con #include<cxxxx>.

    MaximusMeridius ha scritto:


    Per riordinamento pivotale,scusami,intendo lo scambio tra loro delle due righe,una contenente il pivot e l'altra che è la k-esima,in cui k è il passo della riduzione.E' per fare in modo che l'algoritmo computazionale sia stabile(non ho capito ancora bene il motivo di ciò,ma quel che è sicuro è che almeno in C si fa così,l'elemento non nullo con modulo maggiore deve stare al di sopra degli altri nella stessa colonna:questi altri elementi della colonna poi dovranno essere annullati tramite operazioni sulle righe).Ad esempio, se sotto la prima colonna hai ottenuto tutti zeri,ti trovi al secondo passo(per far annullare i termini sotto l'elemento a22).
    Nel cercare il problema ho ripulito un po' il codice:
    #include <cstdio>
    
    #define N 3
    
    double modulo(double a)
    {
        if(a < 0)
        {
            return -a;
        }
        return a;
    }
    
    int ricerca_pivot(double m[N][N], int n, int k)
    {
        unsigned int i_max = k;
        for(unsigned int i = k + 1; i < n; ++i)
        {
            double Max = modulo(m[k][k]);
            double mod = modulo(m[i][k]);
            if(mod > Max)
            {
                Max = mod;
                i_max = i;
            }
        }
        return i_max;
    }
    
    void scambio(double &a, double &b)
    {
        double temp = a;
        a = b;
        b = temp;
    }
    
    void scambio_righe(double m[N][N], double v[N], int n, int k, int k_pivot)
    {
        for(unsigned int j = k; j < n; ++j)
        {
            scambio(m[k][j], m[k_pivot][j]);
        }
        scambio(v[k], v[k_pivot]);
    }
    
    void riduzione_gauss(double m[N][N], double v[N], int n)
    {
        for(unsigned int k = 0; k < n - 1; ++k)
        {
            unsigned int k_pivot = ricerca_pivot(m, n, k);
            if(k_pivot != k)
            {
                printf("!\n");
                scambio_righe(m, v, n, k, k_pivot);
            }
            for(unsigned int i = k + 1; i < n; ++i)
            {
                double q = -m[i][k] / m[k][k];
                for(unsigned int j = k; j < n; ++j)
                {
                    m[i][j] += q * m[k][j];
                }
                v[i] += q * v[k];
            }
        }
    }
    
    int main()
    {
        double m[N][N] = {{3, 3, 3}, {1, 3, 1}, {2, 1, 3}};
        double v[N] = {1, 5, 7};
        riduzione_gauss(m, v, N);
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; ++j)
            {
                printf("%lf\t", m[i][j]);
            }
            printf("%lf\t\n", v[i]);
        }
    }
    L'errore era nel fatto che l'ultimo for della funzione riduzionegauss() partiva da k+1 invece che da k.
  • Re: Riduzione Gauss con riordinamento pivotale

    E' vero,partendo da k nel for su j, viene. Però non ho capito il motivo. Teoricamente devi far annullare i coefficienti sotto il pivot,quel for e quello sopra servono a quello. Se tu stai all'ordine k della riduzione,dovresti partire da (k+1) sia sulle righe,per i,che sulle colonne,per j. Mi sto confondendo. A livello matematico è molto più semplice comunque(lo dico essendo di parte eh,sono uno studente di matematica ahahaha)
  • Re: Riduzione Gauss con riordinamento pivotale

    Ah no,giusto,tutto torna. Devo far annullare gli elementi sotto la riga k-esima,ma dalla k-esima colonna(compresa)in poi. Giusto. Ti ringrazio di cuore comunque!!!
Devi accedere o registrarti per scrivere nel forum
9 risposte