Salta al contenuto
Note per Studenti Algoritmo di Strassen e ibridazione

Algoritmo di Strassen e ibridazione

In questa pagina 6

Il prodotto ricorsivo a blocchi (Prodotto di matrici - algoritmo iterativo e ricorsivo a blocchimodello di costo sulle matrici n x n (operazioni tra scalari, taglia n oppure N = n^2); somma in n^2 operazioni (ottima); prodotto righe per colonne iterativo con costo esatto 2n^3 - n^2; proprietà frattale del prodotto a blocchi dimostrata sugli indici; algoritmo ricorsivo RMUL con T(n) = 8T(n/2) + n^2, Theta(n^3) e soluzione esatta 2n^3 - n^2 per induzione e per srotolamento: fa le stesse operazioni dell'iterativo.Prodotto di matrici - algoritmo iterativo e ricorsivo a blocchi →) ha T(n)=8T(n2)+n2T(n) = 8T(\frac n2) + n^2: la funzione di soglia n3n^3 è molto più grande del lavoro n2n^2 dei nodi interni, siamo nel caso 1 del Master Theorem - enunciato, casi e applicazioniMaster Theorem per T(n) = aT(n/b) + w(n): si confronta w(n) con la funzione soglia n^(log_b a). Caso 1 (w minore di una potenza polinomiale): Theta(n^(log_b a)); caso 2 (w della stessa taglia): Theta(n^(log_b a) log n); caso 3 (w maggiore e regolare): Theta(w(n)). Prova completa dai livelli dell'albero, versione estesa (parti intere, n0 qualsiasi), esempi, e l'uso scorretto quando nessun caso si applica (w = n log n).Master Theorem - enunciato, casi e applicazioni → e dominano le foglie. L'arietà 88 è troppo alta. Per abbassare la complessità bisogna ridurre le chiamate ricorsive senza far crescere troppo il lavoro di divide e conquer. Strassen ha mostrato che bastano 7 chiamate su matrici n2×n2\frac n2 \times \frac n2, al prezzo di 1818 somme/sottrazioni di matrici n2×n2\frac n2 \times \frac n2.

Si assume ancora n=2kn = 2^k e il modello di costo del prodotto di matrici (operazioni tra scalari a costo 11).

Le formule

Divide (14 fattori, 1010 somme/sottrazioni, perché A5,A6,B4,B7A_5, A_6, B_4, B_7 sono quadranti singoli):

ii AiA_i BiB_i
1 A12−A22A_{12} - A_{22} B21+B22B_{21} + B_{22}
2 A11+A22A_{11} + A_{22} B11+B22B_{11} + B_{22}
3 A11−A21A_{11} - A_{21} B11+B12B_{11} + B_{12}
4 A11+A12A_{11} + A_{12} B22B_{22}
5 A11A_{11} B12−B22B_{12} - B_{22}
6 A22A_{22} B21−B11B_{21} - B_{11}
7 A21+A22A_{21} + A_{22} B11B_{11}

Recurse: Pi=Ai×BiP_i = A_i \times B_i per 1≤i≤71 \le i \le 7, con sette chiamate ricorsive.

Conquer (88 somme/sottrazioni):

C11=P1+P2−P4+P6,C12=P4+P5,C21=P6+P7,C22=P2−P3+P5−P7.C_{11} = P_1 + P_2 - P_4 + P_6, \quad C_{12} = P_4 + P_5, \quad C_{21} = P_6 + P_7, \quad C_{22} = P_2 - P_3 + P_5 - P_7.

Non è noto come Strassen sia arrivato alle espressioni, ma per la correttezza non serve: basta verificarle sviluppando i prodotti (le matrici non commutano, quindi l'ordine dei fattori in ogni termine si mantiene).

Verifica di C12C_{12} e C21C_{21}:

P4+P5=(A11+A12)B22+A11(B12−B22)=A11B22+A12B22+A11B12−A11B22=A11B12+A12B22=C12,P_4 + P_5 = (A_{11} + A_{12})B_{22} + A_{11}(B_{12} - B_{22}) = A_{11}B_{22} + A_{12}B_{22} + A_{11}B_{12} - A_{11}B_{22} = A_{11}B_{12} + A_{12}B_{22} = C_{12},

P6+P7=A22(B21−B11)+(A21+A22)B11=A22B21−A22B11+A21B11+A22B11=A21B11+A22B21=C21.P_6 + P_7 = A_{22}(B_{21} - B_{11}) + (A_{21} + A_{22})B_{11} = A_{22}B_{21} - A_{22}B_{11} + A_{21}B_{11} + A_{22}B_{11} = A_{21}B_{11} + A_{22}B_{21} = C_{21}.

Verifica di C11C_{11}. Si sviluppano i quattro prodotti che compaiono:

P1=A12B21+A12B22−A22B21−A22B22,P2=A11B11+A11B22+A22B11+A22B22,−P4=−A11B22−A12B22,P6=A22B21−A22B11.\begin{aligned} P_1 &= A_{12}B_{21} + A_{12}B_{22} - A_{22}B_{21} - A_{22}B_{22},\\ P_2 &= A_{11}B_{11} + A_{11}B_{22} + A_{22}B_{11} + A_{22}B_{22},\\ -P_4 &= -A_{11}B_{22} - A_{12}B_{22},\\ P_6 &= A_{22}B_{21} - A_{22}B_{11}. \end{aligned}

Nella somma si cancellano a coppie: A12B22A_{12}B_{22} con −A12B22-A_{12}B_{22}; −A22B21-A_{22}B_{21} con A22B21A_{22}B_{21}; −A22B22-A_{22}B_{22} con A22B22A_{22}B_{22}; A11B22A_{11}B_{22} con −A11B22-A_{11}B_{22}; A22B11A_{22}B_{11} con −A22B11-A_{22}B_{11}. Restano A11B11+A12B21=C11A_{11}B_{11} + A_{12}B_{21} = C_{11}.

Verifica di C22C_{22}. P2−P3+P5−P7P_2 - P_3 + P_5 - P_7 con

P2=A11B11+A11B22+A22B11+A22B22,−P3=−A11B11−A11B12+A21B11+A21B12,P5=A11B12−A11B22,−P7=−A21B11−A22B11.\begin{aligned} P_2 &= A_{11}B_{11} + A_{11}B_{22} + A_{22}B_{11} + A_{22}B_{22},\\ -P_3 &= -A_{11}B_{11} - A_{11}B_{12} + A_{21}B_{11} + A_{21}B_{12},\\ P_5 &= A_{11}B_{12} - A_{11}B_{22},\\ -P_7 &= -A_{21}B_{11} - A_{22}B_{11}. \end{aligned}

Si cancellano A11B11A_{11}B_{11}, A11B22A_{11}B_{22}, A22B11A_{22}B_{11}, A11B12A_{11}B_{12}, A21B11A_{21}B_{11} e restano A21B12+A22B22=C22A_{21}B_{12} + A_{22}B_{22} = C_{22}.

Esempio numerico (n=2n = 2, i quadranti sono scalari): A=(1234)A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, B=(5678)B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix}.

  • P1=(2−4)(7+8)=−30P_1 = (2 - 4)(7 + 8) = -30
  • P2=(1+4)(5+8)=65P_2 = (1 + 4)(5 + 8) = 65
  • P3=(1−3)(5+6)=−22P_3 = (1 - 3)(5 + 6) = -22
  • P4=(1+2)⋅8=24P_4 = (1 + 2) \cdot 8 = 24
  • P5=1⋅(6−8)=−2P_5 = 1 \cdot (6 - 8) = -2
  • P6=4⋅(7−5)=8P_6 = 4 \cdot (7 - 5) = 8
  • P7=(3+4)⋅5=35P_7 = (3 + 4) \cdot 5 = 35

C11=−30+65−24+8=19C_{11} = -30 + 65 - 24 + 8 = 19, C12=24−2=22C_{12} = 24 - 2 = 22, C21=8+35=43C_{21} = 8 + 35 = 43, C22=65+22−2−35=50C_{22} = 65 + 22 - 2 - 35 = 50: lo stesso risultato (19224350)\begin{pmatrix} 19 & 22 \\ 43 & 50 \end{pmatrix} del prodotto per definizione. Qui Strassen usa 77 prodotti e 1818 somme/sottrazioni, in tutto 2525 operazioni, contro le 1212 dell'iterativo: sui casi piccoli non conviene.

Algoritmo SMUL

SMUL(A, B)
  n <- rows(A)
  if n = 1 then return [a11 * b11]                   { BASE }
  * quadranti Aij, Bij *
  { DIVIDE: 10 somme/sottrazioni di matrici n/2 x n/2 }
  A1 <- A12 - A22;  B1 <- B21 + B22
  A2 <- A11 + A22;  B2 <- B11 + B22
  A3 <- A11 - A21;  B3 <- B11 + B12
  A4 <- A11 + A12;  B4 <- B22
  A5 <- A11;        B5 <- B12 - B22
  A6 <- A22;        B6 <- B21 - B11
  A7 <- A21 + A22;  B7 <- B11
  { RECURSE }
  for i <- 1 to 7 do Pi <- SMUL(Ai, Bi)
  { CONQUER: 8 somme/sottrazioni }
  C11 <- P1 + P2 - P4 + P6;   C12 <- P4 + P5
  C21 <- P6 + P7;             C22 <- P2 - P3 + P5 - P7
  return C

La correttezza segue dalle quattro identità verificate sopra, per induzione sulla taglia (la stessa struttura del prodotto ricorsivo; vedi Dimostrazioni, induzione e invariantiTecniche di dimostrazione (esempio, controesempio, assurdo), induzione con casi base multipli, invarianti di ciclo (inizializzazione, conservazione, uso alla fine), schema generale per provare la correttezza; esempi svolti su arrayMax, sequenza di bit e numeri di Fibonacci.Dimostrazioni, induzione e invarianti →).

Analisi

Costi per n>1n > 1: il divide fa 1010 operazioni su matrici di n2×n2\frac n2 \times \frac n2, cioè 10⋅n24=52n210 \cdot \frac{n^2}{4} = \frac 52 n^2; il conquer ne fa 88, cioè 8⋅n24=2n28 \cdot \frac{n^2}{4} = 2n^2; la ricorsione costa 7 T(n2)7\,T(\frac n2). Il lavoro dei nodi interni è 52n2+2n2=92n2\frac 52 n^2 + 2n^2 = \frac 92 n^2:

T(n)={1n=1,7 T ⁣(n2)+92n2n>1.T(n) = \begin{cases} 1 & n = 1, \\ 7\,T\!\left(\frac n2\right) + \frac 92 n^2 & n > 1. \end{cases}

Ordine di grandezza. La soglia è nlog⁡27≈n2.81n^{\log_2 7} \approx n^{2.81}, sopra n2n^2: siamo ancora nel caso 1 e T(n)=Θ(nlog⁡27)T(n) = \Theta(n^{\log_2 7}), contro il Θ(n3)\Theta(n^3) dell'iterativo.

Soluzione esatta con la formula generale (Albero della ricorrenza e formula generale per le ricorrenzeL'albero della ricorrenza ha la forma dell'albero delle chiamate dell'istanza peggiore e T(n) è la somma dei lavori dei nodi. Per la famiglia T(n) = s(n)T(f(n)) + w(n) con base T0 per n <= n0, contando i nodi per livello con le iterate di f e il livello f*(n,n0) si ottiene la formula chiusa T(n) = somma_l [prodotto_j s(f^(j)(n))] w(f^(l)(n)) + [prodotto s] T0, verificata per induzione e applicata a esempi (merge sort, Hanoi, ricorrenze con radice).Albero della ricorrenza e formula generale per le ricorrenze →): s(n)=7s(n) = 7, f(n)=n2f(n) = \frac n2, w(n)=92n2w(n) = \frac 92 n^2, n0=1n_0 = 1, T0=1T_0 = 1, profondità f∗(n,n0)=log⁡2n−1=k−1f^*(n, n_0) = \log_2 n - 1 = k - 1. Il livello ℓ\ell ha 7ℓ7^\ell nodi di taglia n/2ℓn/2^\ell; le foglie sono 7k=nlog⁡277^k = n^{\log_2 7} (perché 7log⁡2n=nlog⁡277^{\log_2 n} = n^{\log_2 7}):

T(n)=∑ℓ=0k−17ℓ⋅92(n2ℓ)2+7k=92n2∑ℓ=0k−1(74)ℓ+7k.T(n) = \sum_{\ell=0}^{k-1} 7^\ell \cdot \frac 92 \left(\frac{n}{2^\ell}\right)^2 + 7^k = \frac 92 n^2 \sum_{\ell=0}^{k-1} \left(\frac 74\right)^\ell + 7^k.

La somma geometrica vale (7/4)k−17/4−1=43((74)k−1)\dfrac{(7/4)^k - 1}{7/4 - 1} = \dfrac 43 \left( \left(\frac 74\right)^k - 1 \right), e (74)k=7k4k=7kn2\left(\frac 74\right)^k = \dfrac{7^k}{4^k} = \dfrac{7^k}{n^2}. Quindi

T(n)=92n2⋅43(7kn2−1)+7k=6⋅7k−6n2+7k=7nlog⁡27−6n2.T(n) = \frac 92 n^2 \cdot \frac 43 \left( \frac{7^k}{n^2} - 1 \right) + 7^k = 6 \cdot 7^k - 6n^2 + 7^k = 7 n^{\log_2 7} - 6 n^2.

Controllo: n=1n = 1 dà 7−6=17 - 6 = 1; n=2n = 2 dà 7⋅7−6⋅4=257 \cdot 7 - 6 \cdot 4 = 25, il numero di operazioni dell'esempio numerico; n=4n = 4 dà 7⋅49−6⋅16=2477 \cdot 49 - 6 \cdot 16 = 247.

Costanti a confronto. Il termine dominante di Strassen ha costante 77, quello dell'iterativo costante 22: più che tripla. Per quale nn Strassen conviene? Si cerca il primo nn con

7nlog⁡27−6n2<2n3−n2.7 n^{\log_2 7} - 6n^2 < 2n^3 - n^2.

Numericamente il passaggio avviene a n≈655n \approx 655. Per le sole potenze di 22 (le taglie ammesse) la prima è 10241024: matrici con più di un milione di elementi.

nn Strassen iterativo 2n3−n22n^3 - n^2
8 2017 960
64 798967 520192
512 280902385 268173312
1024 1971035287 2146435072
2048 13816121377 17175674880

Grafico interattivo: Rapporto tra le operazioni di Strassen puro e quelle dell'iterativo: sopra 1 Strassen perde, il rapporto scende sotto 1 solo da n = 655 (a n = 1024 vale 0.918)

E il modello di costo ignora la ricorsione, i pattern di accesso alla memoria (meno regolari, quindi peggiori per la cache) e le matrici temporanee per i fattori Ai,BiA_i, B_i e i prodotti PiP_i, che l'iterativo non richiede. In pratica il punto di incrocio è molto più alto di quello teorico.

Evoluzioni. Strassen non è più l'algoritmo asintoticamente migliore: esponenti via via più piccoli (le dispense riportano Θ(n2.372)\Theta(n^{2.372}), Le Gall 2014) sono stati ottenuti con tecniche molto più complesse, con costanti enormi e senza implementazioni usabili. Strassen, pur con costanti più alte dell'iterativo, è invece usato nella pratica per matrici di grande taglia, tipicamente in forma ibrida.

Ibridazione

Definizione (ibridazione). Dati due algoritmi A1A_1 e A2A_2 per lo stesso problema, con TA1(n)=o(TA2(n))T_{A_1}(n) = o(T_{A_2}(n)) ma con costanti moltiplicative più alte per A1A_1 nell'espressione esatta, l'ibridazione li combina in un algoritmo che ha lo stesso andamento asintotico del migliore ma, se possibile, costanti più basse.

Ibrido ingenuo (STUPID_HYBRID): se nn è sotto una soglia (per esempio 10241024) si esegue MUL, altrimenti SMUL. Il costo è il minimo delle due funzioni, ma per n→∞n \to \infty l'algoritmo coincide con SMUL, quindi la costante di nlog⁡27n^{\log_2 7} resta 77: nessun miglioramento asintotico, e l'iterativo viene eseguito al più una volta.

Cambiare il caso base. Si fissa una soglia n0n_0 (potenza di 22) e si usa l'iterativo a ogni foglia dell'albero di Strassen, che viene interrotto alla taglia n0n_0:

H_MUL(A, B)
  n <- rows(A)
  if n <= n0 then return MUL(A, B)
  { DIVIDE, RECURSE, CONQUER come in SMUL, con chiamate ricorsive a H_MUL }

(Le dispense scrivono n < n0 nel codice e n <= n0 nella ricorrenza: qui si usa n <= n0, coerente con la ricorrenza.) La correttezza è la composizione di due algoritmi corretti: l'impianto è quello ricorsivo, e il caso base è risolto da un algoritmo corretto. La ricorrenza diventa

T(n,n0)={2n3−n2n≤n0,7 T ⁣(n2,n0)+92n2n>n0.T(n, n_0) = \begin{cases} 2n^3 - n^2 & n \le n_0, \\ 7\,T\!\left(\frac n2, n_0\right) + \frac 92 n^2 & n > n_0. \end{cases}

Analisi parametrica

Si pone n=2kn = 2^k, n0=2k0n_0 = 2^{k_0} e m=k−k0=log⁡2nn0m = k - k_0 = \log_2 \frac{n}{n_0} livelli interni: f∗(n,n0)=m−1f^*(n, n_0) = m - 1 ultimo livello interno, e 7m7^m foglie, ciascuna con costo 2n03−n022n_0^3 - n_0^2:

T(n,n0)=∑ℓ=0m−17ℓ⋅92(n2ℓ)2+7m(2n03−n02)=92n2⋅43((74)m−1)+7m(2n03−n02).T(n, n_0) = \sum_{\ell=0}^{m-1} 7^\ell \cdot \frac 92 \left(\frac{n}{2^\ell}\right)^2 + 7^m (2n_0^3 - n_0^2) = \frac 92 n^2 \cdot \frac 43 \left( \left(\tfrac 74\right)^m - 1 \right) + 7^m (2n_0^3 - n_0^2).

Ora (74)m=7m4m=7mn02n2\left(\frac 74\right)^m = \frac{7^m}{4^m} = \frac{7^m n_0^2}{n^2}, perché 4m=(n/n0)24^m = (n/n_0)^2. Sostituendo:

T(n,n0)=6⋅7mn02−6n2+7m(2n03−n02)=7m(2n03+5n02)−6n2.T(n, n_0) = 6 \cdot 7^m n_0^2 - 6n^2 + 7^m (2n_0^3 - n_0^2) = 7^m (2n_0^3 + 5n_0^2) - 6n^2.

Infine 7m=nlog⁡27n0log⁡277^m = \dfrac{n^{\log_2 7}}{n_0^{\log_2 7}}, da cui

T(n,n0)=nlog⁡27⋅2n0+5n0log⁡27−2−6n2.T(n, n_0) = n^{\log_2 7} \cdot \frac{2n_0 + 5}{n_0^{\log_2 7 - 2}} - 6n^2.

Controllo: per n0=1n_0 = 1 si ottiene 7nlog⁡27−6n27 n^{\log_2 7} - 6n^2 (Strassen puro); per n0=nn_0 = n si ottiene n2(2n+5)−6n2=2n3−n2n^2(2n + 5) - 6n^2 = 2n^3 - n^2 (iterativo).

Per n=16n = 16, n0=8n_0 = 8: T=7⋅(2⋅512−64)+92⋅256=7⋅960+1152=7872T = 7 \cdot (2 \cdot 512 - 64) + \frac 92 \cdot 256 = 7 \cdot 960 + 1152 = 7872, contro 79367936 dell'iterativo e 1527115271 di Strassen puro.

Scelta di n0n_0

La costante del termine dominante è g(n0)=2n0+5n0log⁡27−2g(n_0) = \dfrac{2n_0 + 5}{n_0^{\log_2 7 - 2}}, da minimizzare sulle potenze di 22. Posto s=log⁡27−2≈0.807s = \log_2 7 - 2 \approx 0.807, g(n0)=(2n0+5)n0−sg(n_0) = (2n_0 + 5) n_0^{-s} ha derivata

g′(n0)=2n0−s−s(2n0+5)n0−s−1=n0−s−1(2n0−s(2n0+5)),g'(n_0) = 2 n_0^{-s} - s(2n_0 + 5) n_0^{-s-1} = n_0^{-s-1} \bigl( 2n_0 - s(2n_0 + 5) \bigr),

positiva se e solo se 2n0(1−s)>5s2n_0 (1 - s) > 5s, cioè

n0>5s2(1−s)=5(log⁡27−2)2(3−log⁡27)≈10.48.n_0 > \frac{5 s}{2(1 - s)} = \frac{5(\log_2 7 - 2)}{2(3 - \log_2 7)} \approx 10.48.

Quindi gg scende fino a circa 10.4810.48 e poi cresce: tra le potenze di 22 il minimo è in n0=8n_0 = 8 o n0=16n_0 = 16.

n0n_0 1 2 4 8 16 32 64
g(n0)g(n_0) 7.000 5.143 4.245 3.918 3.945 4.204 4.631

Grafico interattivo: Costante g(n0) del termine dominante dell'ibrido: dal 7 di Strassen puro (n0 = 1) scende fino a 3.918 con n0 = 8 e poi risale

Si sceglie n0=8n_0 = 8: la costante passa da 77 a 3.923.92, quasi dimezzata. Con n0=8n_0 = 8 l'ibrido supera l'iterativo già da n=16n = 16 (non da 655655 come Strassen puro). Per n=1024n = 1024 i costi relativi all'iterativo sono: n0=1n_0 = 1, 0.9180.918; n0=2n_0 = 2, 0.6740.674; n0=4n_0 = 4, 0.5560.556; n0=8n_0 = 8, 0.5130.513; n0=16n_0 = 16, 0.5160.516; n0=32n_0 = 32, 0.5500.550. Come sempre, l'analisi teorica non conta ricorsione, accessi alla memoria e matrici temporanee: nelle implementazioni la soglia ottimale è più alta e si trova per via sperimentale.

Verifica con un programma

Un'implementazione di iterativo, ricorsivo, SMUL e H_MUL che conta le operazioni (somme/sottrazioni di matrici a n2n^2, prodotti iterativi a 2n3−n22n^3 - n^2) per n=1,2,…,64n = 1, 2, \dots, 64 e ogni n0≤nn_0 \le n dà gli stessi risultati del prodotto di numpy, e conteggi uguali a 2n3−n22n^3 - n^2, a 7nlog⁡27−6n27n^{\log_2 7} - 6n^2 e alla formula parametrica, per tutti i valori provati.

Esercizi collegati

Domande d'esame

  1. Quante chiamate ricorsive fa l'algoritmo di Strassen e di quante somme/sottrazioni di matrici n2×n2\frac n2 \times \frac n2 ha bisogno? Risposta: 77 chiamate, 1010 somme/sottrazioni nel divide e 88 nel conquer, in tutto 1818; T(n)=7T(n2)+92n2T(n) = 7T(\frac n2) + \frac 92 n^2, Θ(nlog⁡27)\Theta(n^{\log_2 7}).
  2. Strassen puro batte l'algoritmo iterativo? Da quale taglia? Traccia: asintoticamente sì (n2.81n^{2.81} contro n3n^3), ma 7nlog⁡27−6n2<2n3−n27 n^{\log_2 7} - 6n^2 < 2n^3 - n^2 solo da n≈655n \approx 655, quindi per le potenze di 22 da 10241024.
  3. Perché STUPID_HYBRID non migliora la costante e H_MUL sì? Traccia: il primo sceglie solo in base alla taglia di partenza, per nn grande coincide con SMUL, costante 77; il secondo usa l'iterativo alle foglie di tutto l'albero, costante g(n0)=(2n0+5)/n0log⁡27−2g(n_0) = (2n_0 + 5)/n_0^{\log_2 7 - 2}, minima (≈3.92\approx 3.92) con n0=8n_0 = 8.

Versione ripasso

  • Idea: l'arietà 88 di Prodotto di matrici - algoritmo iterativo e ricorsivo a blocchimodello di costo sulle matrici n x n (operazioni tra scalari, taglia n oppure N = n^2); somma in n^2 operazioni (ottima); prodotto righe per colonne iterativo con costo esatto 2n^3 - n^2; proprietà frattale del prodotto a blocchi dimostrata sugli indici; algoritmo ricorsivo RMUL con T(n) = 8T(n/2) + n^2, Theta(n^3) e soluzione esatta 2n^3 - n^2 per induzione e per srotolamento: fa le stesse operazioni dell'iterativo.Prodotto di matrici - algoritmo iterativo e ricorsivo a blocchi → è troppo alta (domina il lavoro alle foglie). Strassen usa 7 chiamate e 1818 somme/sottrazioni di matrici n2×n2\frac n2 \times \frac n2 (n=2kn = 2^k).
  • Divide (1010 operazioni, A5,A6,B4,B7A_5, A_6, B_4, B_7 sono quadranti singoli):
    • A1=A12−A22A_1 = A_{12} - A_{22}, B1=B21+B22B_1 = B_{21} + B_{22};
    • A2=A11+A22A_2 = A_{11} + A_{22}, B2=B11+B22B_2 = B_{11} + B_{22};
    • A3=A11−A21A_3 = A_{11} - A_{21}, B3=B11+B12B_3 = B_{11} + B_{12};
    • A4=A11+A12A_4 = A_{11} + A_{12}, B4=B22B_4 = B_{22};
    • A5=A11A_5 = A_{11}, B5=B12−B22B_5 = B_{12} - B_{22};
    • A6=A22A_6 = A_{22}, B6=B21−B11B_6 = B_{21} - B_{11};
    • A7=A21+A22A_7 = A_{21} + A_{22}, B7=B11B_7 = B_{11}.
  • Recurse: Pi=AiBiP_i = A_i B_i (sette chiamate ricorsive).
  • Conquer (88 operazioni): C11=P1+P2−P4+P6C_{11} = P_1 + P_2 - P_4 + P_6, C12=P4+P5C_{12} = P_4 + P_5, C21=P6+P7C_{21} = P_6 + P_7, C22=P2−P3+P5−P7C_{22} = P_2 - P_3 + P_5 - P_7.
  • Verifica: si sviluppano i prodotti rispettando l'ordine dei fattori (le matrici non commutano) e si cancellano i termini a coppie. Per esempio P4+P5=A11B22+A12B22+A11B12−A11B22=A11B12+A12B22P_4 + P_5 = A_{11}B_{22} + A_{12}B_{22} + A_{11}B_{12} - A_{11}B_{22} = A_{11}B_{12} + A_{12}B_{22}. Esempio n=2n = 2 con A=(1234)A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, B=(5678)B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix}: P=(−30,65,−22,24,−2,8,35)P = (-30, 65, -22, 24, -2, 8, 35) e C=(19224350)C = \begin{pmatrix} 19 & 22 \\ 43 & 50 \end{pmatrix}, con 2525 operazioni (l'iterativo ne fa 1212).
  • Ricorrenza: divide 10⋅n24=52n210 \cdot \frac{n^2}{4} = \frac 52 n^2, conquer 8⋅n24=2n28 \cdot \frac{n^2}{4} = 2n^2: T(n)={1n=17T(n2)+92n2n>1.T(n) = \begin{cases} 1 & n = 1 \\ 7T(\frac n2) + \frac 92 n^2 & n > 1. \end{cases} Soglia nlog⁡27≈n2.81>n2n^{\log_2 7} \approx n^{2.81} > n^2, caso 1: T=Θ(nlog⁡27)T = \Theta(n^{\log_2 7}) (Master Theorem - enunciato, casi e applicazioniMaster Theorem per T(n) = aT(n/b) + w(n): si confronta w(n) con la funzione soglia n^(log_b a). Caso 1 (w minore di una potenza polinomiale): Theta(n^(log_b a)); caso 2 (w della stessa taglia): Theta(n^(log_b a) log n); caso 3 (w maggiore e regolare): Theta(w(n)). Prova completa dai livelli dell'albero, versione estesa (parti intere, n0 qualsiasi), esempi, e l'uso scorretto quando nessun caso si applica (w = n log n).Master Theorem - enunciato, casi e applicazioni →).
  • Esatto (formula generale, s=7s = 7, w=92n2w = \frac 92 n^2, foglie 7k=nlog⁡277^k = n^{\log_2 7}): T=92n2∑ℓ=0k−1(74)ℓ+7k=92n2⋅43(7kn2−1)+7k=7nlog⁡27−6n2T = \frac 92 n^2 \sum_{\ell=0}^{k-1}(\frac 74)^\ell + 7^k = \frac 92 n^2 \cdot \frac 43\bigl(\frac{7^k}{n^2} - 1\bigr) + 7^k = 7n^{\log_2 7} - 6n^2. Controlli: n=2n = 2 dà 2525, n=4n = 4 dà 247247.
  • Costante 77 contro 22 dell'iterativo: 7nlog⁡27−6n2<2n3−n27n^{\log_2 7} - 6n^2 < 2n^3 - n^2 solo da n≈655n \approx 655, per le potenze di 22 da n=1024n = 1024 (1971035287<21464350721971035287 < 2146435072; a 512512 perde). Il modello ignora ricorsione, cache e matrici temporanee: nella pratica la soglia è più alta. Gli algoritmi asintoticamente migliori (esponente ≈2.37\approx 2.37) non sono implementabili.
  • Ibridazione: STUPID_HYBRID sceglie in base alla taglia di partenza, per nn grande coincide con SMUL, quindi la costante 77 non cambia. H_MUL usa MUL per n≤n0n \le n_0 a ogni foglia: T(n,n0)={2n3−n2n≤n07T(n2,n0)+92n2n>n0.T(n, n_0) = \begin{cases} 2n^3 - n^2 & n \le n_0 \\ 7T(\frac n2, n_0) + \frac 92 n^2 & n > n_0. \end{cases} La correttezza è la composizione di due algoritmi corretti.
  • Formula parametrica: con m=log⁡2nn0m = \log_2 \frac{n}{n_0} livelli, (74)m=7mn02n2(\frac 74)^m = \frac{7^m n_0^2}{n^2} e 7m=nlog⁡27n0log⁡277^m = \frac{n^{\log_2 7}}{n_0^{\log_2 7}}: T=92n2⋅43((74)m−1)+7m(2n03−n02)=7m(2n03+5n02)−6n2=nlog⁡272n0+5n0log⁡27−2−6n2.T = \frac 92 n^2 \cdot \frac 43\bigl((\tfrac 74)^m - 1\bigr) + 7^m(2n_0^3 - n_0^2) = 7^m(2n_0^3 + 5n_0^2) - 6n^2 = n^{\log_2 7}\frac{2n_0 + 5}{n_0^{\log_2 7 - 2}} - 6n^2. Controlli: n0=1n_0 = 1 Strassen puro, n0=nn_0 = n iterativo. Esempio n=16n = 16, n0=8n_0 = 8: 7⋅960+1152=7872<79367 \cdot 960 + 1152 = 7872 < 7936.
  • Scelta di n0n_0: g(n0)=2n0+5n0log⁡27−2g(n_0) = \frac{2n_0 + 5}{n_0^{\log_2 7 - 2}}; g′≥0g' \ge 0 sse n0≥5(log⁡27−2)2(3−log⁡27)≈10.48n_0 \ge \frac{5(\log_2 7 - 2)}{2(3 - \log_2 7)} \approx 10.48. Potenze di 22: g(8)≈3.918<g(16)≈3.945g(8) \approx 3.918 < g(16) \approx 3.945, quindi n0=8n_0 = 8 (costante da 77 a 3.923.92; per n=1024n = 1024 costo 0.5130.513 volte l'iterativo).
  • Errori tipici: dire 1818 operazioni nel divide (1010 divide, 88 conquer); scambiare l'ordine dei fattori nelle verifiche; confondere la soglia reale 655655 con quella per le potenze di 22 (10241024); dimenticare che n0n_0 va scelto tra le potenze di 22 (88 o 1616, non 1010).

Esercizi su questo argomento

Teoria collegata