Salta al contenuto
Note per Studenti Esercizio - algoritmo ibrido con soglia ottima e confronto con la versione ricorsiva (compito del 4 settembre 2024, es. 1)

Esercizio - algoritmo ibrido con soglia ottima e confronto con la versione ricorsiva (compito del 4 settembre 2024, es. 1)

Esame
In questa pagina 3

Testo (compito d'esame del 4 settembre 2024 di Algoritmi per l'Ingegneria, Ing. Informatica UniPD, seconda parte, esercizio 1, 16 punti). Dato un problema Π⊆I×S\Pi \subseteq I \times S, si consideri un algoritmo ricorsivo A1(i)A_1(i) per Π\Pi la cui complessità in tempo obbedisce alla ricorrenza, per valori della taglia n=∣i∣n = \lvert i\rvert che siano potenze di due, TA1(n)={2n=14 TA1(n/2)+128 nn>1.T_{A_1}(n) = \begin{cases} 2 & n = 1 \\ 4\,T_{A_1}(n/2) + 128\,n & n > 1. \end{cases} Sia inoltre A2(i)A_2(i) un ulteriore algoritmo per Π\Pi di complessità TA2(n)=2n3T_{A_2}(n) = 2n^3.

  1. (a) [3 punti] Si delinei lo pseudocodice dell'algoritmo ibrido H(i,n0)H(i, n_0), ottenuto combinando la strategia ricorsiva di A1A_1 con l'uso di A2A_2 per risolvere sottoistanze di taglia ≤n0\le n_0.
  2. (b) [8 punti] Si scriva e si risolva la ricorrenza parametrica TH(n,n0)T_H(n, n_0), con n,n0≤nn, n_0 \le n potenze di due, associata all'algoritmo sviluppato al punto (a).
  3. (c) [5 punti] Si determini il valore nˉ0\bar n_0 che determina la migliore complessità in tempo per HH.

Teoria usata: 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 → (formula generale con taglia di base n0n_0), 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 →. Stessa struttura dell'esercizio Esercizio - algoritmo ibrido e valore ottimo della soglia (compito del 27 gennaio 2025, es. 1), con costanti diverse e un'ottimizzazione più semplice.

(a) Pseudocodice di HH

Si modifica il caso base di A1A_1: sotto la soglia si passa a A2A_2. A1A_1 genera quattro sottoistanze di taglia n/2n/2 (da s(n)=4s(n) = 4, f(n)=n/2f(n) = n/2) e il suo lavoro per istanza è 128n128n.

H(i, n0)                                      {n0 potenza di due, costante}
n <- size(i)
[B]: if n <= n0 then return A2(i)             // costo 2 n^3
[D]: <i_1, ..., i_4> <- A_D(i)                // Divide di A1: quattro sottoistanze di taglia n/2
[R]: for j <- 1 to 4 do s_j <- H(i_j, n0)
[C]: return A_C(s_1, ..., s_4)                // Conquer di A1

(Il codice è quello di A1A_1 con le chiamate ricorsive a HH, passando sempre lo stesso n0n_0.)

(b) La ricorrenza parametrica e la sua soluzione

Per nn e n0≤nn_0 \le n potenze di due: TH(n,n0)={2n3n≤n04 TH(n/2, n0)+128 nn>n0.T_H(n, n_0) = \begin{cases} 2n^3 & n \le n_0 \\ 4\,T_H(n/2,\, n_0) + 128\,n & n > n_0. \end{cases} Formula generale con s(n)=4s(n) = 4, w(n)=128nw(n) = 128n, f(n)=n/2f(n) = n/2, costo di base T0=2n03T_0 = 2n_0^3 (le foglie hanno taglia n0n_0). Con m=log⁡2(n/n0)m = \log_2(n/n_0) si ha f∗(n,n0)=m−1f^\ast(n, n_0) = m - 1: i livelli non di base sono 0,…,m−10, \dots, m-1, le foglie sono al livello mm. Al livello ℓ\ell ci sono 4ℓ4^\ell nodi, ciascuno di lavoro 128⋅n2ℓ128\cdot\frac n{2^\ell}, quindi il livello costa 4ℓ⋅128n2ℓ=128 n 2ℓ4^\ell\cdot 128\frac{n}{2^\ell} = 128\,n\,2^\ell. Le foglie sono 4m4^m: TH(n,n0)=∑ℓ=0m−1128 n 2ℓ+4m⋅2n03=128 n (2m−1)+2(nn0)2n03=128 n(nn0−1)+2 n2 n0,\begin{aligned} T_H(n, n_0) &= \sum_{\ell=0}^{m-1} 128\,n\,2^\ell + 4^{m}\cdot 2n_0^3 = 128\,n\,(2^m - 1) + 2\left(\frac{n}{n_0}\right)^2 n_0^3 \\ &= 128\,n\left(\frac{n}{n_0} - 1\right) + 2\,n^2\,n_0 , \end{aligned} dove si sono usati ∑ℓ=0m−12ℓ=2m−1\sum_{\ell=0}^{m-1}2^\ell = 2^m - 1, 2m=n/n02^m = n/n_0 e 4m=(2m)2=(n/n0)24^m = (2^m)^2 = (n/n_0)^2. Sviluppando: TH(n,n0)=128 n2n0−128 n+2 n2 n0(n≥n0).T_H(n, n_0) = \frac{128\,n^2}{n_0} - 128\,n + 2\,n^2\,n_0 \qquad (n \ge n_0). Verifica per induzione sul numero di livelli (omessa nel testo): il caso n=n0n = n_0 dà 128n⋅0+2n3=2n3128 n\cdot 0 + 2n^3 = 2n^3 ✓; il passo 4 TH(n/2,n0)+128n=4[128n2(n2n0−1)+2n24n0]+128n=128n(nn0−2)+2n2n0+128n=128n(nn0−1)+2n2n04\,T_H(n/2, n_0) + 128n = 4\left[128\frac n2\left(\frac{n}{2n_0} - 1\right) + 2\frac{n^2}{4}n_0\right] + 128n = 128n\left(\frac n{n_0} - 2\right) + 2n^2n_0 + 128n = 128n\left(\frac{n}{n_0} - 1\right) + 2n^2n_0 ✓. Con Python, per n=2kn = 2^k (k≤14k \le 14) e ogni n0=2j≤nn_0 = 2^j \le n la ricorrenza coincide con la formula.

(c) La soglia ottima

Per nn fisso si minimizza rispetto a n0n_0 (trattato come reale) F(n0)=128 n2n0+2n2n0−128nF(n_0) = \frac{128\,n^2}{n_0} + 2n^2 n_0 - 128n: ∂TH∂n0=−128 n2n02+2n2<?0  ⟺  2n2<128 n2n02  ⟺  n02<64  ⟺  n0<8.\frac{\partial T_H}{\partial n_0} = -\frac{128\,n^2}{n_0^2} + 2n^2 \overset{?}{<} 0 \iff 2n^2 < \frac{128\,n^2}{n_0^2} \iff n_0^2 < 64 \iff n_0 < 8. Quindi THT_H decresce per n0<8n_0 < 8 e cresce per n0>8n_0 > 8 (indipendentemente da nn): il minimo è in nˉ0=8\bar n_0 = 8, che è già una potenza di due. Valore: TH(n,8)=128 n28−128 n+16 n2=32 n2−128 n(n≥8).T_H(n, 8) = \frac{128\,n^2}{8} - 128\,n + 16\,n^2 = 32\,n^2 - 128\,n \qquad (n \ge 8).

Confronto con A1A_1. A1A_1 coincide con HH per n0=1n_0 = 1 (per n=1n = 1 vale 2⋅13=2=TA1(1)2\cdot1^3 = 2 = T_{A_1}(1)): TA1(n)=TH(n,1)=128n2−128n+2n2=130 n2−128 nT_{A_1}(n) = T_H(n, 1) = 128n^2 - 128n + 2n^2 = 130\,n^2 - 128\,n. Il rapporto è TA1(n)TH(n,8)=130 n2−128 n32 n2−128 n⟶13032≈4,06,\frac{T_{A_1}(n)}{T_H(n, 8)} = \frac{130\,n^2 - 128\,n}{32\,n^2 - 128\,n} \longrightarrow \frac{130}{32} \approx 4{,}06, quindi per n≥8n \ge 8 l'ibrido è più di quattro volte più veloce (per n=8n = 8: 72967296 contro 10241024, cioè 7,17{,}1 volte; il valore 1024=2⋅831024 = 2\cdot8^3 è il costo di A2A_2 stesso).

Perché proprio 88. Confronto locale tra risolvere direttamente con A2A_2 e fare un passo di ricorsione per poi usare A2A_2 sulle quattro metà: per n=8n = 8, A2A_2 costa 2⋅83=10242\cdot 8^3 = 1024 e un passo di ricorsione 4⋅2⋅43+128⋅8=512+1024=15364\cdot 2\cdot 4^3 + 128\cdot 8 = 512 + 1024 = 1536: conviene A2A_2. Per n=16n = 16, A2A_2 costa 2⋅163=81922\cdot16^3 = 8192 e un passo di ricorsione 4⋅2⋅83+128⋅16=4096+2048=61444\cdot 2\cdot 8^3 + 128\cdot 16 = 4096 + 2048 = 6144: conviene ricorrere. La soglia sta quindi tra 88 e 1616, e si ferma la ricorsione a n0=8n_0 = 8.

Osservazione. Il costo F(n0)/n2=128n0+2n0F(n_0)/n^2 = \frac{128}{n_0} + 2n_0 è simmetrico rispetto a n0↦64/n0n_0 \mapsto 64/n_0: per esempio n0=4n_0 = 4 e n0=16n_0 = 16 danno lo stesso costo, TH=40 n2−128 nT_H = 40\,n^2 - 128\,n (verificato), peggiore di 32n2−128n32n^2 - 128n.

python
from functools import lru_cache

@lru_cache(None)
def TH(n, n0):
    return 2 * n**3 if n <= n0 else 4 * TH(n // 2, n0) + 128 * n

n = 2**12
print({2**j: TH(n, 2**j) for j in range(0, 8)})
assert min(range(13), key=lambda j: TH(n, 2**j)) == 3                  # n0 = 8
assert all(TH(2**k, 8) == 32 * 4**k - 128 * 2**k for k in range(3, 20))
assert all(TH(2**k, 1) == 130 * 4**k - 128 * 2**k for k in range(0, 20))

Versione ripasso

Testo. TA1(1)=2T_{A_1}(1)=2, TA1(n)=4TA1(n/2)+128nT_{A_1}(n)=4T_{A_1}(n/2)+128n; TA2(n)=2n3T_{A_2}(n)=2n^3. (a) pseudocodice di H(i,n0)H(i,n_0) [3 punti]; (b) scrivere e risolvere TH(n,n0)T_H(n,n_0) [8]; (c) nˉ0\bar n_0 ottimo [5].

Teoria: 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 →, 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 →.

  • (a) Come A1A_1 con base if n <= n0 then return A2(i); 4 chiamate ricorsive H(ij,n0)H(i_j,n_0).
  • (b) TH(n,n0)=2n3T_H(n,n_0)=2n^3 (n≤n0n\le n_0), 4TH(n/2,n0)+128n4T_H(n/2,n_0)+128n. s=4s=4, f=n/2f=n/2, w=128nw=128n, T0=2n03T_0=2n_0^3, m=log⁡2nn0m=\log_2\frac n{n_0}. Livello ℓ\ell: 4ℓ⋅128n2ℓ=128n 2ℓ4^\ell\cdot128\frac n{2^\ell}=128n\,2^\ell; foglie 4m=(n/n0)24^m=(n/n_0)^2. TH=128n(2m−1)+2n2n02n03=128n(nn0−1)+2n2n0.T_H=128n(2^m-1)+2\frac{n^2}{n_0^2}n_0^3=128n\left(\frac n{n_0}-1\right)+2n^2n_0.
  • (c) ∂n0TH=−128n2n02+2n2<0  ⟺  n0<8\partial_{n_0}T_H=-\frac{128n^2}{n_0^2}+2n^2<0\iff n_0<8: minimo in nˉ0=8\bar n_0=8 (potenza di due). TH(n,8)=32n2−128nT_H(n,8)=32n^2-128n. A1=HA_1=H con n0=1n_0=1: 130n2−128n130n^2-128n; rapporto →13032≈4,06\to\frac{130}{32}\approx4{,}06 (per n≥8n\ge8).
  • Perché 8: n=8n=8: A2=1024<1536A_2=1024<1536 (un passo di ricorsione); n=16n=16: A2=8192>6144A_2=8192>6144. Simmetria n0↦64/n0n_0\mapsto64/n_0: n0=4,16n_0=4,16 danno 40n2−128n40n^2-128n.
  • Errori tipici: foglie 4m4^m con costo T(1)T(1) invece di 2n032n_0^3; dimenticare n0≤nn_0\le n; scrivere il numero di foglie 2m2^m invece di 4m4^m; non controllare che 88 sia potenza di due.
  • Verifiche: per induzione 4TH(n2,n0)+128n=128n(nn0−1)+2n2n04T_H(\frac n2,n_0)+128n=128n(\frac n{n_0}-1)+2n^2n_0; con Python TH(n,8)=32n2−128nT_H(n,8)=32n^2-128n e TA1=TH(n,1)=130n2−128nT_{A_1}=T_H(n,1)=130n^2-128n per n=2kn=2^k.

Teoria collegata