Albero della ricorrenza e formula generale per le ricorrenze
In questa pagina 7
Lo srotolamento di Divide and conquer - proprietà di sottostruttura e albero delle chiamateUn algoritmo divide and conquer nasce da una proprietà di sottostruttura (funzioni Divide e Conquer, base n0) con sottoistanze di taglia strettamente minore; l'albero delle chiamate ne descrive l'esecuzione, e il D&C non ha memoria (ripete le sottoistanze uguali). Esempio completo: MAX di un array, con correttezza per induzione, ricorrenza e conteggio esatto n-1 tramite il teorema sugli alberi binari pieni.Divide and conquer - proprietà di sottostruttura e albero delle chiamate → funziona caso per caso. Qui lo si organizza in una formula generale: leggendo l'albero della ricorrenza livello per livello si ricava per un'intera famiglia di algoritmi D&C.
Albero delle chiamate e albero della ricorrenza
A ogni esecuzione di un algoritmo D&C corrisponde un albero delle chiamate. Quando si studia la complessità al caso peggiore con una ricorrenza, si associa l'albero della ricorrenza: ha la stessa forma dell'albero delle chiamate dell'istanza peggiore di taglia , e nei nodi si scrivono:
- per un nodo interno: la taglia della sottoistanza (convenzionalmente dentro il nodo) e il costo complessivo di e su quell'istanza (di fianco);
- per una foglia: la taglia di base e il costo della risoluzione diretta.
Il costo cumulativo di Divide e Conquer per un'istanza non di base, o il costo della risoluzione diretta per una di base, si chiama lavoro (work). La complessità si ottiene sommando tutti i lavori dei nodi: l'albero elenca i contributi dovuti alla gerarchia di istanze generate dall'istanza principale. Un esempio completo è l'albero per MAX in Divide and conquer - proprietà di sottostruttura e albero delle chiamateUn algoritmo divide and conquer nasce da una proprietà di sottostruttura (funzioni Divide e Conquer, base n0) con sottoistanze di taglia strettamente minore; l'albero delle chiamate ne descrive l'esecuzione, e il D&C non ha memoria (ripete le sottoistanze uguali). Esempio completo: MAX di un array, con correttezza per induzione, ricorrenza e conteggio esatto n-1 tramite il teorema sugli alberi binari pieni.Divide and conquer - proprietà di sottostruttura e albero delle chiamate →.
La famiglia di ricorrenze
Siano e funzioni non decrescenti:
- è il numero di sottoistanze generate da un'istanza (non di base) di taglia ;
- è la taglia delle sottoistanze generate; deve essere una contrazione, per ogni taglia non di base (altrimenti la ricorsione non termina);
- è il lavoro (divide più conquer) di un'istanza non di base di taglia ; con costi unitari o nulli .
Si restringe il paradigma al caso in cui ogni istanza di taglia genera sempre sottoistanze, tutte di taglia : il numero può dipendere da ma non dall'istanza. Le istanze di base hanno taglia e è un limite superiore al tempo della risoluzione diretta. Il metacodice è lo stesso di prima con , e la ricorrenza diventa con il lavoro dell'istanza (peggiore, nell'analisi del caso peggiore) di taglia .
Osservazioni. Poiché sono non decrescenti, lo è anche . Se le quantità di sono solo limitazioni superiori di numero di sottoistanze, taglie o lavoro, la soluzione è una limitazione superiore alla complessità.
Esempio: merge sort. Per e con costo unitario solo per i confronti tra chiavi:
- , (un solo elemento è già ordinato);
- (costante);
- ; per qualsiasi si userebbe la contrazione , che è un limite superiore di entrambe le taglie e darebbe solo una limitazione superiore;
- : il Divide spezza l'insieme senza confronti, il Conquer (la fusione ordinata, Algoritmi di ordinamentoSelection sort e insertion sort (quadratici, con invarianti), merge sort (divide et impera, Theta(n log n)), quick sort (caso pessimo quadratico, medio n log n), heap sort (in loco, Theta(n log n)), ordinamento senza confronti per chiavi intere in un intervallo piccolo, limite inferiore Omega(n log n) per gli algoritmi basati su confronti.Algoritmi di ordinamento →) richiede al caso peggiore confronti.
La ricorrenza è per e per .
Le iterate di
Le iterate danno le taglie delle sottoistanze ai vari livelli di ricorsione: cioè applicata volte. Casi notevoli, per ricavare l'espressione esplicita:
| forma di | ||
|---|---|---|
| , | ||
(Per : , e così via.) In presenza di parti intere l'espressione analitica di diventa molto più difficile.
Data una contrazione e un valore si definisce È il massimo indice di iterata la cui taglia è strettamente maggiore di , cioè l'ultimo livello di istanze non di base: alla radice (livello ) c'è taglia , al livello taglia , ... al livello l'ultima taglia , e al livello ci sono le foglie (istanze di base). La definizione ha senso solo per (altrimenti si prenderebbe il massimo dell'insieme vuoto): per si pone per convenzione , in modo che l'unica foglia dell'albero (l'istanza principale, già di base) sia al livello .
Calcolo di nei casi notevoli:
- , : , quindi (con potenza di , perché intero massimo strettamente minore di un intero è l'intero precedente).
- , : , quindi Più in generale, per e : .
- : , quindi e .
Analisi dell'albero della ricorrenza
Studio di . Per quanto visto, a livello le istanze hanno taglia e l'ultimo livello non di base è . Per il lavoro di ogni nodo del livello è ; al livello il lavoro di ogni foglia è .
Numero di nodi per livello. Al livello c'è un nodo, padre di nodi del livello ; ognuno di questi genera nodi del livello , e così via: un nodo del livello genera figli. Quindi i nodi del livello sono per . Si usano le convenzioni dell'elemento neutro: somma vuota e prodotto vuoto per ; così il livello ha nodo, e (con ) dà .
LIVELLO TAGLIA NODI/LIVELLO LAVORO/NODO
0 f^(0)(n) 1 w(f^(0)(n))
1 f^(1)(n) s(f^(0)(n)) w(f^(1)(n))
...
l (0<=l<=f*) f^(l)(n) prod_{j=0}^{l-1} s(f^(j)(n)) w(f^(l)(n))
...
f*+1 (foglie) <= n0 prod_{j=0}^{f*} s(f^(j)(n)) T0Per ottenere si sommano, riga per riga, i prodotti delle ultime due colonne (il lavoro cumulativo del livello):
Formula generale.
La correttezza dipende anche dalle convenzioni sopra. Dimostrazione per induzione, che nelle dispense è lasciata come esercizio:
Dimostrazione (induzione su ).
- [B] Se , : la somma è vuota () e il prodotto vuoto (), quindi .
- [HP] La formula vale per ogni taglia , con .
- [TH] e , quindi per [HP] si applica a . Valgono e (se allora e per convenzione). Posto : Moltiplicando per , che è il fattore di ogni prodotto, e cambiando indici , : Aggiungendo , cioè il termine , si ottiene la formula per .
Esempi di applicazione
Esempio 1: ,
Parametri: , , , , ; quindi , , e . La formula dà Parte . Con l'identità si ha , quindi . Queste riscritture servono a portare i termini in forma standard per poi semplificare.
Parte . Spezzando la somma e usando e : Con e : Somma. .
Verifica per induzione (logicamente non necessaria, perché la formula è dimostrata, ma utile contro gli errori di calcolo): base ; passo, per : .
Esempio 2: ,
Parametri: , , , , ; , , : perché . Controllo: , , , .
Esempio 3: merge sort, conteggio esatto
, , , , , : , nodi al livello : , lavoro per nodo . Ogni livello costa : Per : (verificato con il conteggio della ricorrenza).
Esempio 4: la contrazione
con (le torri di Hanoi): , , , , , , nodi al livello : : Con l'albero è molto profondo ( livelli) e con le foglie sono : esponenziale. Con , per esempio con , i livelli costano e la formula dà .
Taglia delle foglie diversa da (algoritmi ibridi)
Se la ricorsione si ferma a (per esempio si passa a un algoritmo diverso sotto una soglia), con e potenza di , si ha e le foglie sono con lavoro , che può dipendere da . Esempio con , , , , : per esempio . È la situazione degli esercizi con algoritmo ibrido: Esercizio - algoritmo ibrido e valore ottimo della soglia (compito del 27 gennaio 2025, es. 1) e Esercizio - algoritmo ibrido con soglia ottima e confronto con la versione ricorsiva (compito del 4 settembre 2024, es. 1).
Verifica automatica
import math
from functools import lru_cache
def T_rec(n, n0, T0, s, f, w): # ricorrenza (*) applicata alla lettera
return T0 if n <= n0 else s(n) * T_rec(f(n), n0, T0, s, f, w) + w(n)
def T_formula(n, n0, T0, s, f, w): # formula generale per livelli
if n <= n0:
return T0
taglie = [n]
while f(taglie[-1]) > n0:
taglie.append(f(taglie[-1]))
F = len(taglie) - 1 # = f*(n, n0)
tot, prod = 0, 1
for l in range(F + 1):
tot += prod * w(taglie[l])
prod *= s(taglie[l])
return tot + prod * T0
# esempio 1: 6T(n/3) + n(n-1), T(1) = 4
for k in range(12):
n = 3**k
assert T_rec(n, 1, 4, lambda m: 6, lambda m: m // 3, lambda m: m*m - m) \
== T_formula(n, 1, 4, lambda m: 6, lambda m: m // 3, lambda m: m*m - m) == 3*n*n + nLa formula coincide con la ricorrenza anche per funzioni , , qualsiasi non decrescenti (controllato su 500 casi con , costante o dipendente da , ).
Errori comuni
- Dimenticare di portare i termini in forma standard (potenze di con esponente ) prima di sommare: i termini che si cancellano ( e ) non si riconoscono.
- Contare i livelli sbagliati: i livelli non di base sono (cioè livelli) e le foglie stanno al livello , con costo per foglia e non .
- Usare invece di nel prodotto quando dipende dalla taglia.
- Dimenticare la somma dei costi delle foglie (se domina nei casi con molte foglie).
- Usare la formula con non della forma richiesta (, o ) senza dirlo: i risultati valgono per quei .
Domande d'esame
- Risolvere con la formula generale , , per . Traccia: , , , , ; il livello costa , quindi .
- Che cosa rappresenta e quanto vale per , ? Risposta: l'ultimo livello dell'albero con istanze non di base; ; le foglie sono al livello .
- Qual è la complessità esatta (in confronti) del merge sort nel caso peggiore per ? .
Versione ripasso
- Albero della ricorrenza: forma dell'albero delle chiamate dell'istanza peggiore; nodo interno = taglia + lavoro (Divide+Conquer), foglia = costo diretto. = somma dei lavori di tutti i nodi.
- Famiglia: per , per , con non decrescenti e (contrazione). Se sono limiti superiori, è un limite superiore. Merge sort (Algoritmi di ordinamentoSelection sort e insertion sort (quadratici, con invarianti), merge sort (divide et impera, Theta(n log n)), quick sort (caso pessimo quadratico, medio n log n), heap sort (in loco, Theta(n log n)), ordinamento senza confronti per chiavi intere in un intervallo piccolo, limite inferiore Omega(n log n) per gli algoritmi basati su confronti.Algoritmi di ordinamento →): .
- Iterate: , . ; ; . (ultimo livello non di base; se ): per ; per con ; per .
- Livelli: livello : taglia , nodi, lavoro ciascuno; le foglie al livello con lavoro . Convenzioni: somma vuota , prodotto vuoto .
- Formula generale: Dimostrazione per induzione su usando e riindicizzando.
- Esempi: , : (parte , parte con ); , : ; merge sort: ; , : ; : ; con le foglie sono con lavoro (ibridi).
- Errori tipici: non portare i termini in forma standard; sbagliare il numero di livelli (foglie a con costo ); usare al posto di ; dimenticare il termine delle foglie.
- Passi dell'Esempio 1 (, ): , , , . Livello : nodi di lavoro , quindi ; (da e ). Somma: i termini si cancellano. Verifica: .
- Passi dell'Esempio 2: , somma , foglie : . Controllo: , , .
- Merge sort esatto: livello costa ; ( per ). Hanoi (, ): .
- Con (ibridi): , foglie di costo (anche dipendente da ): , , dà . Esercizi: Esercizio - algoritmo ibrido e valore ottimo della soglia (compito del 27 gennaio 2025, es. 1), Esercizio - algoritmo ibrido con soglia ottima e confronto con la versione ricorsiva (compito del 4 settembre 2024, es. 1).
- Calcolo di : con : , massimo . Con : . Con : , quindi .
- Dimostrazione della formula (idea): induzione su ; , e ; moltiplicare la formula di per aggiunge il fattore a ogni prodotto e sposta gli indici di uno; il termine è il livello .
- Domande tipiche: risolvere , : livello , ; significato di ; conteggio esatto del merge sort.
Esercizi su questo argomento
- Esercizio - algoritmo di Strassen sulle matrici supercircolanti (esercizi su supercircolanti e circolanti, es. SC 6)
- Esercizio - algoritmo ibrido con soglia ottima e confronto con la versione ricorsiva (compito del 4 settembre 2024, es. 1)
- Esercizio - algoritmo ibrido e valore ottimo della soglia (compito del 27 gennaio 2025, es. 1)
- Esercizio - domande di prima parte su problemi computazionali, ricorrenze e conteggi (simulazioni d'esame)
- Esercizio - due elementi più piccoli di una stringa con divide and conquer (simulazione d'esame 2023-24, problema 1)
- Esercizio - funzione g(n) definita per ricorrenza, calcolo memoizzato e formula chiusa (compito del 19 giugno 2024, es. 2)
- Esercizio - induzione parametrica con parti intere e limite lineare (compito del 9 luglio 2025, es. 1)
- Esercizio - matrici RCA, somma e prodotto matrice-vettore (compito del 18 giugno 2026, es. 1)
- Esercizio - matrici SIR, somma, prodotto e inversa (compito del 29 gennaio 2026, es. 1)
- Esercizio - ricorrenza con parti intere, Master Theorem e induzione parametrica (simulazione d'esame 2025-26, es. 1)
- Esercizio - ricorrenza con sottoistanze di taglia metà e un quarto, forma chiusa e profondità delle foglie (compito del 5 settembre 2025, es. 1)
Teoria collegata
- Algoritmo di Strassen e ibridazione
- Divide and conquer - proprietà di sottostruttura e albero delle chiamate
- Esponenziazione veloce per quadrature successive
- Fast Fourier Transform - algoritmo ricorsivo e trasformata inversa
- Master Theorem - enunciato, casi e applicazioni
- Matrici circolanti e supercircolanti - algoritmi divide and conquer
- Prodotto di matrici - algoritmo iterativo e ricorsivo a blocchi
- Trasformate notevoli ed esponenziazione di polinomi con la FFT