Salta al contenuto
Note per Studenti Esercizio - convoluzione con il vettore delle potenze di due in tempo lineare (compito del 5 settembre 2025, es. 2)

Esercizio - convoluzione con il vettore delle potenze di due in tempo lineare (compito del 5 settembre 2025, es. 2)

Esame
In questa pagina 4

Testo (compito d'esame del 5 settembre 2025 di Algoritmi per l'Ingegneria, Ing. Informatica UniPD, seconda parte, esercizio 2, 17 punti). Sia n≥1n\ge1. Dato un arbitrario vettore x=(x0,x1,…,xn−1)∈Cn\mathbf x=(x_0,x_1,\dots,x_{n-1})\in\mathbb C^n, si consideri l'operazione di convoluzione lineare c=(c0,c1,…,c2n−2)=x⋆pn\mathbf c=(c_0,c_1,\dots,c_{2n-2})=\mathbf x\star\mathbf p_n, con pn=(1,2,…,2i,…,2n−2,2n−1)\mathbf p_n=(1,2,\dots,2^i,\dots,2^{n-2},2^{n-1}) il vettore che ha 2i2^i come componente ii-esima, per 0≤i≤n−10\le i\le n-1.

  1. (a) [3 punti] Per n=4n=4, si esprimano le 77 componenti di c=x⋆p4\mathbf c=\mathbf x\star\mathbf p_4 in funzione delle componenti di x=(x0,x1,x2,x3)\mathbf x=(x_0,x_1,x_2,x_3).
  2. (b) [4 punti] Per n≥1n\ge1, si specializzi la sommatoria associata alla generica componente della convoluzione lineare, ottenendo due sommatorie associate ai due sottoinsiemi di componenti {ci:0≤i≤n−1}\{c_i:0\le i\le n-1\} e {ci:n≤i≤2n−2}\{c_i:n\le i\le2n-2\}.
  3. (c) [6 punti] Con riferimento ai due sottoinsiemi di componenti specificati nel punto (b), si determinino due ricorrenze, una per sottoinsieme: la prima ricorrenza con caso di base c0c_0 e che esprima cic_i in funzione di ci−1c_{i-1}, per 0<i≤n−10<i\le n-1; la seconda ricorrenza con caso di base c2n−2c_{2n-2} e che esprima cic_i in funzione di ci+1c_{i+1}, per 2n−2>i≥n2n-2>i\ge n.
  4. (d) [4 punti] Sulla base delle ricorrenze sviluppate al punto (c) si sviluppi un algoritmo iterativo POWER_CONV(x) che restituisca c\mathbf c eseguendo O(n)O(n) operazioni aritmetiche tra scalari complessi.

Teoria usata: Polinomi - rappresentazione per coefficienti e per valori e convoluzioneun polinomio di grado limitato da n si rappresenta con n coefficienti o con n valori su n punti distinti (teorema di interpolazione, matrice di Vandermonde). Per coefficienti la somma costa n e il prodotto, la convoluzione lineare, n^2 prodotti e (n-1)^2 somme; per valori il prodotto costa 2n-1 prodotti ma serve una base estesa di almeno 2n-1 punti. Le conversioni generali costano troppo: valutazione n volte con Horner 2n(n-1), interpolazione con Gauss-Jordan n^3 o con Lagrange n^2. Serve una base speciale: la DFT.Polinomi - rappresentazione per coefficienti e per valori e convoluzione → (definizione di convoluzione lineare, regola di Horner), 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 → (invarianti dei cicli), Esponenziazione veloce per quadrature successivepotenza x^n con un operatore associativo con elemento neutro, costo = numero di applicazioni dell'operatore; STUPID_POWER costa n-1, la sottostruttura x^n = (x^floor(n/2))^2 (per n dispari per x) dà POWER con T(n) = floor(log2 n) + (numero di bit a 1 di n) - 1, Theta(log n) (caso peggiore 2 floor(log2 n), n = 2^t - 1); versioni iterative POWER2 e POWER3 dalla rappresentazione binaria di n; esempio con la matrice di Fibonacci; il modello a costo unitario vale solo se l'operatore non fa crescere gli operandi.Esponenziazione veloce per quadrature successive → (potenza di 22 in tempo logaritmico).

La convoluzione per definizione costa Θ(n2)\Theta(n^2) operazioni (nota sui polinomi) e la FFT darebbe Θ(nlog⁡n)\Theta(n\log n) (Fast Fourier Transform - algoritmo ricorsivo e trasformata inversala FFT calcola DFT_n (n potenza di due) con divide et impera: si separano i coefficienti di indice pari e dispari, p(x) = p0(x^2) + x p1(x^2), e per il lemma di dimezzamento bastano due DFT di ordine n/2; y_i = y0_i + w^i y1_i e y_{i+n/2} = y0_i - w^i y1_i. La ricorrenza T(n) = 2T(n/2) + 2n con T(1) = 0 ha soluzione esatta 2 n log2 n. L'inversa ha matrice F^{-1}_{ij} = w^{-ij}/n: si ottiene con la FFT sulle potenze negative (poi si divide per n fuori dalla ricorsione) oppure con DFT_n(y) rovesciato. LIN_CONV moltiplica due polinomi con n coefficienti in 12 n log2(2n) + 4n operazioni.Fast Fourier Transform - algoritmo ricorsivo e trasformata inversa →). Qui il secondo vettore ha una struttura (progressione geometrica di ragione 22) che permette di scendere a Θ(n)\Theta(n) con due semplici ricorrenze, senza trasformate.

(a) Il caso n=4n=4

La componente cjc_j somma i prodotti xk⋅2hx_k\cdot2^{h} con k+h=jk+h=j, 0≤k,h≤30\le k,h\le3 (convoluzione lineare, vedi la definizione nella nota sui polinomi). Per j=0,…,3j=0,\dots,3 si possono usare tutti gli indici k=0,…,jk=0,\dots,j: c0=x0⋅20=x0,c1=x0⋅21+x1⋅20=2x0+x1,c2=x0⋅22+x1⋅21+x2⋅20=4x0+2x1+x2,c3=x0⋅23+x1⋅22+x2⋅21+x3⋅20=8x0+4x1+2x2+x3.\begin{aligned}c_0&=x_0\cdot2^0=x_0,\\c_1&=x_0\cdot2^1+x_1\cdot2^0=2x_0+x_1,\\c_2&=x_0\cdot2^2+x_1\cdot2^1+x_2\cdot2^0=4x_0+2x_1+x_2,\\c_3&=x_0\cdot2^3+x_1\cdot2^2+x_2\cdot2^1+x_3\cdot2^0=8x_0+4x_1+2x_2+x_3.\end{aligned} Per j=4,5,6j=4,5,6 l'esponente h=j−kh=j-k non può superare 33, quindi k≥j−3k\ge j-3: si parte da k=j−3k=j-3 e si arriva a k=3k=3: c4=x1⋅23+x2⋅22+x3⋅21=8x1+4x2+2x3,c5=x2⋅23+x3⋅22=8x2+4x3,c6=x3⋅23=8x3.\begin{aligned}c_4&=x_1\cdot2^3+x_2\cdot2^2+x_3\cdot2^1=8x_1+4x_2+2x_3,\\c_5&=x_2\cdot2^3+x_3\cdot2^2=8x_2+4x_3,\\c_6&=x_3\cdot2^3=8x_3.\end{aligned} Controllo con x=(1,0,0,0)\mathbf x=(1,0,0,0): c=(1,2,4,8,0,0,0)=p4\mathbf c=(1,2,4,8,0,0,0)=\mathbf p_4 completato con zeri, come deve essere perché x\mathbf x è l'elemento neutro della convoluzione.

(b) Le due sommatorie

La definizione di convoluzione lineare per vettori di lunghezza nn è ci=∑j=max⁡{0, i−n+1}min⁡{i, n−1}xj pi−j,pm=2m,c_i=\sum_{j=\max\{0,\,i-n+1\}}^{\min\{i,\,n-1\}}x_j\,p_{i-j},\qquad p_m=2^m, cioè ci=∑j=max⁡{0,i−n+1}min⁡{i,n−1}xj 2 i−jc_i=\sum_{j=\max\{0,i-n+1\}}^{\min\{i,n-1\}}x_j\,2^{\,i-j}, per 0≤i≤2n−20\le i\le2n-2. Si distinguono due casi:

  • se 0≤i≤n−10\le i\le n-1 allora i−n+1≤0i-n+1\le0 e i≤n−1i\le n-1, quindi max⁡{0,i−n+1}=0\max\{0,i-n+1\}=0 e min⁡{i,n−1}=i\min\{i,n-1\}=i;
  • se n≤i≤2n−2n\le i\le2n-2 allora i−n+1≥1i-n+1\ge1 e i≥n−1i\ge n-1, quindi max⁡{0,i−n+1}=i−n+1\max\{0,i-n+1\}=i-n+1 e min⁡{i,n−1}=n−1\min\{i,n-1\}=n-1.

Perciò ci={∑j=0ixj 2 i−j0≤i≤n−1,∑j=i−n+1n−1xj 2 i−jn≤i≤2n−2.c_i=\begin{cases}\displaystyle\sum_{j=0}^{i}x_j\,2^{\,i-j}&0\le i\le n-1,\\[3mm]\displaystyle\sum_{j=i-n+1}^{n-1}x_j\,2^{\,i-j}&n\le i\le2n-2.\end{cases} Per n=4n=4 danno esattamente le espressioni del punto (a) (per esempio i=5i=5: jj da 22 a 33, x2⋅23+x3⋅22x_2\cdot2^3+x_3\cdot2^2).

(c) Le due ricorrenze

Prima metà, 0≤i≤n−10\le i\le n-1. Caso base: c0=x0⋅20=x0c_0=x_0\cdot2^0=x_0. Per 0<i≤n−10<i\le n-1 si separa l'ultimo addendo (j=ij=i, con 20=12^0=1) e si raccoglie 22 dai restanti: ci=∑j=0i−1xj2 i−j+xi=2∑j=0i−1xj2 i−1−j+xi=2 ci−1+xi.\begin{aligned}c_i&=\sum_{j=0}^{i-1}x_j2^{\,i-j}+x_i=2\sum_{j=0}^{i-1}x_j2^{\,i-1-j}+x_i\\&=2\,c_{i-1}+x_i.\end{aligned} (la somma che resta è proprio ci−1c_{i-1} per la formula del punto (b), valida perché i−1≤n−1i-1\le n-1). Questa è una regola di Horner calcolata in 22 (cic_i è il polinomio con coefficienti x0,…,xix_0,\dots,x_i valutato in z=2z=2, vedi Polinomi - rappresentazione per coefficienti e per valori e convoluzioneun polinomio di grado limitato da n si rappresenta con n coefficienti o con n valori su n punti distinti (teorema di interpolazione, matrice di Vandermonde). Per coefficienti la somma costa n e il prodotto, la convoluzione lineare, n^2 prodotti e (n-1)^2 somme; per valori il prodotto costa 2n-1 prodotti ma serve una base estesa di almeno 2n-1 punti. Le conversioni generali costano troppo: valutazione n volte con Horner 2n(n-1), interpolazione con Gauss-Jordan n^3 o con Lagrange n^2. Serve una base speciale: la DFT.Polinomi - rappresentazione per coefficienti e per valori e convoluzione →).

Seconda metà, n≤i≤2n−2n\le i\le2n-2. Caso base: c2n−2=xn−1⋅2(2n−2)−(n−1)=xn−1 2n−1c_{2n-2}=x_{n-1}\cdot2^{(2n-2)-(n-1)}=x_{n-1}\,2^{n-1} (un solo addendo, j=n−1j=n-1). Per n≤i<2n−2n\le i<2n-2 si ha i+1≤2n−2i+1\le2n-2, quindi per la formula del punto (b) ci+1=∑j=i−n+2n−1xj 2 i+1−j,cioeˋ12ci+1=∑j=i−n+2n−1xj 2 i−j.c_{i+1}=\sum_{j=i-n+2}^{n-1}x_j\,2^{\,i+1-j},\qquad\text{cioè}\qquad\frac12c_{i+1}=\sum_{j=i-n+2}^{n-1}x_j\,2^{\,i-j}. La somma di cic_i ha lo stesso intervallo con in più l'addendo j=i−n+1j=i-n+1, che vale xi−n+12 i−(i−n+1)=xi−n+1 2 n−1x_{i-n+1}2^{\,i-(i-n+1)}=x_{i-n+1}\,2^{\,n-1}: ci=12 ci+1+xi−n+1 2 n−1.c_i=\frac12\,c_{i+1}+x_{i-n+1}\,2^{\,n-1}. Controllo per n=4n=4, i=5i=5: c5=12c6+x2⋅23=4x3+8x2c_5=\frac12c_6+x_2\cdot2^3=4x_3+8x_2, come in (a).

(d) L'algoritmo e il suo costo

La prima ricorrenza si percorre in avanti da i=1i=1 a n−1n-1; la seconda all'indietro da 2n−32n-3 a nn. Serve 2n−12^{n-1}, che si ottiene man mano nel primo ciclo, dove pow2 assume il valore 2i2^i.

POWER_CONV(x)
n <- x.len
if n = 1 then return (x[0])
c[0] <- x[0]
pow2 <- 1
for i <- 1 to n-1
    pow2 <- 2 * pow2                   { pow2 = 2^i }
    c[i] <- 2 * c[i-1] + x[i]
                                       { ora pow2 = 2^(n-1) }
c[2n-2] <- x[n-1] * pow2
for i <- 2n-3 downto n
    c[i] <- c[i+1] / 2 + x[i-n+1] * pow2
return c

Correttezza. Primo ciclo, invarianti: all'inizio dell'iterazione ii vale pow2 =2i−1=2^{i-1} e c[i-1] =ci−1=c_{i-1} (per i=1i=1 perché c[0] =x0=c0=x_0=c_0 e pow2 =1=1). L'iterazione calcola pow2 =2i=2^i e ci=2ci−1+xic_i=2c_{i-1}+x_i, corretto per la prima ricorrenza. Alla fine pow2 =2n−1=2^{n-1} e le componenti c0,…,cn−1c_0,\dots,c_{n-1} sono corrette. Dopo il ciclo, c[2n-2] =xn−12n−1=x_{n-1}2^{n-1} è il caso base della seconda ricorrenza. Secondo ciclo, invariante: all'inizio dell'iterazione ii la componente c[i+1] è corretta (vero per i=2n−3i=2n-3 dopo l'assegnamento del caso base); l'iterazione calcola ci=12ci+1+xi−n+12n−1c_i=\frac12c_{i+1}+x_{i-n+1}2^{n-1}, corretto per la seconda ricorrenza. Quindi alla fine tutte le 2n−12n-1 componenti sono corrette. Verificato con un'implementazione confrontata con numpy.convolve per n=1,…,11n=1,\dots,11 con x\mathbf x complesso casuale.

Costo (operazioni tra complessi: somme, prodotti, divisioni; per n≥2n\ge2):

  • primo ciclo, n−1n-1 iterazioni, ciascuna con un prodotto per aggiornare pow2, un prodotto per 2ci−12c_{i-1} e una somma: 3(n−1)3(n-1);
  • c[2n-2]: 11 prodotto;
  • secondo ciclo, n−2n-2 iterazioni (da 2n−32n-3 a nn), ciascuna con una divisione, un prodotto e una somma: 3(n−2)3(n-2).

Totale: T(n)=3(n−1)+1+3(n−2)=6n−8=Θ(n)T(n)=3(n-1)+1+3(n-2)=6n-8=\Theta(n). Per n=1n=1 nessuna operazione. Il conteggio è stato verificato contando le operazioni dell'implementazione per n=2,…,11n=2,\dots,11 (4,10,16,…,584,10,16,\dots,58).

Miglioramento della costante. pow2 =2n−1=2^{n-1} serve solo dopo il primo ciclo, e si può ottenere per quadrature successive con O(log⁡n)O(\log n) prodotti (Esponenziazione veloce per quadrature successivepotenza x^n con un operatore associativo con elemento neutro, costo = numero di applicazioni dell'operatore; STUPID_POWER costa n-1, la sottostruttura x^n = (x^floor(n/2))^2 (per n dispari per x) dà POWER con T(n) = floor(log2 n) + (numero di bit a 1 di n) - 1, Theta(log n) (caso peggiore 2 floor(log2 n), n = 2^t - 1); versioni iterative POWER2 e POWER3 dalla rappresentazione binaria di n; esempio con la matrice di Fibonacci; il modello a costo unitario vale solo se l'operatore non fa crescere gli operandi.Esponenziazione veloce per quadrature successive →). Allora il primo ciclo costa 2(n−1)2(n-1) (2ci−12c_{i-1} e la somma) e il totale diventa 2(n−1)+1+3(n−2)+O(log⁡n)=5n+O(log⁡n)2(n-1)+1+3(n-2)+O(\log n)=5n+O(\log n), con coefficiente del termine dominante 55 al posto di 66. In entrambi i casi la complessità è lineare, contro il Θ(n2)\Theta(n^2) della convoluzione per definizione.

Versione ripasso

Testo. c=x⋆pn\mathbf c=\mathbf x\star\mathbf p_n con pn=(1,2,…,2n−1)\mathbf p_n=(1,2,\dots,2^{n-1}), x∈Cn\mathbf x\in\mathbb C^n arbitrario. (a) [3] n=4n=4, le 77 componenti; (b) [4] specializzare la sommatoria nei due insiemi 0≤i≤n−10\le i\le n-1 e n≤i≤2n−2n\le i\le2n-2; (c) [6] due ricorrenze, la prima da c0c_0 in funzione di ci−1c_{i-1}, la seconda da c2n−2c_{2n-2} in funzione di ci+1c_{i+1}; (d) [4] algoritmo iterativo POWER_CONV con O(n)O(n) operazioni. Teoria: Polinomi - rappresentazione per coefficienti e per valori e convoluzioneun polinomio di grado limitato da n si rappresenta con n coefficienti o con n valori su n punti distinti (teorema di interpolazione, matrice di Vandermonde). Per coefficienti la somma costa n e il prodotto, la convoluzione lineare, n^2 prodotti e (n-1)^2 somme; per valori il prodotto costa 2n-1 prodotti ma serve una base estesa di almeno 2n-1 punti. Le conversioni generali costano troppo: valutazione n volte con Horner 2n(n-1), interpolazione con Gauss-Jordan n^3 o con Lagrange n^2. Serve una base speciale: la DFT.Polinomi - rappresentazione per coefficienti e per valori e convoluzione →, Esponenziazione veloce per quadrature successivepotenza x^n con un operatore associativo con elemento neutro, costo = numero di applicazioni dell'operatore; STUPID_POWER costa n-1, la sottostruttura x^n = (x^floor(n/2))^2 (per n dispari per x) dà POWER con T(n) = floor(log2 n) + (numero di bit a 1 di n) - 1, Theta(log n) (caso peggiore 2 floor(log2 n), n = 2^t - 1); versioni iterative POWER2 e POWER3 dalla rappresentazione binaria di n; esempio con la matrice di Fibonacci; il modello a costo unitario vale solo se l'operatore non fa crescere gli operandi.Esponenziazione veloce per quadrature successive →.

  • (a) c0=x0c_0=x_0, c1=2x0+x1c_1=2x_0+x_1, c2=4x0+2x1+x2c_2=4x_0+2x_1+x_2, c3=8x0+4x1+2x2+x3c_3=8x_0+4x_1+2x_2+x_3, c4=8x1+4x2+2x3c_4=8x_1+4x_2+2x_3, c5=8x2+4x3c_5=8x_2+4x_3, c6=8x3c_6=8x_3.
  • (b) ci=∑j=max⁡{0,i−n+1}min⁡{i,n−1}xj2i−jc_i=\sum_{j=\max\{0,i-n+1\}}^{\min\{i,n-1\}}x_j2^{i-j}, cioè ∑j=0ixj2i−j\sum_{j=0}^{i}x_j2^{i-j} se i≤n−1i\le n-1 e ∑j=i−n+1n−1xj2i−j\sum_{j=i-n+1}^{n-1}x_j2^{i-j} se i≥ni\ge n.
  • (c) Prima metà: c0=x0c_0=x_0 e ci=2ci−1+xic_i=2c_{i-1}+x_i (si isola j=ij=i e si raccoglie 22; è Horner in z=2z=2). Seconda metà: c2n−2=xn−12n−1c_{2n-2}=x_{n-1}2^{n-1} e ci=12ci+1+xi−n+12n−1c_i=\frac12c_{i+1}+x_{i-n+1}2^{n-1} (l'addendo che manca in 12ci+1\frac12c_{i+1} è j=i−n+1j=i-n+1).
  • (d) pow2 <- 2*pow2 e c[i] <- 2*c[i-1]+x[i] per i=1..n−1i=1..n-1; poi c[2n-2] <- x[n-1]*pow2; poi c[i] <- c[i+1]/2 + x[i-n+1]*pow2 per i=2n−3i=2n-3 giù a nn. Correttezza per invarianti (pow2=2i\texttt{pow2}=2^i; c[i+1] corretto prima dell'iterazione ii).
  • Costo: 3(n−1)+1+3(n−2)=6n−83(n-1)+1+3(n-2)=6n-8; con 2n−12^{n-1} per quadrature successive 5n+O(log⁡n)5n+O(\log n). Per n=1n=1 nessuna operazione.
  • Controllo: x=(1,0,0,0)\mathbf x=(1,0,0,0) dà p4\mathbf p_4 con zeri; confronto con numpy.convolve per n=1..11n=1..11.
  • Errori tipici: limiti sbagliati della sommatoria nella seconda metà (i−n+1i-n+1, non ii); dimenticare l'addendo xi−n+12n−1x_{i-n+1}2^{n-1} nella seconda ricorrenza; contare n−1n-1 invece di n−2n-2 iterazioni del secondo ciclo; usare la FFT quando la struttura dà O(n)O(n).

Teoria collegata