Riduzione della complessit`a degli algoritmi

Riduzione della complessità degli
algoritmi
In generale con il termine complessità computazionale si intende il numero
complessivo di operazioni aritmetiche necessarie per la implementazione di
un algoritmo. Questo numero è, in prima approssimazione, proporzionale al
tempo di esecuzione al calcolatore. In effetti una volta implementato l’algoritmo
vanno tenuti presenti anche i tempi necessari per le varie scelte logiche che possono dare un contributo anche determinante sul tempo complessivo di elaborazione. Quindi quando si parla di complessità di un algoritmo si intende
anche il numero delle variabili create, che corrisponde alla quantit à di memoria utilizzata nell’esecuzione del programma. Spesso ci sono situazioni in cui
guardando certe proprietà del problema numerico da risolvere, si possono
trovare degli accorgimenti che risultano avere un impatto non trascurabile sul
costo computazionale dell’algoritmo di calcolo. Vediamo di seguito un paio di
esempi di questo tipo.
1 Prodotto Matrice-Vettore
Supponiamo che sia necessario compiere il prodotto matrice-vettore A ∗ b, in
cui la matrice A, di dimensione n × n, è un campionamento della funzione
f (x, y) = cos(x) ∗ cos(y)
in una griglia cartesiana di punti nell’intervallo [0, pi] × [0, pi] , e b è un vettore
colonna di lunghezza n . La matrice A può essere espressa nella forma
A = vx0 ∗ vy
dove
• vx(i) = cos(x(i)), ottenuto scegliendo un vettore riga di coordinate x
avente n elementi nell’intervallo [0, pi],
• vy(i) = cos(y(i)), ottenuto scegliendo un vettore riga di coordinate y
avente n elementi nell’intervallo [0, pi].
Per calcolare il prodotto A ∗ b, essendo A una matrice densa e b un generico
vettore, si avrà un costo computazionale dell’ordine di O(n2 ).
1
Riduzione della complessità degli algoritmi
Se, però, utilizziamo l’espressione con cui viene costruita A ed invertiamo la
precedenza nei calcoli, avremo
A ∗ b = vx0 ∗ (vy ∗ b)
in questo caso la complessità si è all’ordine di O(n) perchè si presenta un
prodotto tra un vettore e uno scalare, in quanto il prodotto (vy ∗ b) (vettore
riga per vettore colonna) fornisce come risultato uno scalare.
Esercizio: costruire un programma Matlab/Octave che esegua il prodotto matricevettore A · b nelle due modalità qui prospettate e si verifichi automaticamente
che il risultato del prodotto nei due casi è il medesimo. Inoltre, verificare
sperimentalmente, per alcuni valori di n, se l’andamento dei tempi di esecuzione segue l’andamento della complessità prospettato e costuire un grafico
(n, tempi).
2 Potenze di matrici
Per calcolare la potenza p-esima di una matrice quadrata A di ordine n cio è
Ap := A
. . ∗ A}
| ∗ .{z
p volte
senza usare l’operatore di elevamento a potenza
seguente algoritmo (pseudocodice)
∧
, si può implementare il
Pa=A;
for i=1:p-1
Pa=Pa*A;
end
Alternativamente (in maniera più stabile ed efficiente) si può decomporre p
come
M
X
p=
ci 2i
i=0
dove M = blog2 pc e ci = 0 oppure ci = 1. Si osserva facilmente che questa non
è altro che la classica rappresentazione di p in base 2. Usando la propriet à della
potenze
M Y
PM
i
i ci
B = Ap = A i=0 ci 2 =
A2
i=0
i
i−1
i−1
ove ogni termine A2 può essere calcolato come A2 · A2 .
Confrontiamo i due metodi per p = 6. Nel primo si calcola A 6 come
A6 = A ∗ A ∗ A ∗ A ∗ A ∗ A
2
Riduzione della complessità degli algoritmi
e quindi sono necessari 5 prodotti tra matrici. Nel secondo caso essendo 6 =
0 ∗ 20 + 1 ∗ 21 + 1 ∗ 22 si ha
A6 =
2 Y
A2
i
ci
1
0
2
= (A2 )0 ∗ (A2 )1 ∗ (A2 )1 = I ∗ (A2 ) ∗ (A4 ).
i=0
Calcolati A = A ∗ A ed in seguito A4 = (A2 ) ∗ (A2 ), abbiamo finalmente A6
con solo 3 prodotti tra matrici ma con lo storage addizionale di alcune matrici
in memoria.
Sia per esempio A è una matrice di ordine 7 e p = 22. Per poter utilizzare il
secondo metodo bisogna trasformare p in binario e memorizzarlo nel vettore
c. Il valore assunto da c è in questo caso [0 1 1 0 1] poiché, come si legge dalla
rappresentazione binaria (da sinistra verso destra),
2
22 = 0 · 20 + 1 · 21 + 1 · 22 + 0 · 23 + 1 · 24 .
Notiamo che il vettore c ha lunghezza M + 1 = floor(log2(22)) + 1 = 5.
Vogliamo quindi calcolare
B = (A)0 ∗ (A2 )1 ∗ (A4 )1 ∗ (A8 )0 ∗ (A16 )1 .
Un esempio di struttura del codice può essere la seguente
p=22; n=7;
c=binario(p);
A=rand(n);
B=eye(n);
C=A;
M=floor(log2(p));
for index=0:M
j=index+1;
if c(j) == 1
B=B*C;
end
C=C*C;
end
Ad ogni passo la matrice B contiene la potenza di A calcolata fino a quel moi
i−1
mento, mentre la matrice C contiene la potenza di A2 ottenuta come A2
∗
i−1
A2 . Alla fine del processo il risultato Ap sarà memorizzato in B.
Esercizio: costruire un programma Matlab/Octave che, data una generica matrice A ed assegnato un valore di q, esegua il calcolo di A q nelle due modalità
qui esposte e verifichi automaticamente che il risultato nei due casi è il medesimo. Inoltre, valutare dal programma qual’è la complessità dei due algoritmi e
verificare sperimentalmente, con una matrice A a scelta e valori di q = 4, 5, ...15
se l’andamento dei tempi di esecuzione segue l’andamento della complessit à
atteso. Costuire un grafico (n, tempi).
3