[Java] Implementazione algoritmo di riduzione di Gauss

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Frank-95
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 299
Iscrizione: sabato 5 dicembre 2009, 14:32
Desktop: Mate
Distribuzione: Mint 18.1
Sesso: Maschile

[Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da Frank-95 »

Ciao a tutti.

Stavo provando ad implementare questo algoritmo in java, però dubito fortemente di esserte vicino alla soluzione. Per chi non sapesse cos'è è un metodo per ridurre una matrice qualsiasi in una matrice a gradini. Questo è l'algoritmo:
[quote=Wikipedia]
L'algoritmo di Gauss trasforma una qualsiasi matrice in una matrice a scalini tramite mosse di Gauss. Funziona nel modo seguente:

Se la prima riga ha il primo elemento nullo, scambiala con una riga che ha il primo elemento non nullo. Se tutte le righe hanno il primo elemento nullo, vai al punto 3.
Per ogni riga Ai con primo elemento non nullo, eccetto la prima (i>1), moltiplica la prima riga per un coefficiente scelto in maniera tale che la somma tra la prima riga e Ai abbia il primo elemento nullo (quindi coefficiente = −Ai1/A11). Sostituisci Ai con la somma appena ricavata.
Adesso sulla prima colonna tutte le cifre, eccetto forse la prima, sono nulle. A questo punto ritorna al punto 1 considerando la sottomatrice che ottieni cancellando la prima riga e la prima colonna.
[/quote]

Non posterò tutta la classe ma solo le funzioni incriminate perché ve ne sono molte altre non utilizzate da questo metodo.

Alcuni metodi di classe:

Codice: Seleziona tutto

public class Matrix
{
	private final AtomicInteger nrows;
	private final AtomicInteger ncolumns;
	private final ArrayList<ArrayList<Double>> matrix = new ArrayList<ArrayList<Double>>();

	
	public Matrix(int rows, int cols)
	{
		this.nrows = new AtomicInteger(rows);
		this.ncolumns = new AtomicInteger(cols);
		for(int i = 0; i < this.nrows.intValue(); i++)
		{
			this.matrix.add(new ArrayList<Double>());
			for(int j = 0; j < this.ncolumns.intValue(); j++)
			{
				this.matrix.get(i).add(null);
			}
		}
	}
	
	public Matrix(Matrix mat)
	{
		this.matrix.clear();
		for(ArrayList<Double> subList: mat.getValue())
		{
			this.matrix.add(subList);
		}	
		this.nrows = new AtomicInteger(mat.getLength()[0]);
		this.ncolumns = new AtomicInteger(mat.getLength()[1]);
	}
	
	public final void init()
	{
		Scanner sc = new Scanner(System.in);
		
		for(int i = 0; i < this.nrows.intValue(); i++)
		{
			for(int j = 0; j < this.ncolumns.intValue(); j++)
			{
				this.matrix.get(i).set(j, (Double) sc.nextDouble());
			}
		}
		//sc.close();
	}
	
	public final Double getValue(int row, int col)
	{
		return this.matrix.get(row).get(col);
	}
	
	public final ArrayList<Double> getValue(int row)
	{
		return this.matrix.get(row);
	}

	public final ArrayList<ArrayList<Double>> getValue()
	{
		return this.matrix;
	}
	
	public final void setValue(int row, int col, Double val)
	{
		this.matrix.get(row).set(col, val);
	}
	
	public final void setValue(int row, ArrayList<Double> val)
	{
		this.matrix.set(row, val);
	}
	
	public final void setValue(ArrayList<ArrayList<Double>> val)
	{
		this.matrix.clear();
		for(ArrayList<Double> subList: val)
		{
			this.matrix.add(subList);
		}		
		this.nrows.lazySet(this.matrix.size());
		this.ncolumns.lazySet(this.matrix.get(0).size());
	}

	public final int[] getLength()
	{
		int[] len = new int[2];
		ArrayList<Double> subMatrix = this.matrix.get(0);
		
		len[0] = this.matrix.size();
		len[1] = subMatrix.size();

		return len;
	}
		
	private final int getMinDimension()
	{
		int[] dim = this.getLength();
		if(dim[0] <= dim[1])
		{
			return dim[0];
		}
		else
		{
			return dim[1];
		}
	}

	private final void exchangeRows(int i1, int i2)
	{
		ArrayList<Double> temp = this.getValue(i1);
		this.setValue(i1, this.getValue(i2));
		this.setValue(i2, temp);
	}
Algoritmo vero e proprio:

Codice: Seleziona tutto

	public final Matrix gaussSubMatrix()
	{
		Matrix s = new Matrix(this.getLength()[0] - 1, this.getLength()[1] - 1);
		for(int i = 1; i < this.getLength()[0]; i++)
		{
			for(int j = 1; j < this.getLength()[1]; j++)
			{
				s.setValue(i - 1 , j - 1, this.getValue(i, j));
			}
		}
		return s;
	}
	
	private static Matrix gauss1(Matrix a)
	{
		for(int i = 0; i < a.getLength()[0]; i++)
		{
			if((double) a.getValue(i, 0) == 0.0)
			{
				for(int x = i; x < a.getLength()[0]; x++)
				{
					if((double) a.getValue(i, 0) != 0.0)
					{
						a.exchangeRows(i, x);
						return gauss2(a,false);
					}
				}
			}
		}
		return gauss2(a,true);
	}
	
	private static Matrix gauss2(Matrix a, boolean allZeroPivot)
	{
		if(allZeroPivot)
		{
			return a;
		}
		
		Double pivot = a.getValue(0, 0);
		Matrix fRow = new Matrix(1, a.getLength()[1]);
		fRow.setValue(0, a.getValue(0));
		
		for(int i = 1; i < a.getLength()[0]; i++)
		{
			if((double) a.getValue(i, 0) != 0.0)
			{
				Matrix coef = Matrix.scalarProduct(fRow, -a.getValue(i, 0)/pivot);
				for(int j = 0; j < a.getLength()[1]; j++)
				{
					a.setValue(i, j, a.getValue(i, j) + coef.getValue(0, j));
				}
			}
		}
		
		return a;
	}
	
	public static Matrix rowEchelonForm(Matrix a)
	{
		int ncut = 0;
		Matrix cmat = new Matrix(a);
		
		while(cmat.getMinDimension() != 1)
		{
			cmat = new Matrix(gauss1(cmat));
			for(int i = 0; i < cmat.getLength()[0]; i++)
			{
				for(int j = 0; j < cmat.getLength()[1]; j++)
				{
					a.setValue(i + ncut , j + ncut, cmat.getValue(i, j));
				}
			}
			ncut += 1;
			cmat = cmat.gaussSubMatrix();
		}
		return a;
	}
}
Il risultato è sempre la classe di partenza :muro:

So che è un codice bello lungo, ma grazie in anticipo :)
Frank-95
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 299
Iscrizione: sabato 5 dicembre 2009, 14:32
Desktop: Mate
Distribuzione: Mint 18.1
Sesso: Maschile

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da Frank-95 »

Mi permetto di uppare questo topic, perché per me è un esercizio abbastanza importante, e non capisco dove ho sbagliato. Se è perché ancora non sono entrato molto bene nella logica java, o se c'è proprio qualche errore sematico che non riesco a trovare.

Grazie :ciao:
John_Marco
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 311
Iscrizione: martedì 5 maggio 2009, 19:55
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS X86_64
Sesso: Maschile
Località: Potenza - Roma

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da John_Marco »

Ciao, se posso darti un consiglio ti suggerisco di postare qualcosa che sia compilabile ed eseguibile, quindi magari anche una classe che contenga un main e magari una chiamata di prova (con annessi risultati attesi) in modo che si possa compilare ed eseguire per valutare effettivamente quale sia il problema..
Frank-95
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 299
Iscrizione: sabato 5 dicembre 2009, 14:32
Desktop: Mate
Distribuzione: Mint 18.1
Sesso: Maschile

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da Frank-95 »

Si hai ragione scusa. link al package con sorgenti e bytecode della classe e dell'app.

Una possibile chiamata:
C:\... java Matrice.App
//input (2x3)
1 2 3
4 5 6
//ouput (stessa matrice)
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Output atteso:
[[1.0, 2.0, 3.0], [0.0, -3.0, -6.0]]
John_Marco
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 311
Iscrizione: martedì 5 maggio 2009, 19:55
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS X86_64
Sesso: Maschile
Località: Potenza - Roma

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da John_Marco »

Ciao e scusa il ritardo nella risposta, ma ero fuori casa e sono rientrato appena 10 minuti fa :)
Ho dato uno sguardo rapido al codice ed ho risolto il problema. In pratica, se ho capito bene la tua logica, da gauss1 a gauss2 passi con un parametro che specifica se hai trovato tutti i primi elementi nulli o meno, giusto? Se questo è vero, in realtà tu non consideri il caso in cui il primo elemento sia non nullo e quindi buono per l'elaborazione. Cerco di spiegarmi con un po' di codice :
Quando scrivi

Codice: Seleziona tutto

for(int i = 0; i < a.getLength()[0]; i++)
		{
			if((double) a.getValue(i, 0) == 0.0)
			{
				for(int x = i; x < a.getLength()[0]; x++)
				{
					if((double) a.getValue(i, 0) != 0.0)
					{
						a.exchangeRows(i, x);
						return gauss2(a,false);
					}
				}
			}//Problema
		}
		return gauss2(a,true);
hai specificato al programma cosa fare nel caso in cui trova un primo elemento nullo in qualche riga, ma non quello che dovrebbe fare nel momento in cui il primo elemento sia non nullo (come nel tuo esempio). Inoltre aggiungendo al termine del for la chiamata a gauss2 con parametro true, gli stai dicendo che hai trovato tutti elementi nulli quando invece questo è falso. Se ad esempio hai una matrice fatta

Codice: Seleziona tutto

1 1
1 1
il primo if, quello che dice

Codice: Seleziona tutto

if((double) a.getValue(i, 0) != 0.0)
sarà sempre non verificato, dunque non entrerai mai in quel ramo. Il for finisce quando ha scansionato tutti e due i primi elementi ed esce, e a quel punto tu gli dici che hai trovato tutti nulli e dunque lo avvii ad un comportamento sbagliato. Per ovviare a questo errore, ti basta sostituire il tuo codice con

Codice: Seleziona tutto

private static Matrix gauss1(Matrix a)
	{
		for(int i = 0; i < a.getLength()[0]; i++)
		{
			if((double) a.getValue(i, 0) == 0.0)
			{
				for(int x = i; x < a.getLength()[0]; x++)
				{
					if((double) a.getValue(i, 0) != 0.0)
					{
						a.exchangeRows(i, x);
						return gauss2(a,false);
					}
				}
			}else
				return gauss2(a,false);
		}
	}
Quindi con l'aggiunta del ramo else per considerare il primo elemento non nullo. Infatti se provi a modificare così il tuo codice e ad eseguirlo con l'esempio che hai postato, produce il risultato da te atteso. Spero di essere stato abbastanza chiaro e comprensibile, ho scritto al volo il tutto :) Se qualcosa non è chiaro chiedi pure :) Buona serata :)
Frank-95
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 299
Iscrizione: sabato 5 dicembre 2009, 14:32
Desktop: Mate
Distribuzione: Mint 18.1
Sesso: Maschile

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da Frank-95 »

Grazie mille davvero, quello era l'errore. Comunque non è finita, perché sembra che risolvendone uno ne ho trovato un altro. Praticamente la funzione da il risultato giusto fintanto che il primo numero della prima riga è diverso da zero. Per esempio:
1 2 3
4 5 6
7 8 9
[[1.0, 2.0, 3.0], [0.0, -3.0, -6.0], [0.0, 0.0, 0.0]] <- output effettivo ed atteso
Mentre se il primo numero è zero copia una linea due volte:
0 1 2
3 4 5
0 5 6
[[3.0, 4.0, 5.0], [3.0, 4.0, 5.0], [0.0, 0.0, -0.25]] <- output effettivo ma non atteso
[[3.0, 4.0, 5.0], [0.0, 1.0, 2.0], [0.0, 0.0, -4.0]] <- output atteso
Se vuoi rilinko tutto il progetto altrimenti queste sono le funzioni cambiate (in una ho aggiunto il debug):

Codice: Seleziona tutto

	private static Matrix gauss1(Matrix a)
	{
		for(int i = 0; i < a.getLength()[0]; i++)
		{
			if((double) a.getValue(i, 0) == 0.0)
			{
				for(int x = i; x < a.getLength()[0]; x++)
				{
					if((double) a.getValue(x, 0) != 0.0)
					{
						a.exchangeRows(i, x);
						return gauss2(a,false);
					}
				}
				return gauss2(a,true);
			}
			else
			{
				return gauss2(a,false);
			}
		}
		return null; //aggiunto altrimenti eclipse mi solleva un errore ed impostato su null, perché il ciclo for esaurisce tutti i casi possibili
	}
	
	private static Matrix gauss2(Matrix a, boolean allZeroPivot)
	{
		if(allZeroPivot)
		{
			return a;
		}
		
		Double pivot = a.getValue(0, 0);
		Matrix fRow = new Matrix(1, a.getLength()[1]);
		fRow.setValue(0, a.getValue(0));
		
		for(int i = 1; i < a.getLength()[0]; i++)
		{
			if((double) a.getValue(i, 0) != 0.0)
			{
				Matrix coef = Matrix.scalarProduct(fRow, (Double) (double) Math.round((-a.getValue(i, 0)/pivot) * 1000) / 1000);
				for(int j = 0; j < a.getLength()[1]; j++)
				{
					a.setValue(i, j, a.getValue(i, j) + coef.getValue(0, j));
				}
			}
		}
		
		return a;
	}
	
	public static Matrix rowEchelonForm(Matrix a)
	{
		int ncut = 0;
		Matrix cmat = new Matrix(a);
		System.out.println("a: " + a.getValue());
		System.out.println("cmat: " + cmat.getValue());
		System.out.println("------------------------------");
		
		while(cmat.getMinDimension() != 1)
		{
			cmat = new Matrix(gauss1(cmat));
			System.out.println("cmat: " + cmat.getValue());
			System.out.println("ncut: " +  ncut);
			for(int i = 0; i < cmat.getLength()[0]; i++)
			{
				for(int j = 0; j < cmat.getLength()[1]; j++)
				{
					a.setValue(i + ncut , j + ncut, cmat.getValue(i, j));
				}
			}
			System.out.println("a: " + a.getValue());
			System.out.println("------------------------------");
			ncut += 1;
			cmat = cmat.gaussSubMatrix();
		}
		return a;
	}
John_Marco
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 311
Iscrizione: martedì 5 maggio 2009, 19:55
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS X86_64
Sesso: Maschile
Località: Potenza - Roma

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da John_Marco »

Ciao. In questo momento non sono dal mio sistema e non ho eclipse sotto mano per fare un po' di debug. Comunque, se non lo usi, ti suggerisco di provare Eclipse e, sopratutto, il suo debugger. Analizzando riga per riga il valore delle varie variabili, e conoscendo la logica e quindi quello che dovrebbe esserci, puoi scovare una marea di errori. Se non dovessi riuscirci tu a trovare l'errore in serata faccio qualche prova e ti faccio sapere
Frank-95
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 299
Iscrizione: sabato 5 dicembre 2009, 14:32
Desktop: Mate
Distribuzione: Mint 18.1
Sesso: Maschile

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da Frank-95 »

Utilizzando un po' di print per il debug l'output che esce fuori è questo:
0 1 2
3 4 5
5 6 7
a: [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [5.0, 6.0, 7.0]]
cmat: [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [5.0, 6.0, 7.0]]
------------------------------
cmat: [[3.0, 4.0, 5.0], [0.0, 1.0, 2.0], [-0.001000000000000334, -0.6680000000000001, -1.3350000000000009]] <--- Va bene.
ncut: 0
a: [[3.0, 4.0, 5.0], [3.0, 4.0, 5.0], [-0.001000000000000334, -0.6680000000000001, -1.3350000000000009]] <--- Copia della riga! (prima iterazione del for di rowechelonform)
------------------------------
cmat: [[4.0, 5.0], [-1.1102230246251565E-16, -0.5000000000000009]]
ncut: 1
a: [[3.0, 4.0, 5.0], [3.0, 4.0, 5.0], [-0.001000000000000334, -1.1102230246251565E-16, -0.5000000000000009]]
------------------------------
[[3.0, 4.0, 5.0], [3.0, 4.0, 5.0], [-0.001000000000000334, -1.1102230246251565E-16, -0.5000000000000009]]
L'errore quindi sta nel ciclo for della funzione principale. Però mi viene difficile capire perché in un caso funziona e in un altro no.

Tra l'altro devo anche aumentare la precisione di arrotondamento...
John_Marco
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 311
Iscrizione: martedì 5 maggio 2009, 19:55
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS X86_64
Sesso: Maschile
Località: Potenza - Roma

Re: [Java] Implementazione algoritmo di riduzione di Gauss

Messaggio da John_Marco »

Ciao.. Ho dato rapidamente uno sguardo al debug di Eclipse ed ho notato che prima di eseguire l'istruzione

Codice: Seleziona tutto

a.setValue(i + ncut , j + ncut, cmat.getValue(i, j));
le matrici sono buone. Nel momento in cui aggiorna 'a' si aggiorna automaticamente anche cmat, il che lascia pensare che a e cmat rappresentino la stessa istanza di Matrix. Probabilmente devi rivedere il modo in cui crei un oggetto partendo da uno già esistente
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: nik1404 e 14 ospiti