Salta al contenuto
Note per Studenti Formulario · Algoritmi per l'Ingegneria

FormularioAlgoritmi per l'Ingegneria: definizioni, teoremi e formule delle note, in ordine di capitolo

In questa pagina 7

1. Problemi computazionali e analisi degli algoritmi

Problemi computazionali - istanze, soluzioni, correttezza e complessità

Definizione (algoritmo). Un algoritmoProcedura finita e deterministica: sequenza di passi elementari di un modello di calcolo. è una procedura computazionale finita e deterministica, specificata come sequenza di passi elementari (istruzioni) di un dato modello computazionale, che trasforma un ingresso in una e una sola uscita. Dal punto di vista insiemistico un algoritmo è una funzione A:I→OA : \mathcal{I} \to \mathcal{O} dall'insieme degli ingressi a quello delle uscite. La particolare funzione realizzata è descritta dalla sequenza finita di istruzioni.

Definizione (problema computazionale). Un problema computazionale è una specifica astratta della relazione tra un insieme di istanze II e un insieme di soluzioni SS: Π⊆I×S,Π={(i1,s1),(i2,s2),… }.\Pi \subseteq I \times S, \qquad \Pi = \{(i_1, s_1), (i_2, s_2), \dots\}. Si scrive anche i Π si \,\Pi\, s per (i,s)∈Π(i, s) \in \Pi. La relazione deve essere:

  1. totale: ogni istanza ha almeno una soluzione, ∀i∈I, ∃s∈S:(i,s)∈Π\forall i \in I,\ \exists s \in S : (i,s) \in \Pi;
  2. non necessariamente univoca: possono esistere s1≠s2s_1 \neq s_2 con i Π s1i \,\Pi\, s_1 e i Π s2i \,\Pi\, s_2.

Definizione (algoritmo che risolve un problema). Un algoritmo AΠ:I→OA_\Pi : \mathcal{I} \to \mathcal{O} risolve Π\Pi se I=I\mathcal{I} = I, O=S\mathcal{O} = S e ∀i∈I:AΠ(i)=s ⟹ i Π s.\forall i \in I : \quad A_\Pi(i) = s \ \Longrightarrow\ i \,\Pi\, s.

Definizione (correttezza). Provare che AΠA_\Pi è corretto per Π\Pi significa dimostrare ∀i∈I:AΠ(i)=s ⟹ i Π s.\forall i \in I : A_\Pi(i) = s \ \Longrightarrow\ i \,\Pi\, s. Informalmente: la funzione di ingresso e uscita realizzata dall'algoritmo è coerente con la specifica.

Definizione (modello di costo). Un modello di costo associa a ogni istruzione del modello computazionale un costo non negativo cistr∈R+∪{0}c_{\text{istr}} \in \mathbb{R}^+ \cup \{0\}. Il costo dell'esecuzione di AΠA_\Pi sull'istanza ii è tAΠ,i=∑istruzioni eseguite da AΠ(i)cistr.t_{A_\Pi, i} = \sum_{\text{istruzioni eseguite da } A_\Pi(i)} c_{\text{istr}}.

Definizione (taglia). La taglia di un'istanza, ∣i∣∈N\lvert i\rvert \in \mathbb{N}, è una misura intera non negativa ragionevolmente rappresentativa della grandezza della codifica dell'istanza.

Definizione (tre complessità).

  • Caso peggiore: TA(n)=sup⁡i∈IntA,iT_A(n) = \sup_{i \in I_n} t_{A,i}.
  • Caso migliore: TA(n)=inf⁡i∈IntA,iT_A(n) = \inf_{i \in I_n} t_{A,i}.
  • Caso medio: TA(n)=E[tA,i]T_A(n) = \mathbb{E}[t_{A,i}], media rispetto a una distribuzione fissata su InI_n.

Esempio (ricerca lineare). Cerco xx scandendo un array di n=10n = 10 elementi distinti, costo unitario per ogni confronto A[k]=xA[k] = x:

2. Il paradigma divide & conquer

Divide and conquer - proprietà di sottostruttura e albero delle chiamate

Definizione (proprietà di sottostruttura). Per Π⊆I×S\Pi \subseteq I \times S è una terna (n0,D,C)(n_0, D, C):

  • una soglia n0∈Nn_0 \in \mathbb{N} e la descrizione esplicita delle soluzioni delle istanze di base, cioè con ∣i∣≤n0\lvert i\rvert \le n_0;
  • una funzione di Divide D:I→I⋆D : I \to I^\star, che a un'istanza ii con ∣i∣>n0\lvert i\rvert > n_0 associa una sequenza di sottoistanze ⟨i1,…,ik⟩\langle i_1, \dots, i_k\rangle;
  • una funzione di Conquer C:S⋆→SC : S^\star \to S, che da una sequenza di soluzioni ne costruisce una.

Devono valere, per ogni istanza non di base ii con D(i)=⟨i1,…,ik⟩D(i) = \langle i_1, \dots, i_k\rangle:

  1. ∣ij∣<∣i∣\lvert i_j\rvert < \lvert i\rvert per 1≤j≤k1 \le j \le k (le sottoistanze sono strettamente più piccole);
  2. se s1,…,sk∈Ss_1, \dots, s_k \in S sono tali che i1 Π s1,…,ik Π ski_1 \,\Pi\, s_1, \dots, i_k \,\Pi\, s_k, allora i Π C(⟨s1,…,sk⟩)i \,\Pi\, C(\langle s_1, \dots, s_k\rangle).

Teorema. Un albero binario pieno con nn foglie ha sempre n−1n - 1 nodi interni.

Albero della ricorrenza e formula generale per le ricorrenze

Formula generale. T(n)=∑ℓ=0f∗(n,n0)[∏j=0ℓ−1s(f(j)(n))]w(f(ℓ)(n))  +  [∏j=0f∗(n,n0)s(f(j)(n))]T0.T(n) = \sum_{\ell=0}^{f^\ast(n,n_0)} \left[\prod_{j=0}^{\ell-1} s\bigl(f^{(j)}(n)\bigr)\right] w\bigl(f^{(\ell)}(n)\bigr) \;+\; \left[\prod_{j=0}^{f^\ast(n,n_0)} s\bigl(f^{(j)}(n)\bigr)\right] T_0.

Master Theorem - enunciato, casi e applicazioni

Teorema (Master Theorem). La soluzione di (∗)(\ast) soddisfa T(n)={>Θ(nlog⁡ba)se ∃ ε>0:w(n)=O(nlog⁡ba−ε)(Caso 1)>Θ(nlog⁡balog⁡n)se w(n)=Θ(nlog⁡ba)(Caso 2)>Θ(w(n))se {∃ ε>0:w(n)=Ω(nlog⁡ba+ε)∃ c∈R, 0<c<1:∀na w(n/b)≤c w(n)(Caso 3)>T(n) = \begin{cases} > \Theta\bigl(n^{\log_b a}\bigr) & \text{se } \exists\,\varepsilon > 0 : w(n) = O\bigl(n^{\log_b a - \varepsilon}\bigr) \quad \text{(Caso 1)} \\[4pt] > \Theta\bigl(n^{\log_b a}\log n\bigr) & \text{se } w(n) = \Theta\bigl(n^{\log_b a}\bigr) \quad \text{(Caso 2)} \\[4pt] > \Theta\bigl(w(n)\bigr) & \text{se } \begin{cases} \exists\,\varepsilon > 0 : w(n) = \Omega\bigl(n^{\log_b a + \varepsilon}\bigr) \\ \exists\,c \in \mathbb{R},\ 0 < c < 1 : \forall n \quad a\,w(n/b) \le c\,w(n) \end{cases} \quad \text{(Caso 3)} > \end{cases}

Grafico interattivo: Soglie a confronto (b = 2): n^(log2 a) per a = 2 (n), a = 3 (n^1,58), a = 4 (n^2); e n log2 n

3. Moltiplicazione di matrici

Prodotto di matrici - algoritmo iterativo e ricorsivo a blocchi

Teorema (proprietà frattale del prodotto). I quadranti di C=A×BC = A \times B si ottengono con la stessa formula del prodotto righe per colonne di matrici 2×22 \times 2, dove al posto degli scalari ci sono i quadranti: per 1≤i,j≤21 \le i, j \le 2

Cij=∑k=12AikBkj=Ai1B1j+Ai2B2j.C_{ij} = \sum_{k=1}^{2} A_{ik} B_{kj} = A_{i1} B_{1j} + A_{i2} B_{2j}.

Proposizione. T(n)=2n3−n2T(n) = 2n^3 - n^2 per ogni n=2kn = 2^k: RMUL esegue esattamente le stesse operazioni di MUL, solo in ordine diverso.

Esempio (n=4n = 4). Il livello 00 ha una chiamata con 44 somme 2×22 \times 2 (cioè 4⋅4=164 \cdot 4 = 16 operazioni); il livello 11 ha 88 chiamate di taglia 22, ognuna con 44 somme di matrici 1×11 \times 1 (44 operazioni), cioè 3232; il livello 22 ha 6464 foglie, 6464 prodotti. Totale 16+32+64=112=2⋅43−4216 + 32 + 64 = 112 = 2 \cdot 4^3 - 4^2.

Algoritmo di Strassen e 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.

Matrici circolanti e supercircolanti - algoritmi divide and conquer

Definizione. Una matrice AA di ordine n=2kn = 2^k è supercircolante (SC) se: n=1n = 1 (ogni scalare è SC di ordine 11); oppure n>1n > 1 e

A=(A1A2A2A1)A = \begin{pmatrix} A_1 & A_2 \\ A_2 & A_1 \end{pmatrix}

con A1,A2A_1, A_2 matrici SC di ordine n2\frac n2 (quadranti alternati).

Esempio. Con quattro scalari a,b,c,da, b, c, d: (abba)\begin{pmatrix} a & b \\ b & a \end{pmatrix} e (cddc)\begin{pmatrix} c & d \\ d & c \end{pmatrix} sono SC di ordine 22; componendole come A1,A2A_1, A_2 si ottiene la SC di ordine 44

Teorema. Per ogni SC AA di ordine nn e ogni v∈B(n)v \in \mathcal B(n) vale Av=αvAv = \alpha v con α=r(A)⋅v\alpha = r(A) \cdot v (prodotto scalare della prima riga con vv).

Teorema. Se A,BA, B sono SC e c(A),c(B),c(C)c(A), c(B), c(C) sono le prime righe viste come vettori colonna (c(A)=r(A)Tc(A) = r(A)^T), il prodotto C=ABC = AB soddisfa

c(C)=H(n)−1[(H(n) c(A))⊗(H(n) c(B))],c(C) = H(n)^{-1} \bigl[ (H(n)\,c(A)) \otimes (H(n)\,c(B)) \bigr],

dove ⊗\otimes è il prodotto componente per componente.

Definizione. Una matrice n×nn \times n è circolante (CR) se ogni riga è la rotazione di una posizione verso destra della precedente, cioè A(h+ℓ) mod n, (k+ℓ) mod n=Ah,kA_{(h+\ell) \bmod n,\,(k+\ell) \bmod n} = A_{h,k} per ogni h,k,ℓh, k, \ell.

Teorema (convoluzione ciclica, forma matriciale). Per due CR A,BA, B di ordine nn:

r(C)T=(F(n))−1[(F(n)r(A)T)⊗(F(n)r(B)T)].r(C)^T = \bigl(F^{(n)}\bigr)^{-1} \bigl[ \bigl(F^{(n)} r(A)^T\bigr) \otimes \bigl(F^{(n)} r(B)^T\bigr) \bigr].

Esponenziazione veloce per quadrature successive

Definizione (potenza). Per x∈Dx \in D e n∈Nn \in \mathbb N:

xn={en=0,x∘xn−1=xn−1∘xn>0.x^n = \begin{cases} e & n = 0, \\ x \circ x^{n-1} = x^{n-1} \circ x & n > 0. \end{cases}

Per n≥1n \ge 1, xn=x∘x∘⋯∘xx^n = x \circ x \circ \dots \circ x (nn fattori), senza parentesi per l'associatività.

Proprietà. Sia k=⌊n2⌋k = \lfloor \frac n2 \rfloor.

  • Se n=2kn = 2k è pari: xn=xk∘xkx^n = x^k \circ x^k.
  • Se n=2k+1n = 2k + 1 è dispari: xn=xk∘xk∘xx^n = x^k \circ x^k \circ x.

4. Polinomi e FFT

Polinomi - rappresentazione per coefficienti e per valori e convoluzione

Definizione (polinomio di grado limitato da nn). Il polinomio p(x)=∑i=0n−1aixi=a0+a1x+⋯+an−1xn−1p(x)=\sum_{i=0}^{n-1}a_i x^i=a_0+a_1x+\dots+a_{n-1}x^{n-1} è rappresentato con nn coefficienti dal vettore a=(a0,…,an−1)∈Cn\mathbf a=(a_0,\dots,a_{n-1})\in\mathbb C^n e si scrive p(x)≡ap(x)\equiv\mathbf a. Il numero nn è il limite di grado (degree bound); il grado effettivo è max⁡{i:ai≠0}≤n−1\max\{i:a_i\neq 0\}\le n-1.

Teorema (interpolazione). Date nn coppie (xi,yi)∈C×C(x_i,y_i)\in\mathbb C\times\mathbb C con xi≠xjx_i\ne x_j per i≠ji\ne j, esiste uno e un solo polinomio pp di grado limitato da nn tale che p(xi)=yip(x_i)=y_i per 0≤i≤n−10\le i\le n-1.

Definizione (rappresentazione per valori). Dato x∈Cn\mathbf x\in\mathbb C^n a componenti distinte (la base), il polinomio pp di grado limitato da nn è rappresentato dalla coppia (x,y)(\mathbf x,\mathbf y) con yi=p(xi)y_i=p(x_i). Basi diverse danno rappresentazioni diverse. L'unicità è garantita dal teorema.

Esempio. p(x)=x2p(x)=x^2 nella base x=(0,1,2)\mathbf x=(0,1,2) è y=(0,1,4)\mathbf y=(0,1,4). Anche questa rappresentazione si può estendere: aggiungendo il punto x3=3x_3=3 si aggiunge y3=9y_3=9, ma il padding è più elaborato di quello per coefficienti (bisogna calcolare un nuovo valore).

Definizione (convoluzione lineare). Per a,b∈Cn\mathbf a,\mathbf b\in\mathbb C^n, il vettore c=a⋆b∈C2n−1\mathbf c=\mathbf a\star\mathbf b\in\mathbb C^{2n-1} ha componenti cj=∑k=max⁡{0, j−n+1}min⁡{j, n−1}ak bj−k,0≤j≤2n−2.c_j=\sum_{k=\max\{0,\,j-n+1\}}^{\min\{j,\,n-1\}}a_k\,b_{j-k},\qquad 0\le j\le 2n-2. Se p≡ap\equiv\mathbf a e q≡bq\equiv\mathbf b allora p(x) q(x)≡a⋆bp(x)\,q(x)\equiv\mathbf a\star\mathbf b. Con vettori di lunghezza mm e nn il risultato ha lunghezza m+n−1m+n-1.

Esempio. a=(1,2,3)\mathbf a=(1,2,3), b=(4,5,6)\mathbf b=(4,5,6) (n=3n=3, c∈C5\mathbf c\in\mathbb C^5). Si ordinano i prodotti akbha_kb_h per k+hk+h:

Teorema (costo della convoluzione per definizione). Calcolare a⋆b\mathbf a\star\mathbf b con a,b∈Cn\mathbf a,\mathbf b\in\mathbb C^n richiede n2n^2 prodotti e (n−1)2(n-1)^2 somme, in tutto 2n2−2n+1=Θ(n2)2n^2-2n+1=\Theta(n^2) operazioni.

DFT, matrice di Fourier e radici n-esime dell'unità

Definizione (radice principale). La radice nn-esima principale dell'unità è ωn=e2πi/n\omega_n=e^{2\pi i/n}. Le radici nn-esime dell'unità sono le sue prime nn potenze, ωn0,ωn1,…,ωnn−1\omega_n^0,\omega_n^1,\dots,\omega_n^{n-1}, e formano il vettore Ωn=(ωn0,…,ωnn−1)\Omega_n=(\omega_n^0,\dots,\omega_n^{n-1}). Poiché ωnn=1\omega_n^n=1, vale ωnk=ωn k mod n\omega_n^k=\omega_n^{\,k\bmod n} per ogni k∈Zk\in\mathbb Z (aritmetica modulare sugli esponenti; ωn−1=ωnn−1\omega_n^{-1}=\omega_n^{n-1}).

Esempio (n=8n=8). ω8=eiπ/4=1+i2\omega_8=e^{i\pi/4}=\frac{1+i}{\sqrt2} e le potenze sono i vertici di un ottagono regolare inscritto nella circonferenza di raggio 11:

Grafico interattivo: Le otto radici ottave dell'unità: sono i vertici di un ottagono regolare sulla circonferenza di raggio 1, ω⁰ = 1 e ω⁴ = −1; ω^k forma l'angolo 2πk/8 con l'asse reale

Definizione (DFT). La trasformata discreta di Fourier di ordine nn è la funzione DFTn:Cn→Cn\mathrm{DFT}_n:\mathbb C^n\to\mathbb C^n che a a\mathbf a (coefficienti di pp) associa le valutazioni di pp sulle radici nn-esime dell'unità: yi=p(ωni)=∑j=0n−1aj ωnij,0≤i≤n−1.y_i=p(\omega_n^i)=\sum_{j=0}^{n-1}a_j\,\omega_n^{ij},\qquad 0\le i\le n-1. La coppia (Ωn,y)(\Omega_n,\mathbf y) è una rappresentazione per valori di pp (le radici sono distinte, quindi vale il teorema di interpolazione).

Esempio (n=4n=4). Con ω4=i\omega_4=i e ω4ij=i ij mod 4\omega_4^{ij}=i^{\,ij\bmod4}: F4=(11111i−1−i1−11−11−i−1i).F_4=\begin{pmatrix}1&1&1&1\\1&i&-1&-i\\1&-1&1&-1\\1&-i&-1&i\end{pmatrix}. Per p(x)=3+4x+5x2+6x3≡(3,4,5,6)p(x)=3+4x+5x^2+6x^3\equiv(3,4,5,6) si ha y0=3+4+5+6=18y_0=3+4+5+6=18, y1=3+4i−5−6i=−2−2iy_1=3+4i-5-6i=-2-2i, y2=3−4+5−6=−2y_2=3-4+5-6=-2, y3=3−4i−5+6i=−2+2iy_3=3-4i-5+6i=-2+2i. Quindi DFT4(3,4,5,6)=(18, −2−2i, −2, −2+2i)\mathrm{DFT}_4(3,4,5,6)=(18,\,-2-2i,\,-2,\,-2+2i) (controllato con numpy, dopo aver cambiato il segno dell'esponente).

Lemma di cancellazione. Per n,dn,d interi positivi e k∈Zk\in\mathbb Z: ωdndk=ωnk\omega_{dn}^{dk}=\omega_n^k.

Lemma di dimezzamento. Per n>0n>0 pari, i quadrati (ωni)2(\omega_n^i)^2, 0≤i≤n−10\le i\le n-1, coincidono a coppie: per 0≤i≤n/2−10\le i\le n/2-1 vale (ωni)2=(ωn i+n/2)2=ωn/2 i.(\omega_n^i)^2=(\omega_n^{\,i+n/2})^2=\omega_{n/2}^{\,i}. Elevando al quadrato le nn radici nn-esime si ottengono quindi le n/2n/2 radici n/2n/2-esime, ciascuna due volte.

Esempio (n=8n=8). Quadrando ω80,…,ω87\omega_8^0,\dots,\omega_8^7 si ottiene 1, i, −1, −i, 1, i, −1, −i1,\ i,\ -1,\ -i,\ 1,\ i,\ -1,\ -i: le quattro radici quarte dell'unità, ciascuna due volte.

Lemma di somma. Per n>1n>1 e k≥0k\ge0 interi: ∑j=0n−1(ωnk)j={nse k eˋ multiplo di n,0altrimenti.\sum_{j=0}^{n-1}\bigl(\omega_n^k\bigr)^j=\begin{cases}n&\text{se }k\text{ è multiplo di }n,\\0&\text{altrimenti.}\end{cases}

Esempio. n=4n=4, k=1k=1: 1+i+i2+i3=1+i−1−i=01+i+i^2+i^3=1+i-1-i=0. k=4k=4: 1+1+1+1=41+1+1+1=4. k=2k=2: 1+(−1)+1+(−1)=01+(-1)+1+(-1)=0.

Teorema (convoluzione lineare). Siano a,b∈Cn\mathbf a,\mathbf b\in\mathbb C^n e c=a⋆b∈C2n−1\mathbf c=\mathbf a\star\mathbf b\in\mathbb C^{2n-1}. Allora (c ∣ 0)=DFT2n−1(DFT2n((a∣0n))⊙DFT2n((b∣0n))),(\mathbf c\,|\,0)=\mathrm{DFT}^{-1}_{2n}\Bigl(\mathrm{DFT}_{2n}\bigl((\mathbf a|\mathbf 0_n)\bigr)\odot\mathrm{DFT}_{2n}\bigl((\mathbf b|\mathbf 0_n)\bigr)\Bigr), dove ∣| indica la concatenazione (padding con zeri), ⊙\odot il prodotto componente per componente.

Esempio (n=2n=2). a=(1,2)\mathbf a=(1,2), b=(3,4)\mathbf b=(3,4), ordine 44. Con F4F_4:

Fast Fourier Transform - algoritmo ricorsivo e trasformata inversa

Teorema (sottostruttura). Siano y=DFTn(a)\mathbf y=\mathrm{DFT}_n(\mathbf a), y[0]=DFTn/2(a[0])\mathbf y^{[0]}=\mathrm{DFT}_{n/2}(\mathbf a^{[0]}), y[1]=DFTn/2(a[1])\mathbf y^{[1]}=\mathrm{DFT}_{n/2}(\mathbf a^{[1]}). Per 0≤i≤n/2−10\le i\le n/2-1: yi=yi[0]+ωni yi[1],yi+n/2=yi[0]−ωni yi[1].y_i=y^{[0]}_i+\omega_n^i\,y^{[1]}_i,\qquad y_{i+n/2}=y^{[0]}_i-\omega_n^i\,y^{[1]}_i.

Teorema (matrice inversa di Fourier). [Fn−1]ij=1n ωn−ij\bigl[F_n^{-1}\bigr]_{ij}=\dfrac1n\,\omega_n^{-ij} per 0≤i,j≤n−10\le i,j\le n-1.

Esempio. y=(18,−2−2i,−2,−2+2i)\mathbf y=(18,-2-2i,-2,-2+2i) (dall'esempio di prima). FFT(y)=(12,24,20,16)\mathrm{FFT}(\mathbf y)=(12,24,20,16): per esempio z1=18+i(−2−2i)+(−1)(−2)+(−i)(−2+2i)=18+(2−2i)+2+(2+2i)=24z_1=18+i(-2-2i)+(-1)(-2)+(-i)(-2+2i)=18+(2-2i)+2+(2+2i)=24. Rovesciando: (12,16,20,24)(12,16,20,24); dividendo per 44: (3,4,5,6)(3,4,5,6), i coefficienti originali.

Trasformate notevoli ed esponenziazione di polinomi con la FFT

Definizione. Siano nn e kk potenze di due, 1≤k≤n1\le k\le n. Un vettore x∈Cn\mathbf x\in\mathbb C^n è (n,k)(n,k)-sparso se xi=0x_i=0 per ogni indice ii con i mod k≠0i\bmod k\ne0.

Esempio (n=8n=8, k=4k=4). x=(2,0,0,0,1,0,0,0)\mathbf x=(2,0,0,0,1,0,0,0). Le componenti non nulle (2,1)(2,1) hanno DFT2(2,1)=(2+1, 2−1)=(3,1)\mathrm{DFT}_2(2,1)=(2+1,\,2-1)=(3,1), ripetuta 44 volte: DFT8(x)=(3,1,3,1,3,1,3,1)\mathrm{DFT}_8(\mathbf x)=(3,1,3,1,3,1,3,1).

5. Programmazione dinamica

Programmazione dinamica - Fibonacci, memoizzazione e schema del paradigma

Definizione. Si pone F0=F1=1F_0 = F_1 = 1 e Fn=Fn−1+Fn−2F_n = F_{n-1} + F_{n-2} per n>1n > 1. I primi valori sono 1,1,2,3,5,8,13,21,…1, 1, 2, 3, 5, 8, 13, 21, \dots

Definizione (sottostruttura ottima). Un problema di ottimizzazione ha la proprietà di sottostruttura ottima se una soluzione ottima di un'istanza si ottiene da soluzioni ottime di istanze più piccole.

Longest Common Subsequence - ricorrenza, tabella e ricostruzione

Definizione (LCS). Date X=⟨x1,…,xm⟩X = \langle x_1, \dots, x_m \rangle e Y=⟨y1,…,yn⟩Y = \langle y_1, \dots, y_n \rangle, determinare una stringa Z=⟨z1,…,zk⟩Z = \langle z_1, \dots, z_k \rangle di lunghezza massima che sia sottosequenza sia di XX sia di YY.

Esempio. X=⟨A,B,C,B,B,D⟩X = \langle A, B, C, B, B, D \rangle, Y=⟨A,D,C,C,B,D⟩Y = \langle A, D, C, C, B, D \rangle: la LCS è Z=⟨A,C,B,D⟩Z = \langle A, C, B, D \rangle, lunga 44, realizzata in XX da ⟨1,3,4,6⟩\langle 1, 3, 4, 6 \rangle e in YY da ⟨1,4,5,6⟩\langle 1, 4, 5, 6 \rangle.

Teorema (sottostruttura della LCS). Siano XiX_i, YjY_j prefissi di XX e YY e sia Z=⟨z1,…,zk⟩Z = \langle z_1, \dots, z_k \rangle una LCS di XiX_i e YjY_j.

  1. Se i=0i = 0 o j=0j = 0, allora Z=εZ = \varepsilon.
  2. Se i>0i > 0 e j>0j > 0:
    • (a) se xi=yjx_i = y_j, allora zk=xi=yjz_k = x_i = y_j e Zk−1=⟨z1,…,zk−1⟩Z_{k-1} = \langle z_1, \dots, z_{k-1} \rangle è una LCS di Xi−1X_{i-1} e Yj−1Y_{j-1};
    • (b) se xi≠yjx_i \ne y_j, allora ZZ è una LCS di Xi−1,YjX_{i-1}, Y_j oppure di Xi,Yj−1X_i, Y_{j-1}, e quindi è la più lunga tra le due.

Matrix Chain Multiplication - parentesizzazione ottima

Definizione (MCM). Dato p∈(N+)n+1p \in (\mathbb{N}^+)^{n+1}, determinare la parentesizzazione della catena associata che richiede il minimo numero di prodotti tra scalari.

Teorema. P(n)≥2n−1nP(n) \ge \dfrac{2^{n-1}}{n} per ogni n≥1n \ge 1.

Grafico interattivo: Parentesizzazioni di una catena di n matrici: P(n) esatto (numeri di Catalan, 14 per n = 5, 4862 per n = 10) e approssimazione 4^(n-1) / (√π (n-1)^(3/2)), contro il limite inferiore 2^(n-1) / n

Teorema (sottostruttura ottima). Sia i<ji < j e sia Ti..j∗T^*_{i..j} un albero ottimo per ⟨Ai,…,Aj⟩\langle A_i, \dots, A_j \rangle. Allora esiste k∗k^* con i≤k∗<ji \le k^* < j tale che la radice ha come sottoalberi due alberi ottimi Ti..k∗∗T^*_{i..k^*} e Tk∗+1..j∗T^*_{k^*+1..j}, e c(Ti..j∗)=c(Ti..k∗∗)+c(Tk∗+1..j∗)+pi−1 pk∗ pj,c(T^*_{i..j}) = c(T^*_{i..k^*}) + c(T^*_{k^*+1..j}) + p_{i-1}\, p_{k^*}\, p_j, dove k∗k^* è un indice che minimizza c(Ti..k∗)+c(Tk+1..j∗)+pi−1pkpjc(T^*_{i..k}) + c(T^*_{k+1..j}) + p_{i-1} p_k p_j su i≤k<ji \le k < j.

6. Programmazione dinamica su stringhe

Allineamento di stringhe e sottosequenza palindroma più lunga

Definizione (allineamento). Date X=⟨x1,…,xm⟩X = \langle x_1, \dots, x_m \rangle e Y=⟨y1,…,yn⟩Y = \langle y_1, \dots, y_n \rangle su ΣDNA\Sigma_{DNA}, un allineamento di lunghezza kk è una coppia di stringhe ZX=⟨z1X,…,zkX⟩Z^X = \langle z^X_1, \dots, z^X_k \rangle, ZY=⟨z1Y,…,zkY⟩Z^Y = \langle z^Y_1, \dots, z^Y_k \rangle su ΣDNAE\Sigma^E_{DNA}, con k≥max⁡{m,n}k \ge \max\{m, n\}, tali che:

  1. XX è sottosequenza di ZXZ^X e YY è sottosequenza di ZYZ^Y (esistono 1≤i1<⋯<im≤k1 \le i_1 < \dots < i_m \le k con X=⟨zi1X,…,zimX⟩X = \langle z^X_{i_1}, \dots, z^X_{i_m} \rangle, e analogamente per YY);
  2. le posizioni che non realizzano caratteri di XX (rispettivamente YY) contengono solo −-;
  3. in ogni posizione rr non ci sono due −-: (zrX∈ΣDNA)∨(zrY∈ΣDNA)(z^X_r \in \Sigma_{DNA}) \lor (z^Y_r \in \Sigma_{DNA}).

Esempio. X=⟨A,G,T,T⟩X = \langle A, G, T, T \rangle, Y=⟨A,T,T⟩Y = \langle A, T, T \rangle. Due allineamenti:

Definizione. Una stringa Z=⟨z1,…,zm⟩Z = \langle z_1, \dots, z_m \rangle è palindroma se z1+h=zm−hz_{1+h} = z_{m-h} per 0≤h≤m−10 \le h \le m - 1, cioè ZZ è uguale alla stringa letta al contrario. La stringa vuota è palindroma (m=0m = 0). Esistono palindrome di lunghezza pari (⟨A,C,C,A⟩\langle A, C, C, A \rangle) e dispari (⟨A,I,A⟩\langle A, I, A \rangle).

Memoizzazione di ricorrenze triangolari - conteggio esatto e diagonali costanti

Proposizione. Per ogni 1≤ℓ≤n1 \le \ell \le n, tutti gli elementi M(i,j)M(i, j) con j−i+1=ℓj - i + 1 = \ell valgono uno stesso numero V(ℓ)V(\ell), dove V(ℓ)={1ℓ=12ℓ=2V(ℓ−1)2⋅V(ℓ−2)ℓ≥3V(\ell) = \begin{cases} 1 & \ell = 1 \\ 2 & \ell = 2 \\ V(\ell - 1)^2 \cdot V(\ell - 2) & \ell \ge 3 \end{cases}

7. Il paradigma greedy

Paradigma greedy - scelta golosa, sottostruttura e selezione delle attività

Proprietà di scelta greedy. Esiste sempre una soluzione ottima che contiene la scelta greedy. Garantisce che la scelta incauta non compromette l'ottimalità.

Proprietà di sottostruttura ottima. Esiste una soluzione ottima che, oltre alla scelta greedy, contiene una soluzione ottima della sottoistanza residua.

Teorema (schema di correttezza). Se un algoritmo greedy compie ogni volta la scelta greedy, fa il clean-up e risolve ricorsivamente la residua, e valgono le due proprietà, allora ritorna una soluzione ottima.

Dimostrazione. Per induzione sulla taglia dell'istanza. Base: istanza vuota, la soluzione vuota è ottima. Passo: si assume l'algoritmo corretto sulle istanze più piccole, in particolare sulla residua, che è strettamente più piccola. Per le due proprietà esiste un ottimo A⋆A^\star composto dalla scelta greedy e da una soluzione ottima della residua. L'algoritmo ritorna la scelta greedy più la soluzione che l'ipotesi induttiva garantisce ottima per la residua: ha lo stesso costo di A⋆A^\star, quindi è ottima. □\square

Definizione. Due attività aia_i e aja_j sono compatibili se gli intervalli sono disgiunti, [si,fi)∩[sj,fj)=∅[s_i, f_i) \cap [s_j, f_j) = \emptyset, cioè se sj≥fioppuresi≥fj.s_j \ge f_i \quad\text{oppure}\quad s_i \ge f_j . Un sottoinsieme è mutuamente compatibile se lo sono tutte le coppie. Il problema (activity selection) chiede un sottoinsieme mutuamente compatibile di cardinalità massima.

Esempio. s=(2,1,2,3,5,7)s = (2,1,2,3,5,7), f=(3,5,6,6,7,8)f = (3,5,6,6,7,8), cioè a1=[2,3)a_1 = [2,3), a2=[1,5)a_2 = [1,5), a3=[2,6)a_3 = [2,6), a4=[3,6)a_4 = [3,6), a5=[5,7)a_5 = [5,7), a6=[7,8)a_6 = [7,8). In rosso le attività scelte dall'algoritmo che segue (si trovano più sotto).

Grafico interattivo: Le sei attività: in rosso quelle scelte dal greedy del primo che finisce, a1, a4 e a6

Codifica di Huffman - codici prefissi, algoritmo e ottimalità

Definizione (codice prefisso). Una codifica è libera da prefissi (prefix-code) se nessuna parola è prefisso di un'altra.

Teorema. Un codice prefisso è invertibile sulle stringhe, e si decodifica con una sola scansione leggendo i bit fino a incontrare una parola.

Dimostrazione. Si legge dall'inizio il minimo numero di bit che forma una parola: questa è l'unica parola con tale inizio, perché una seconda parola, essendo anch'essa un inizio della stessa sequenza, sarebbe prefisso della prima o viceversa. Il primo carattere è dunque determinato; si ripete dal bit successivo. □\square

Definizione (costo di un trie). B(T)=∑c∈CdT(c) f(c)B(T) = \displaystyle\sum_{c \in C} d_T(c)\, f(c) (lunghezza media in bit per carattere).

Lemma di scambio. Siano a,ba, b due foglie di un trie TT con f(a)≤f(b)f(a) \le f(b) e dT(a)≤dT(b)d_T(a) \le d_T(b) e sia T′T' il trie ottenuto scambiando i caratteri aa e bb. Allora B(T′)≤B(T)B(T') \le B(T).

Dimostrazione. Dopo lo scambio aa sta alla profondità di bb e viceversa, e gli altri caratteri non si muovono: B(T′)−B(T)=f(a)dT(b)+f(b)dT(a)−f(a)dT(a)−f(b)dT(b)=(f(a)−f(b))(dT(b)−dT(a))≤0,B(T') - B(T) = f(a) d_T(b) + f(b) d_T(a) - f(a) d_T(a) - f(b) d_T(b) = \big(f(a) - f(b)\big)\big(d_T(b) - d_T(a)\big) \le 0, perché i due fattori sono entrambi ≤0\le 0. □\square