Matrici Java v1
Matrici Java v1
2.
Operazioni
su
Matrice
Date
due
matrici
A
e
B
conformi
(cio
il
numero
n
di
righe
di
A
uguale
al
numero
di
righe
di
B
e
il
numero
m
di
colonne
di
A
uguale
al
numero
di
colonne
di
B),
possiamo
calcolare
la
matrice
C
come
somma
di
A
e
B.
Cio
per
0
<=
i
<
n,
0
<=
j
<
m,
C[i][j]
=
A[i][j]+B[i][j].
Presentiamo
di
seguito
un
metodo
per
il
calcolo
della
somma
che
restituisce
true
se
la
somma
stata
effettuata
e
false
diversamente
(cio
le
matrici
non
sono
conformi).
static boolean sommaMatrici( double[] [] A, double [][] B, double [][] C) { // somma di due matrici: C = A+B // Le 3 matrici devono essere conformi (stesse dimensioni) if ( A.length != B.length || A[0].length != B[0].length || A.length != C.length || A[0].length != C[0].length ) return false; // matrici non conformi
for ( int i = 0; i < A.length; i++) for ( int j =0; j < A[0].length; j++ ) C[i][j] = A[i][j]+B[i][j]; return true; }
La
complessit
delloperazione
di
somma
(nm)
ovvio
che
si
tratta
di
un
algoritmo
ottimale
(non
possibile
fare
di
meglio
in
quanto
vanno
trascritti
tutti
i
valori
su
C
e
questi
sono
in
numero
di
n
m).
In
modo
simile
pu
essere
definita
loperazione
di
sottrazione.
Unaltra
classica
operazione
sulle
matrice
il
calcolo
della
trasposta.
Data
una
matrice
A
di
dimensioni
n
x
m,
la
sua
trasposta
una
matrice
AT
di
dimensioni
m
n
tale
che
per
ogni
0
<=
i
<
n,
0
<=
j
<
m,
AT[j][i]
=
A[i][j].
Presentiamo
di
seguito
un
metodo
per
restituisce
la
trasposta
di
una
matrice.
static double [] [] trasposta ( double [][] A) { // AT la trasposta della matrice M double [][] AT = new double [A[0].length][A.length]; for ( int i = 0; i < A.length; i++ ) for ( int j = 0; j < A[0].length; j++ ) AT[j][i] = A[i][j]; return AT; }
La
complessit
delloperazione
ancora
(n
m)
anche
in
questo
caso
che
si
tratta
di
un
algoritmo
ottimale
(non
possibile
fare
di
meglio
in
quanto
vanno
trascritti
tutti
i
valori
su
C
e
questi
sono
in
numero
di
n
m).
Vediamo
ora
come
calcolare
il
prodotto
scalare
di
due
matrici
A
e
B
conformi
(cio
il
numero
di
colonne
di
A
uguale
al
numero
di
righe
di
B).
Il
risultato
una
matrice
C
che
ha
lo
stesso
numero
di
righe
di
A
e
lo
stesso
numero
di
colonne
di
B.
Siano
n
il
numero
di
righe
di
A,
p
il
numero
di
colonne
di
A
(pari
al
numero
di
righe
di
B)
e
m
il
numero
di
colonne
di
B.
Per
ogni
0
<=
i
<
n,
0
<=
j
<
m,
C[j][i]
=
0
<=
k
<
p
A[i][k]
x
B[k][j].
Presentiamo
di
seguito
un
metodo
per
il
calcolo
del
prodotto
scalare
che
restituisce
true
se
il
prodotto
stato
effettuato
e
false
diversamente
(cio
le
matrici
non
sono
conformi).
static boolean moltiplicaMatrici( double[][] A, double []] B, double [][] C) { if ( A[0].length != B.length || A.length != C.length || B[0].length != C[0].length ) return false; // matrici non conformi for ( int i = 0; i < C.length; i++) for ( int j =0; j < C[i].length; j++ ) { C[i][j] = 0; for ( int k = 0; k < A[0].length; k++ ) C[i][j] += A[i][k]*B[k][j]; } return true; }
La complessit delloperazione ancora (n p m) che diventa (n3) nel caso di matrici quadrate. In questo caso non si tratta di un algoritmo ottimale in quanto il lower bound del problema (n2). In effetti esistono algoritmi che effettuano il prodotto in tempo (nq) con q intorno a 2.5.
3.
Matrici
Triangolari
Una
matrice
quadrata
A
di
dimensioni
n
x
n
triangolare
inferiore
se
tutti
gli
elementi
sopra
la
diagonale
principale
sono
uguali
a
0,
cio
per
0
<=
i
<
n,
i
<
j
<
m,
C[i][j]
=
0.
Presentiamo
di
seguito
un
metodo
per
la
verifica
se
una
matrice
quadrata
sia
triangolare
inferiore.
static boolean eTriangolareInferiore( double [] [] A ) { boolean verificato = true; for ( int i = 0; i < A.length-1 && verificato; i++ ) for ( int j = i+1; j < A[0].length && verificato; j++) verificato = A[i][j] == 0; return verificato; }
Calcoliamo
il
numero
N
di
confronti
effettuati
dal
metodo.
Per
i
=
0,
vengono
effettuati
n-1
confronti,
per
i
=
1,
n-2
confronti,
,
per
i
=
n-2,
1
confronto
e
per
i
=
n-1,
0
confronti.
Quindi
N
=
(n-1)
+
(n-2)
+
+
1
=
n
(n-1)
/
2.
Quindi
la
complessit
(n2).
Una
matrice
quadrata
A
di
dimensioni
n
x
n
triangolare
superiore
se
tutti
gli
elementi
sotto
la
diagonale
principale
sono
uguali
a
0,
cio
per
0
<=
i
<
n,
0
<=
j
<
i,
C[i][j]
=
0.
Presentiamo
di
seguito
un
metodo
per
la
verifica
se
una
matrice
quadrata
sia
triangolare
superiore.
static boolean eTriangolareSuperiore( double [] [] A ) { boolean verificato = true; for ( int i = 1; i < A.length && verificato; i++ ) for ( int j = 0; j < i && verificato; j++) verificato = A[i][j] == 0; return verificato; }
Anche
in
questo
caso
il
numero
N
pari
a
n
(n-1)
/
2
e
la
complessit
(n2).
Un
caso
degenere
di
matrice
triangolare
una
matrice
diagonale
in
cui
tutti
gli
elementi
sopra
e
sotto
la
diagonale
principale
sono
uguali
a
0,
cio
per
0
<=
i
<
n,
0
<=
j
<
n,
i
j,
C[i][j]
=
0.
Presentiamo
di
seguito
un
metodo
per
la
verifica
se
una
matrice
quadrata
sia
diagonale.
static boolean eDiagonale( double [] [] A ) { boolean verificato = true; for ( int i = 0; i < A.length && verificato; i++ ) for ( int j = i+1; j < A[0].length && verificato; j++) verificato = A[i][j] == 0 && A[j][i] == 0; return verificato; }
= b0 = b1 = bi
+ + an 1,n 1 x n 1 = bn 1
In forma matriciale S pu essere scritto come A XT = BT, dove A la matrice nn dei coefficienti ai,j, XT il trasposto del vettore X delle incognite xi e BT il trasposto del vettore dei termini noti bi (cio XT e BT hanno dimensioni n1).
La risoluzione del sistema consiste nel determinare il vettore X delle incognite. Affrontiamo questo problema considerando per prima il caso semplice in cui la matrice A sia triangolare superiore (o singolare, cio tutti gli elementi sulla diagonale principale aj,j sono diversi da 0. Il inferiore) e non sistema ha quindi questo formato:
a0,0 x 0 S
+ + a0,n 1 x n 1 + + a1,n 1 x n 1
= b0 = b1
+ + a j ,n 1 x n 1 = b j an 1,n 1 x n 1 = bn 1
In
questo
caso
la
risoluzione
immediata
in
quanto
sufficiente
calcolare
xn-1
=
bn-1/an-1,n-1
dallultima
equazione
e
quindi
sostituire
questo
valore
per
xn-1
nella
penultima
equazione:
an 2,n 2 x n 2 + an 2,n 1 x n 1 = bn 2
per
calcolare
xn-2
=
(bn-2
an-2,n-1
bn-1/an-1,n-1)/an
-2,n
-2
e
cos
via.
Il
sistema
S
triangolare
superiore
pu
essere
quindi
risolto
dal
seguente
metodo
si
noti
che
per
valutare
una
eventuale
singolarit
della
matrice,
va
verificato
se
sulla
diagonale
non
ci
sia
qualche
elemento
uguale
a
zero:
static boolean risSistemaTriangSup( double [] [] A, double [] B, double [] X ) { if ( diagonaleConZero(A) ) return false; for ( int i = X.length-1; i >= 0 ; i-- ) { X[i] = B[i]; for ( int j=i+1; j < A[0].length; j++) X[i] -= X[j]*A[i][j]; X[i] = X[i]/A[i][i];
} return true; }
Poich
ci
sono
due
cicli
di
'for'
innestati,
la
funzione
ha
complessit
(n2);
quindi
essa
ha
una
complessit
ottima
in
quanto
per
calcolare
le
incognite
non
possibile
evitare
di
scandire
gli
elementi
nel
triangolo
non-zero
della
matrice
che
sono
in
numero
di
n(n+1)/2
=
(n2).
Si
noti
che
abbiamo
assunto
che
le
matrici
A
e
B
e
il
vettore
X
abbiano
dimensioni
conformi
per
cui
non
effettuato
alcun
controllo
responsabilit
del
metodo
chiamante
verificare
tale
conformit
prima
della
chiamata.
Il
metodo
diagonaleConZero
:
static boolean diagonaleConZero( double [] [] A ) { boolean esisteZero = false; for ( int i = 0; i < A.length && !esisteZero; i++ ) esisteZero = A[i][i] == 0; return esisteZero; }
La
complessit
ovviamente
(n).
Prima
di
procedere
alla
risoluzione
del
caso
generale
di
un
sistema
di
equazioni
lineari,
mostriamo
la
risoluzione
anche
per
il
caso
di
matrice
triangolare
inferiore.
static boolean risSistemaTriangInf( double [] [] A, double [] B, double [] X ) { if ( diagonaleConZero(A) ) return false; for ( int i = 0; i < X.length ; i++ ) { X[i] = B[i]; for ( int j=0; j < i; j++) X[i] -= X[j]*A[i][j]; X[i] = X[i]/A[i][i]; } return true; }
Per risolvere un sistema generale di equazioni lineari sufficiente far precedere la funzione 'risSistemaTriangSup' da un passo di triangolarizzazione della matrice, ad esempio applicando la ben nota eliminazione gaussiana nel seguente modo. Per rendere la matrice A triangolare superiore, procediamo nel seguente modo. Cominciamo con azzerare tutti i coefficienti della colonna 0 sotto la diagonale, cio aj,1, 1 j n-1, in modo che la matrice diventi:
= b0 = b1 = bi
+ + an 1, j x n 1 + + an 1,n 1 x n 1 = bn 1
A tale scopo lequazione 1 viene modificata sottraendo ad essa la riga 0 moltiplicata per a1,0/a0,0 in modo che il coefficiente di x1 nella riga 1 (cio a1,0) diventi zero. Quindi per ogni j, 0 j n-1, ogni coefficiente a1,j viene modificato in a1,j a0,j a1,0/a0,0 e il termine noto b1 viene modificato in b1 b0
a1,0/a0,0. Allo stesso modo si annulla il coefficiente ai,0 di x0 nelle altre righe i sottostanti, sottraendo a ciascuna di esse la riga 0 moltiplicata per ai,0/a0,0. Passiamo ora ad annullare i coefficienti sotto la diagonale della colonna 1; pertanto, per ogni riga i, 2 i n-1, annulliamo il coefficiente ai,1 di x1 sottraendo ad essa la riga 1 moltiplicata per ai,1/a1,1. Si noti che il coefficiente ai,0, che era stato modificato a 0 nel passo precedente, rimane tale in quanto per esso la sottrazione comporta la modifica in ai,0 a1,0 ai,1/a1,1 , che vale 0 essendo anche a1,0 = 0.
= b0 = b1 = bi
+ + an 1,n 1 x n 1 = bn 1
Iteriamo il procedimento per tutte le altre colonne j. Per annullare i coefficienti sotto la diagonale della colonna j, per ogni riga i, j+1 i n-1, annulliamo il coefficiente di xj sottraendo alla riga i la riga j moltiplicata per ai,j/aj,j. Al passo j-mo il sistema diventa:
+ a0, j x j + a1, j x j a j, j x j
= b0 = b1 = bj
an 1, j x n 1 + + an 1,n 1 x n 1 = bn 1
Alla fine delle iterazioni il sistema diventa triangolare superiore a meno che la matrice A non sia singolare e non ammetta, quindi, soluzioni. Per la verifica di singolarit, sufficiente verificare che, ad passo j della triangolarizzazione, non sia effettuata una divisione per aj,j sia diverso da 0 ogni altrimenti verrebbe effettuata una divisione per zero. Ma il fatto che un qualche aj,j sia zero non comporta necessariamente che la matrice sia singolare; infatti l'anomalia potrebbe essere eliminata con un opportuno scambio di righe. Ad esempio, la matrice
0 1
1 1
pu
essere
triangolarizzata
semplicemente
scambiando
le
due
righe.
Introduciamo
quindi
nell'algoritmo
di
triangolarizzazione
la
possibilit
di
scambiare
una
riga
i
con
una
riga
successiva
se
il
coefficiente
aj,j
sulla
diagonale
nullo.
Otteniamo
cos
un
metodo
per
risolvere
un
sistema
generale
di
equazioni
lineari
che
realizza
il
metodo
di
eliminazione
di
Gauss:
static boolean risSistemaEquazioni ( double [][] A, double [] B, double [] X) { boolean singolare = false; for ( int i = 0; i < A.length-1 && !singolare; i++ ) { if ( A[i][i] == 0 ) singolare = !scambiaRiga(A,B,i); for ( int j = i+1; j < A.length && !singolare; j++ ) { double d = A[j][i]/A[i][i]; for ( int k = i+1; k < A.length; k++ ) A[j][k] -= d*A[i][k]; /***
B[j] -= d*B[i]; } } if ( !singolare ) risSistemaTriangSup(A,B,X); return !singolare; } static boolean scambiaRiga( double [] [] A, double [] B, int i ) { boolean trovatoNonZero = false; for ( int j = i+1; j < A.length && !trovatoNonZero; j++ ) if ( A[i][j] != 0 ) { trovatoNonZero=true; double temp = B[i]; B[i]=B[j]; B[j]=temp; for (int k = i; k < A.length; k++) { temp = A[i][k]; A[i][k]=A[j][k]; A[j][k]=temp; } } return trovatoNonZero; }
Il
metodo
scambiaRiga
ha
una
complessit
(n)
in
quanto
il
for
pi
interno
eseguito
al
pi
una
volta.
Pertanto,
poich
la
complessit
del
metodo
risSistemaTriangSup
(n2)
come
abbiamo
gi
mostrato
precedentemente,
la
complessit
del
metodo
risSistemaEquazioni
determinata
dalla
istruzione
asteriscata
dentro
i
tre
cicli
di
'for'
per
cui
essa
(n3).
L'algoritmo
di
risoluzione
non
ottimale
poich
esistono
algoritmi
che
risolvono
un
sistema
di
equazioni
in
tempo
(nk)
con
2
<
k
<
3.
Si
noti
che
la
complessit
del
problema
(n2)
in
quanto
la
semplice
scansione
di
tutti
i
coefficienti
del
sistema
richiede
un
tempo
quadratico.
Tale
limite
pu
essere
abbattuto
nel
caso
di
matrici
sparse
(cio
con
un
numero
limitato
di
valori
diversi
da
zero)
tramite
algoritmi
ad
hoc.
la matrice con fattore di compressione 4 la matrice B di dimensione 3 2 : in quanto le sei celle di B rappresentano la compressione dei valori dei 6 gruppi di celle di A, come indicati di seguito: Utilizzando un fattore di compressione 3, la matrice B ha dimensione 4 3 e rappresenta i seguenti valori: rappresentano la compressione dei valori dei 12 gruppi di celle di A, come indicati di seguito::
Si
noti
che
i
4
gruppi
sul
bordo
destro
hanno
dimensione
3
2
in
quanto
lapplicazione
del
fattore
di
riduzione
3
al
numero
di
colonne
8
d
il
valore
2,667
(arrotondato
a
3).
In
alcuni
casi
pu
essere
richiesto
che
nella
aggregazione
di
celle
sia
effettuata
la
somma
dei
valori
piuttosto
che
la
loro
media
ad
esempio
nel
caso
che
i
valori
rappresentino
la
popolazione
in
una
cella
territoriale.
Di
seguito
presentiamo
un
metodo
che
effettua
la
compressione
a
partire
dalla
matrice
A,
del
fattore
di
compressione
fC,
che
deve
essere
un
intero
maggiore
di
1,
e
dallindicazione
tramite
il
parametro
S_M
se
si
tratta
di
effettuare
la
somma
(S_M
=
S)
o
la
media
altrimenti:
static double [][] compressione ( double [][] A, int fC, char S_M ) { int n = A.length; int m = A[0].length; int nc = (int) Math.ceil(n / (double) fC); //approssimazione per eccesso int mc = (int) Math.ceil(m / (double) fC); //approssimazione per eccesso double [][] B = new double[nc][mc]; for ( int i = 0; i < nc; i++ ) for ( int j = 0; j < mc; j++ ) B[i][j]=aggregazione(A,i*fC,j*fC, fC, S_M); return B; } static double aggregazione ( double[][] A, int i, int j, int fC, char S_M) { double somma =0; int nElem = 0; for ( int ic=i; ic<A.length && ic < i+fC; ic++) for ( int jc=j; jc<A[0].length && jc < j+fC; jc++) { somma += A[ic][jc]; nElem++; } if ( S_M == S ) return somma; else return somma/nElem; }
Il
metodo
aggregazione
ha
una
complessit
C
=
(fC2)
a
causa
dei
due
for
innestati,
ciascuno
ripetuto
al
pi
fC
volte.
La
complessit
del
metodo
compressione
ha
complessit
(nc
mc
C2);
poich
nc
=
n/fC,
mc
=
m/fC,
abbiamo
(n
m
C2/
fC2).
Quindi
essendo
C
=
(fC2),
la
complessit
della
compressione
(n
m)
e
quindi
si
tratta
di
un
metodo
ottimale
in
quanto
per
comprimere
i
valori
di
A
necessario
scorrerli
tutti.
domanda 1/8 se B posiziona il suo punto di vendita a 1/4 in tal caso tutti i clienti da 1/4 in poi vanno da B e quelli compresi tra 0 e 1/4 si dividono tra A e B 1/4 se B posiziona il suo punto di vendita a 1/2 in tal caso tutti i clienti da 1/2 in poi vanno da B e quelli compresi tra 0 e 1/2 si dividono tra A e B 3/8 se B posiziona il suo punto di vendita a 3/4 in tal caso tutti i clienti da 3/4 in poi vanno da B e quelli compresi tra 0 e 3/4 si dividono tra A e B 1/2 se B posiziona il suo punto di vendita alla fine del ponte in tal caso tutti i clienti si divideranno equamente tra i due punti di vendita. Allo stesso modo si calcolano i valori delle altre righe. Pertanto la matrice M : Avendo a disposizione M, il soggetto A calcoler per ogni riga i il valore minimo, che rappresenta la sua minima quota di mercato se dovesse decidere di allocare il punto vendita in i. La sua scelta allora ricadr sulla riga i che ha il valore minimo pi grande nella fattispecie la posizione i=2 a met del ponte. Anche B riesce a costruire la matrice M ma i suoi calcoli li far per colonna. Per ogni colonna j, egli calcola il valore massimo, che rappresenta la massima quota di mercato di A se B dovesse decidere di allocare il punto vendita in j. Poich egli vuole minimizzare la quota di mercato di A, la scelta di B ricadr sulla colonna j che ha il valore massimo pi piccolo nella fattispecie la posizione j=2 a met del ponte. Lelemento M[2][2] un punto di sella ( il minimo della riga 2 e il massimo della colonna 2) e costituisce la situazione di equilibrio, cio due soggetti A e B razionali e avversari alla fine opteranno per tale scelta.: entrambi posizioneranno il proprio punto di vendita a met del ponte. Per verificare che non ci sono altri punti di sella, riportiamo di seguito i valori minimi di riga e i valori massimi di colonna: Pur non essendoci altri punti di equilibrio, tuttavia esistono altre 8 scelte che permettono ai due avversari di suddividersi equamente le quote di mercato (cio valori della matrice pari a 1/2: a. entrambi collocano il punto di vendita alla stessa estremit del ponte (2 possibilit)
10
b. entrambi
collocano
il
punto
di
vendita
a
1/4
o
a
3/4
del
ponte
(altre
2
possibilit)
c. A
colloca
il
suo
punto
di
vendita
ad
una
estremit
e
B
allaltra
(ulteriori
2
possibilit)
d. A
colloca
il
suo
punto
di
vendita
a
1/4
e
B
a
3/4
o
viceversa
(ultime
2
possibilit)
E
interessante
notare
che
delle
8
altre
possibilit,
le
ultime
due
(punto
d)
sono
vantaggiose
per
la
clientela
in
quanto
riducono
il
totale
degli
spostamenti
in
effetti
ogni
cliente
al
massimo
si
dovr
spostare
di
1/4
di
ponte
per
arrivare
al
punto
di
vendita
mentre
nella
soluzione
del
punto
di
sella
i
clienti
agli
estremi
dovranno
percorrere
met
del
ponte.
Ma
nonostante
questo
vantaggio,
tale
soluzione
non
sar
scelta
dai
due
soggetti
spontaneamente
il
soggetto
A
non
proporr
la
collocazione
a
1/4
per
timore
che
B
scelga
la
collocazione
a
met.
Lunico
modo
per
pervenire
alle
soluzioni
di
cui
al
punto
d
che
ci
sia
una
regolamentazione
da
parte
di
un
soggetto
terzo,
nel
nostro
caso
lUniversit.
Possiamo
ora
mostrare
il
metodo
calcoloPuntiSella,
che
restituisce
tutti
gli
eventuali
punti
di
sella
di
una
matrice.
Il
metodo
riceve
come
parametro
la
matrice
M,
gli
indici
di
riga
(iPuntiSella)
e
gli
indici
di
colonna
(jPuntiSella)
degli
eventuali
punti
di
sella
e
restituisce
il
numero
di
punti
di
sella.
I
due
array
iPuntiSella
e
jPuntiSella
sono
stati
dimensionati
dal
metodo
chiamante
con
n
m
elementi
per
il
caso
estremo
che
tutti
gli
elementi
di
M
siano
punti
di
sella.
Il
metodo
calcoloPuntiSella
restituisce
il
numero
effettivo
di
punti
di
sella
nel
caso
dellesempio
dei
due
punti
di
vendita
sul
ponte
dellUniversit,
viene
restituito
1
e
i
due
array
iPuntiSella
e
jPuntiSella
contengono
ciascuno
un
solo
valore,
in
particolare
iPuntiSella[0]
=
2
e
jPuntiSella[0]
=
2,
che
stabiliscono
che
lunico
punto
di
sella
M[2][2].
static int calcoloPuntiSella ( double [][] M, int [] iPuntiSella, int [] jPuntiSella ) { int n = M.length; int m = M[0].length; double [] minRiga = new double[n]; for ( int i=0; i < n; i++) minRiga[i]=minimoRiga(M,i); double [] maxColonna = new double [m]; for ( int j=0; j < m; j++) maxColonna[j]=massimoColonna(M,j); int nPuntiSella = 0; for ( int i = 0; i < n; i++) for (int j =0; j < m; j++) if ( M[i][j] == minRiga[i] && M[i][j] == maxColonna[j]) { iPuntiSella[nPuntiSella]=i;jPuntiSella[nPuntiSella]=j; nPuntiSella++; } return nPuntiSella; } static double minimoRiga ( double[][] M, int i ) { double minimo =M[i][0]; for ( int j=1; j<M[0].length; j++) if ( M[i][j]<minimo) minimo = M[i][j]; return minimo; } static double massimoColonna ( double[][] M, int j ) { double massimo =M[0][j]; for ( int i=1; i<M.length; i++) if ( M[i][j]>massimo) massimo = M[i][j]; return massimo; }
E facile verificare che la complessit dellalgoritmo (n m), cio lalgoritmo ottimale non essendo in generale possibile calcolare i punti di sella senza consultare tutti i valori di M.
11