Salta al contenuto
Note per Studenti Dimostrazioni, induzione e invarianti

Dimostrazioni, induzione e invarianti

In questa pagina 4

Servono per le analisi di complessità e di correttezza (l'algoritmo termina e risolve il problema computazionale, vedi Problemi computazionali e algoritmiProblema computazionale come insieme di coppie (istanza, soluzione); algoritmo e modello di calcolo RAM; pseudocodice; taglia di un'istanza; ADT e struttura dati concreta; esempio svolto con ricerca lineare e binaria in un array ordinato.Problemi computazionali e algoritmi →). Le tecniche sono quelle di Analisi 1 (Tecniche di dimostrazioneDimostrazione diretta, per contronominale e per assurdo di un teorema del tipo ipotesi implica tesi.Tecniche di dimostrazione →, Principio di induzionePer dimostrare una proprietà per ogni n basta il passo base e il passo induttivo da n a n+1.Principio di induzione →, Logica e quantificatoriProposizioni, connettivi, predicati e quantificatori (per ogni, esiste), con le regole per negarli.Logica e quantificatori →): qui si applicano agli algoritmi. In particolare gli enunciati "per ogni nn abbastanza grande" e "esiste un'istanza" delle analisi di complessità sono quantificatori ∀\forall ed ∃\exists, e il modo di provarli è diverso: un ∀\forall richiede una dimostrazione valida per ogni caso, un ∃\exists si prova esibendo un caso.

Esempio, controesempio, assurdo

Induzione

Teorema (principio di induzione). Per provare che Q(n)Q(n) vale per ogni n≥n0n \ge n_0:

  1. si sceglie k≥0k \ge 0 (di solito k=0k = 0);
  2. base: si dimostra Q(n0),Q(n0+1),…,Q(n0+k)Q(n_0), Q(n_0+1), \dots, Q(n_0+k);
  3. passo: si fissa n≥n0+kn \ge n_0 + k arbitrario e, assumendo Q(m)Q(m) vera per ogni mm con n0≤m≤nn_0 \le m \le n (ipotesi induttiva), si dimostra Q(n+1)Q(n+1).

Il passo deve valere per ogni n≥n0+kn \ge n_0 + k. Servono più casi base quando il passo usa più valori precedenti (Fibonacci usa i due precedenti: k=1k = 1).

Esempio 1. ∑i=0ni=n(n+1)2\sum_{i=0}^{n} i = \frac{n(n+1)}{2} per n≥0n \ge 0 (sommatorie: SommatorieIl simbolo di sommatoria, le sue proprietà (linearità, additività, cambio di indice) e le somme notevoli di Gauss e geometrica.Sommatorie →). Base n=0n = 0: a sinistra c'è la somma del solo termine i=0i=0, cioè 00; a destra 0⋅12=0\frac{0\cdot1}{2}=0. Passo: si suppone vera la formula per nn e si scrive la somma fino a n+1n+1 isolando l'ultimo termine, ∑i=0n+1i=∑i=0ni⏟ipotesi induttiva+(n+1)=n(n+1)2+(n+1)=n(n+1)+2(n+1)2=(n+1)(n+2)2,\sum_{i=0}^{n+1} i = \underbrace{\sum_{i=0}^{n} i}_{\text{ipotesi induttiva}} + (n+1) = \frac{n(n+1)}{2} + (n+1) = \frac{n(n+1) + 2(n+1)}{2} = \frac{(n+1)(n+2)}{2}, dove si è messo a denominatore comune 22 e raccolto (n+1)(n+1). L'ultimo membro è proprio la formula con n+1n+1 al posto di nn. ✓

Esempio 2 (Fibonacci). F(0)=0F(0) = 0, F(1)=1F(1) = 1, F(n+1)=F(n)+F(n−1)F(n+1) = F(n) + F(n-1). Si prova F(n)=15(Φn−Φ^n)F(n) = \frac{1}{\sqrt 5}(\Phi^n - \hat\Phi^n) con Φ=1+52\Phi = \frac{1+\sqrt5}{2}, Φ^=1−52\hat\Phi = \frac{1-\sqrt5}{2}. Base: n=0,1n = 0, 1 (verifica diretta): per n=0n=0, 15(Φ0−Φ^0)=1−15=0=F(0)\frac{1}{\sqrt5}(\Phi^0-\hat\Phi^0)=\frac{1-1}{\sqrt5}=0=F(0); per n=1n=1, Φ−Φ^=1+52−1−52=5\Phi-\hat\Phi=\frac{1+\sqrt5}{2}-\frac{1-\sqrt5}{2}=\sqrt5, e 55=1=F(1)\frac{\sqrt5}{\sqrt5}=1=F(1). Servono due casi base perché il passo usa i due valori precedenti F(n)F(n) ed F(n−1)F(n-1). Passo (n≥1n \ge 1): F(n+1)=F(n)+F(n−1)=15[(Φn+Φn−1)−(Φ^n+Φ^n−1)]F(n+1) = F(n) + F(n-1) = \frac{1}{\sqrt5}\big[(\Phi^n + \Phi^{n-1}) - (\hat\Phi^n + \hat\Phi^{n-1})\big]. Si osserva Φn+Φn−1=Φn−1(Φ+1)=Φn−1Φ2=Φn+1\Phi^n + \Phi^{n-1} = \Phi^{n-1}(\Phi + 1) = \Phi^{n-1}\Phi^2 = \Phi^{n+1}, perché Φ+1=3+52=Φ2\Phi + 1 = \frac{3+\sqrt5}{2} = \Phi^2: infatti Φ2=(1+5)24=1+25+54=6+254=3+52\Phi^2 = \frac{(1+\sqrt5)^2}{4} = \frac{1 + 2\sqrt5 + 5}{4} = \frac{6+2\sqrt5}{4} = \frac{3+\sqrt5}{2}. Allo stesso modo per Φ^\hat\Phi: Φ^+1=3−52=Φ^2\hat\Phi + 1 = \frac{3-\sqrt5}{2} = \hat\Phi^2 (i due numeri sono le radici di x2=x+1x^2 = x + 1). Quindi F(n+1)=15(Φn+1−Φ^n+1)F(n+1) = \frac{1}{\sqrt5}(\Phi^{n+1} - \hat\Phi^{n+1}). ✓ (Verificato numericamente: 0,1,1,2,3,5,8,13,21,340, 1, 1, 2, 3, 5, 8, 13, 21, 34.)

Esempio 3. Q(n)Q(n): ∑i=0ni2=n(n+1)(2n+1)6∈Θ(n3)\sum_{i=0}^{n} i^2 = \frac{n(n+1)(2n+1)}{6} \in \Theta(n^3). Base n=0n=0: 0=00=0. Passo (si somma (n+1)2(n+1)^2 a entrambi i membri dell'ipotesi, poi si raccoglie (n+1)(n+1), che compare in entrambi i termini): n(n+1)(2n+1)6+(n+1)2=(n+1) [ n(2n+1)+6(n+1) ]6=(n+1)(2n2+7n+6)6=(n+1)(n+2)(2n+3)6\frac{n(n+1)(2n+1)}{6} + (n+1)^2 = \frac{(n+1)\,[\,n(2n+1) + 6(n+1)\,]}{6} = \frac{(n+1)(2n^2 + 7n + 6)}{6} = \frac{(n+1)(n+2)(2n+3)}{6}, dove l'ultimo passo è la scomposizione 2n2+7n+6=(n+2)(2n+3)2n^2+7n+6=(n+2)(2n+3) (verifica: (n+2)(2n+3)=2n2+3n+4n+6(n+2)(2n+3)=2n^2+3n+4n+6). Il risultato è la formula con n+1n+1 al posto di nn, perché 2(n+1)+1=2n+32(n+1)+1=2n+3. ✓

Correttezza di un algoritmo

Schema generale: si individuano lo stato iniziale e quello finale desiderato, si scompone l'algoritmo in segmenti con uno stato atteso a fine segmento (checkpoint) e si prova che dallo stato iniziale si raggiungono in successione tutti i checkpoint; l'ultimo deve implicare lo stato finale. I segmenti notevoli sono i cicli. La terminazione si prova assicurandosi che cicli e ricorsione abbiano fine.

Invarianti di ciclo

Definizione (invariante di ciclo). Un invarianteProprietà sulle variabili del ciclo che descrive lo stato a ogni iterazione. è una proprietà delle variabili usate nel ciclo che:

  1. vale all'inizio del ciclo, come conseguenza dello stato iniziale (inizializzazione);
  2. vale alla fine di ogni iterazione, assumendo che valesse all'inizio di quella iterazione (conservazione);
  3. alla fine dell'ultima iterazione, insieme alla condizione di uscita, implica la correttezza del ciclo (uso).

Esempio. In arrayMax l'invariante «alla fine dell'iterazione ii, currMax=max⁡{A[0],…,A[i]}currMax=\max\{A[0],\dots,A[i]\}» vale all'inizio (currMax=A[0]currMax=A[0]), si conserva a ogni passo e a fine ciclo (i=n−1i=n-1) dà il massimo dell'intero array.

Un ciclo esegue ≥0\ge 0 iterazioni del corpo; for i <- 1 to n ne fa sempre nn. Il legame con l'induzione è stretto: "vale all'inizio" è il caso base, "si conserva a ogni iterazione" è il passo induttivo, e le iterazioni successive sono il nn che cresce di 11.

arrayMax. currMax <- A[0]; for i <- 1 to n-1 do currMax <- max(currMax, A[i]). Invariante: alla fine dell'iterazione ii, currMax=max⁡{A[0],…,A[i]}currMax = \max\{A[0], \dots, A[i]\}. All'inizio (ii prima del ciclo) currMax=A[0]currMax = A[0] = massimo di A[0..0]A[0..0]. Conservazione: il massimo di A[0..i]A[0..i] è il massimo tra il massimo di A[0..i−1]A[0..i-1] e A[i]A[i]. Alla fine (i=n−1i = n-1) currMaxcurrMax è il massimo di tutto l'array. Traccia su A=[3,7,2,9,4]A=[3,7,2,9,4]:

dopo l'iterazione ii A[i]A[i] currMaxcurrMax max⁡{A[0..i]}\max\{A[0..i]\}
(prima del ciclo) 3 3
1 7 7 7
2 2 7 7
3 9 9 9
4 4 9 9

Le due colonne finali coincidono a ogni riga: è ciò che afferma l'invariante.

Il più lungo segmento di 1 in una sequenza di bit S[1..n]S[1..n]:

max <- 0; curr <- 0
for i <- 1 to n do
    if S[i] = 1 then
        curr <- curr + 1
        if curr > max then max <- curr
    else curr <- 0
return max

Invariante alla fine dell'iterazione ii: (a) currcurr è la lunghezza del segmento di 1 che termina in S[i]S[i] (zero se S[i]=0S[i] = 0) e (b) maxmax è la lunghezza massima di un segmento di 1 in S[1..i]S[1..i]. Inizio (i=0i = 0): sequenza vuota, entrambi 00. Conservazione: se S[i]=1S[i] = 1 il segmento che termina in ii estende quello che terminava in i−1i-1, quindi currcurr cresce di 1 e maxmax viene aggiornato se lo supera; se S[i]=0S[i] = 0 il segmento si interrompe e curr=0curr = 0, mentre maxmax non cambia. Fine: per i=ni = n la (b) dà la risposta. (Controllato con un test casuale su sequenze di lunghezza ≤15\le 15.) Traccia su S=[1,1,0,1,1,1,0]S=[1,1,0,1,1,1,0]:

ii S[i]S[i] currcurr maxmax
0 (inizio) 0 0
1 1 1 1
2 1 2 2
3 0 0 2
4 1 1 2
5 1 2 2
6 1 3 3
7 0 0 3

Alla fine max=3max=3, la lunghezza del segmento S[4..6]S[4..6].

Cicli con due indici (esempio d'esame): in while (i < n) AND (j < m) su una matrice con colonne decrescenti, l'invariante lega il valore corrente a ii e jj; si veda l'esercizio Esercizio 3 · invariante su matrice a colonne decrescenti.

Errori comuni

  • Un invariante che vale solo alla fine (non è un invariante): deve valere anche all'inizio e dopo ogni iterazione.
  • Un invariante troppo debole: non basta a concludere la correttezza all'uscita (es. "currMax∈AcurrMax \in A" non dice che sia il massimo).
  • Nell'induzione: provare il passo solo per un nn specifico, o dimenticare uno dei casi base quando il passo usa due valori precedenti.
  • Confondere ipotesi induttiva e tesi: nel passo si assume Q(m)Q(m) per m≤nm \le n e si prova Q(n+1)Q(n+1).

Versione ripasso

  • Tecniche: esempio (per un Ω\Omega basta un' istanza cattiva); controesempio (basta un caso in cui l'enunciato ∀\forall è falso: 24−1=15=3⋅52^4-1 = 15 = 3\cdot5); assurdo (si nega la tesi e si trova una contraddizione: se abab è dispari, allora aa e bb sono dispari).
  • Principio di induzione: per Q(n)Q(n) con n≥n0n\ge n_0: si sceglie k≥0k\ge0; base Q(n0),…,Q(n0+k)Q(n_0), \dots, Q(n_0+k); passo: assumendo Q(m)Q(m) per n0≤m≤nn_0 \le m \le n, si prova Q(n+1)Q(n+1) per ogni n≥n0+kn \ge n_0+k (vedi Principio di induzionePer dimostrare una proprietà per ogni n basta il passo base e il passo induttivo da n a n+1.Principio di induzione →).
  • Gauss: ∑i=0ni=n(n+1)2\sum_{i=0}^{n} i = \frac{n(n+1)}{2}; passo: n(n+1)2+(n+1)=(n+1)(n+2)2\frac{n(n+1)}{2} + (n+1) = \frac{(n+1)(n+2)}{2}.
  • Quadrati: ∑i=0ni2=n(n+1)(2n+1)6∈Θ(n3)\sum_{i=0}^{n} i^2 = \frac{n(n+1)(2n+1)}{6} \in \Theta(n^3); passo: (n+1)(n+2)(2n+3)6\frac{(n+1)(n+2)(2n+3)}{6}, usando 2n2+7n+6=(n+2)(2n+3)2n^2+7n+6=(n+2)(2n+3).
  • Fibonacci: F(n)=15(Φn−Φ^n)F(n) = \frac{1}{\sqrt5}(\Phi^n - \hat\Phi^n) con Φ=1+52\Phi = \frac{1+\sqrt5}{2} e Φ^=1−52\hat\Phi = \frac{1-\sqrt5}{2}. Servono due casi base (n=0,1n=0,1) perché il passo usa F(n)F(n) e F(n−1)F(n-1). Passo: Φn+Φn−1=Φn+1\Phi^n + \Phi^{n-1} = \Phi^{n+1}, perché Φ+1=Φ2\Phi+1=\Phi^2; lo stesso vale per Φ^\hat\Phi, radici di x2=x+1x^2=x+1.
  • Correttezza: stato iniziale →\to checkpoint →\to stato finale; la terminazione di cicli e ricorsione va provata a parte (vedi Problemi computazionali e algoritmiProblema computazionale come insieme di coppie (istanza, soluzione); algoritmo e modello di calcolo RAM; pseudocodice; taglia di un'istanza; ADT e struttura dati concreta; esempio svolto con ricerca lineare e binaria in un array ordinato.Problemi computazionali e algoritmi →).
  • Invariante di ciclo: (1) vale prima della prima iterazione; (2) se vale prima di un'iterazione, vale dopo; (3) a fine ciclo, con la condizione di uscita, implica la tesi. Vale all'inizio come caso base e si conserva come passo induttivo.
  • arrayMax: invariante currMax=max⁡{A[0..i]}currMax = \max\{A[0..i]\} dopo l'iterazione ii. Traccia su A=[3,7,2,9,4]A=[3,7,2,9,4]: 3,7,7,9,93, 7, 7, 9, 9, cioè il massimo del prefisso.
  • Segmento di 1 in S[1..n]S[1..n]: currcurr = lunghezza del segmento di 1 che termina in S[i]S[i] (zero se S[i]=0S[i]=0); maxmax = lunghezza massima in S[1..i]S[1..i]. Traccia su S=[1,1,0,1,1,1,0]S=[1,1,0,1,1,1,0]: max=3max=3 alla fine.
  • Schema per i cicli: un for i <- 1 to n fa sempre nn iterazioni; la base dell'induzione è lo stato prima del ciclo, il passo è un singolo giro.
  • Errori tipici: invariante vero solo a fine ciclo o troppo debole (es. "currMax∈AcurrMax \in A" non dice che sia il massimo); passo induttivo non valido per ogni nn; casi base mancanti quando il passo usa due valori; confondere ipotesi e tesi.

Esercizi su questo argomento

Teoria collegata