Introduzione alla correttezza degli algoritmi
Massimo Benerecetti
Università di Napoli, “Federico II”, Napoli

Questo documento contiene una breve introduzione alle dimostrazioni di correttezza deglialgoritmi. Nella prima sezione del documento verrà fornito un richiamo alla tecnica di dimostrazione per induzione. La seconda sezione illustra, tramite semplici esempi, le problematiche connesse alle dimostrazioni di correttezza degli algoritmi. Le sezioni 3 e 4 descrivono
le tecniche di dimostrazione per algoritmi ricorsivi e iterativi, rispettivamente. In sezione 5
verrà trattato il problema della dimostrazione di terminazione per algoritmi iterativi e ricorsivi.
Invine, la sezione 6 presenta un’applicazione degli argomenti trattati in questa dispensa alla
dimostrazione completa della correttezza dell’algoritmo di ordinamento InsertionSort.

1

Principio di Induzione

Il Principio di Induzione Matematica è una importante tecnica di dimostrazione, particolarmente adatta a dimostrare proprietà universali che in cui intervengano numeri interi.

Principio di Induzione Semplice. Sia n0 un intero e sia P (n) un enunciato (una proprietà) che ha senso per ogni n ≥ n0 . Se:
1. P (n0 ) è vero
2. per ogni n > n0 , P (n) vero implica P (n + 1) vero
allora l’enunciato P (n) è vero per tutti i numeri naturali n ≥ n0 .

Esempio 1. Mostriamo che che la somma dei primi n numeri interi naturali ècioè mostriamo che vale l’uguaglianza:
n
X
i=1

i=

n(n + 1)2

1n(n + 1),2

1 Principio di Induzione2

per ogni intero n ≥ 1.
n
X

n+1
X
n(n + 1)(n + 1)(n + 2)
In questo caso P (n) è, mentre P (n+1) è. Vediamo
i=
i=
2
2
i=1
i=1
che:

• P (1) è vera: infatti il primo membro vale 1 e il secondo membro

1(1 + 1)= 1;
2

• supponendo vero P (n), si ottiene che P (n + 1) è vero. Scomponendo la sommatorianXopportunamente e utilizzando la verità dell’ipotesi induttiva P (n) secondo cui èi=
i=1n(n + 1)
, otteniamo:
2
" n #


X
(n + 1)(n + 2)[n(n + 1) + 2(n + 1)]
n(n + 1)=+(n+1) =i=i +(n+1) =
2
2
2
i=1
i=1

n+1
X

che è la tesi.
Pertanto la proprietà è vera per tutti gli n ≥ 1.
Esempio 2. Dimostrare che la somma dei primi n numeri naturali dispari (non nulli) è n2 ,cioè che vale l’uguaglianza:nX(2n − 1) = n2i=1

per ogni intero n ≥ 1.
• P(1) è vero: infatti il primo membro è 2 · 1 − 1 = 1, mentre il secondo membro è 12 = 1.
n
X
• Supposto P (n) vero, cioè supposto che valga l’uguaglianza(2n − 1) = n2 , proviamo
i=1

P (n + 1), cioè l’uguaglianza

n+1
X
i=1

(2n − 1) = (n + 1)2

1 Principio di Induzione

3

Scomponendo opportunamente la sommatoria e utilizzando l’ipotesi induttiva, otteniamo:n+1
X(2n−1) =

i=1

" n
X

#
(2n − 1) +2·(n+1)−1 = n2 +2·(n+1)−1 = n2 +2n+1 = (n+1)2

i=1

che è la tesi.
Pertanto la proprietà P (n) è vera per tutti gli n ≥ 1.
Principio di Induzione Forte. Sia n0 un intero e sia P (n) un enunciato (una proprietà)che ha senso per ogni n ≥ n0 . Se:
1. P (n0 ) è vero
2. per ogni n > n0 , P (k) vero per ogni n0 ≤ k < n implica P (n) vero
allora l’enunciato P (n) è vero per tutti i numeri naturali n ≥ n0 .
Esempio 3.

Vogliamo provare che ogni numero naturale n ≥ 2 è prodotto di numeri primi.

• P(2): l’affermazione è vera per n = 2, poiché esso stesso è un numero primo.
• Consideriamo un generico n > 2 e supponiamo che tutti i numeri da 2 fino ad n − 1 siano
prodotti di numeri primi. Cerchiamo di proviare che, allora, anche n lo deve essere.
Infatti, se n è esso stesso un numero primo, allora l’affermazione è banalmente vera. Se,
invece, n non è primo, allora, per definizione di numero primo, si può esprimere comeprodotto di due numeri interi più piccoli di n, cioè n = k1 · k2 , con k1 < n e k2 < n.
Essendo sia k1 che k2 interi minori di n, per ipotesi induttiva sono entrambi esprimibili
come prodotto di numeri primi. Cioè, k1 = p1 · ... · pr , con pi primo per ogni 1 ≤ i ≤ r,
e k2 = q1 · ... · qs , con qj primo per ogni 1 ≤ j ≤ s.
Quindi, sostituendo, avremo che:n = p1 · ... · pr · q1 · ... · qs
che dimostra che anche n è prodotto di numeri primi.
Concludiamo, allora, che l’affermazione è vera per ogni intero n ≥ 2.

2 Correttezza degli algoritmi

2

4Correttezza degli algoritmi

In questo documento diremo che un algoritmo è corretto se il suo valore di output soddisfa una determinata condizione (detta postcondizione) per ogni istanza di input che, a sua
volta, soddisfa una determinata condizione (detta precondizione). Intuitivamente, la postcondizione è una condizione che esprime formalmente la nozione informale di “risultato corretto”
dell’algoritmo, mentre la precondizione è la condizione che identifica le istanze di input “ragionevoli”, cioè quelle per le quali ci si aspetta che l’algoritmo fornisca risultati significativi.
Sia la precondizione che la postcondizione sono fortemente correlate col problema specifico
che l’algoritmo è inteso risolvere.
Ad esempio, il seguente semplice algoritmo è pensato per calcolare la somma tra due numeri
reali.
Algorithm A LG -1(X,Y)
1
2
3

S = X;
S = S + Y;
return S

In tal caso la correttezza può essere espressa dalle seguenti condizioni:(
Precondizione: X e Y numeri reali.
Correttezza
Postcondizione: Se z = A LG -1(X,Y), allora z = X+Y.
La formulazione completa della correttezza di un algoritmo, detta correttezza totale viene
formulata nel nel seguente modo.
Correttezza totale: L’algoritmo termina per tutte le istanze di input che soddisfano la precondizione e restituisce il risultato corretto.
La dimostrazione di correttezza di un algoritmo viene, tipicamente, suddivisa nella dimostrazione delle due seguenti prorpietà:• Correttezza parziale: Se l’algoritmo termina, esso restituirà il risultato corretto (che,cioè, soddisfa la postcondizione);• Terminazione: L’algoritmo termina per tutte le istanze di input che soddisfano la precondizione.
È evidente che la dimostrazione delle due proprietà sopra riportate fornisce la dimostrazionedella correttezza totale.
Una dimostrazione di correttezza consiste essenzialmente in una dimostrazione matematica.

2 Correttezza degli algoritmi

5

Il primo passo è, dunque, quello di tradurre le operazioni eseguite dall’algoritmo in opportune
relazioni matematiche tra le variabili coinvolte, che ne descrivano adeguatamente l’effetto.
Consideriamo l’esempio dell’algoritmo A LG -1(X,Y) riportato sopra. Le uniche operazioni
eseguite dall’algoritmo sono assegnamenti del valore di un’espressione matematica ad una
variabile. Appare, quindi, naturale rappresentare ogni variabile dell’algoritmo con una variabile matematica. Ad esempio, possiamo rappresentare le variabili X, Y e S con le variabilimatematiche x, y e s (intese come variabili su un dominio reale), rispettivamente. Sebbene
possa apparire naturale tradurre l’assegnamento S = X con la relazione di uguaglianza s = x,
non cosı̀ è per il secondo assegnamento. Se volessimo, infatti, rappresentare l’assegnamento
S = S + Y con la relazione s = s + y, otterremmo una descrizione matematica errata
dell’operazione di assegnamento (non esiste, infatti, alcuna coppia di valori reali per s e
y, eccettuato il caso banale y = 0, tale che la loro somma sia uguale ad uno dei due numeri). In generale, mentre l’uguaglianza è una relazione che può sussistere tra i valori divariabili o espressioni (e che, quindi, non ha alcun effetto sul loro valore), l’assegnamentoè un’operazione che modifica il valore di una variabile. È chiaro, infatti, che le occorrenzedella variabile S a destra e a sinistra dell’assegnamento svolgono ruoli diversi e assumono
valori differenti durante l’esecuzione dell’algoritmo. In particolare, l’occorrenza della variabile S a destra dell’assegnamento si riferisce al valore che la variabile S assume primadell’assegnamento, mentre quella a sinistra si riferisce al valore che la variabile S assume
dopo dell’assegnamento. Al fine di poter rappresentare i differenti valori che la stessa variabiledell’algoritmo può assume durante l’esecuzione è, quindi, necessario rappresentare la stessa
variabile con più variabili matematiche. Le variabili matematiche associate alla stessa variabile dell’algoritmo intendono rappresentare i possibili stati (valori) che essa può assumere
durante l’esecuzione. Tornando all’esempio dell’algoritmo A LG -1(X,Y), assoceremo alla
variabile S tre variabili matematiche, che chiameremo s0 , s1 e s2 , che rappresentano il valore che S assume inizialmente, dopo il primo assegnamento e dopo il secondo assegnamento,
rispettivamente. Poiché né X né Y compaiono a sinistra di un assegnamento, è sufficiente
rappresentarle ciascuna di esse con una singola variabile matematica (x e y). Possiamo ora
tradurre l’algoritmo A LG -1(X,Y) con le seguenti relazioni matematiche:
s1 = xs2 = s1 + y
Poiché il valore di ritorno dell’algoritmo è proprio il valore di S dopo il secondo assegnamento,
il valore finale dell’algoritmo è quindi rapprensetato dalla variabile matematica s2 . Sfruttando
le proprietà della relazione di uguaglianza (in particolare, il fatto che se sussistono le relazioni
a = b e c = a + d allora sussiste anche la relazione c = b + d), otteniamo che, a partire dalle
relazioni che descrivono l’algoritmo segue anche la relazione s2 = x + y. In altre parole, ilvalore ritornato dall’algoritmo è proprio la somma dei valori forniti in inpunt. Ciò dimostra laverità della proprietà di correttezza sopra individuata.

3 Correttezza di algoritmi ricorsivi

6Consideriamo ora il seguente algoritmo.
Algorithm A LG -2(X)
if (X < 0) thenS = -X
else
S=X
return S

1
2
3
4
5

(
Precondizione: X numero reale.
Correttezza

Postcondizione: Se z = A LG -2(X), allora z = | X |.

Seguendo le considerazioni sopra riportate, rappresenteremo con la variabile x la variabile X
e con le variabili s0 e s1 la variabile S. In base alla semantica del costrutto if ... then ... else ...,
l’assegnamento S = -X viene eseguito quando il valore di X è negativo, mentre l’assegnamento
S = X viene eseguito quando il valore di X è positivo o nullo. Possiamo, quindi, tradurre
l’algoritmo con le seguenti relazioni (dove → è il simbolo che denota l’implicazione logica):(x < 0) → (s1 = −x)(x ≥ 0) → (s1 = x)
Dalle relazioni sopra riportate segue immediatamente la seguente:−x se x < 0s1 =x se x ≥ 0
Per la definizione di valore assoluto, questo implica s1 = | x |. Essendo s1 la rappresentazionedel valore di S al termine dell’algoritmo, segue che il valore ritornato dall’algoritmo è proprio
il valore assoluto del dato X in input.

3

Correttezza di algoritmi ricorsivi

Una delle tecniche più utilizzate per dimostrare la correttezza di un algoritmo ricorsivo è
quella di sviluppare, sfruttando la struttura induttiva dell’algoritmo, una dimostrazione per
induzione. Tipicamente, la dimostrazione pocede per induzione sulla dimensione dell’istanza
di input ricevuta dall’algoritmo. In altre parole, si vuole dimostrare, per ogni valore i ≥ 0della dimensione dell’input, che: per ogni istanza di input di dimensione i che soddisfi laprecondizione, l’algoritmo, se termina (correttezza parziale), restituisce il risultato desiderato(postcondizione).
Per completare la dimostrazione di correttezza (correttezza totale) è poi sufficiente dimostrare
che: l’algoritmo termina per ogni istanza in input che soddisfa la precondizione (terminazione).

3 Correttezza di algoritmi ricorsivi

7Consideriamo, a titolo di esempio, il seguente algoritmo:
Algorithm B IN S EARCH (A[],K,P,R)
1
2
3
4
5
6
7
8

if P < R then

P +R
Q=
2
if K ≤ A[Q] thenreturn BinSearch(A[],K,P,Q)
else
return BinSearch(A[],K,Q+1,R)
else
return P

CorrettezzaPrecondizione: L’array A è ordinato. Quindi, posto n = length(A),l’assunzione è esprimibile come segue: ∀1 ≤ x < n. A[x]≤ A[x+1].
Postcondizione: Se k è un elemento presente nell’array A (k ∈A) e
z = B IN S EARCH (A,K,1,N) , allora k =A[z].

Iniziamo con l’individuare le variabili matematiche che utilizzeremo per rappresentare le variabili dell’algoritmo. Poiché ogni variabile dell’algoritmo viene assegnata al massimo una
volta durante l’esecuzione della stessa chiamata ricorsiva, sarà sufficiente utilizzare una singola variabile matematica per ciascuna variabile dell’algoritmo. La variabile a (che, con un
abuso di notazione, nel seguito verrà intesa, per semplicità, a volte come insieme e a volte
come sequenza) sarà utilizzata per rappresentare l’array A. Per convenzione, indicheremo
com a[p..r] l’insieme di elementi della sequenza a contenuti tra la posizione p e la posizione
r. Le variabili p, r, k e q verranno, invece, utilizzate di seguito per rappresentare le variabili P,
R, K e Q, rispettivamente.
Al fine di dimostrare la proprietà di correttezza sopra riportata, dimostreremo la seguente
proprietà più generale:
Precondizione: P e R sono interi tali che 0 ≤ P,R ≤ length(A) e l’arrayA è ordinato. L’assunzione è esprimibile come segue: ∀P ≤ x <
R. A[x]≤ A[x + 1].
CorrettezzaPostcondizione: Se k è un elemento presente in A[P..R] (k ∈A[P..R]) ez = B IN S EARCH (A,K,P,R) , allora k =A[z].
Nel caso specifico, dimostreremo che, posto i = r − p + 1 (cioè la dimensione della porzione
dell’array compresa tra p e r):∀ i ≥ 0, se k ∈ a[p..r] e z = B IN S EARCH(a, k, p, r), allora k = a[z].
La dimostrazione procede come segue:3 Correttezza di algoritmi ricorsivi

8• Caso base 1 per i = 0. In questo caso p > r. La condizione dell’if...then...else è quindi
falsa e l’algoritmo procede ad eseguire il ramo else (riga 8). Viene, quindi, ritornato ilvalore p. Poiché, però, se i = 0 la sequenza in input è vuota, la proprietà di correttezza èbanalmente verificata in quanto k 6∈ a.
• Caso base 2 per i = 1. In questo caso p = r. La condizione dell’if...then...else è,ancora una volta, falsa e l’algoritmo procede ad eseguire il ramo else (riga 8), ritornando
il valore p. Segue che:– se k ∈ a allora deve necessariamente valere che a[p] = k e la proprietà di correttezzaè verificata.
– se k 6∈ a la priprietà di correttezza vale banalmente, come nel caso i = 0.
In entrambi i casi, la proprietà di correttezza vale.
• Caso induttivo per i > 1. Assumiamo, per ipotesi induttiva, che una qualsiasi chiamata
all’algoritmo B IN S EARCH(a, k, p, r), con r − p + 1 < i, soddisfi la postcondizione, cioè
che, se k ∈ a[p..r] e z è il valore restituito, allora a[z] = k.
Consideriamo ora una qualsiasi chiamata all’algoritmo con parametri p e r tali che r −p + 1 = i. Poiché i > 1, p < r e la condizione a riga 1 è sicuramente vera. L’algoritmo
procede, quindi, ad eseguire la riga 2. L’assegnamento a riga 2 viene tradotto nellarelazione q = b p+rc. È immediato verificare che p ≤ q < r. Infatti:
2– poiché p < r, allora, sommando p ad entrambi i membri, otteniamo che p+p < p+r.
Da quest’ultima e dividendo entrambi i membri per 2, otteniamo che p+p
< p+r
, che
2
2
p+r
è equivalente a p < 2 . Poicé p ed r sono due valori interi, è immediato concluderec = q;
che p ≤ b p+r
2– analogamente, poiché p < r, allora, sommando r ad entrambi i membri, otteniamo
che p + r < r + r. Da quest’ultima e dividendo entrambi i membri per 2, otteniamo
che p+r< r+r, che è equivalente a p+r< r. Poicé b p+r
c ≤ p+r, per transitività
2
2
2
2
2
p+r
otteniamo che q = b 2 c < r.
A questo punto, abbiamo due casi, in relazione alla verità o meno della condizione allariga 3.
– Se la condizione è vera, allora k ≤ a[q]. In tal caso, l’assunzione della precondizione
afferma che l’array a[1..n] è ordinato e, se fosse vero che k ∈ a[p..r], allora sarebbe
necessariamente vero che k ∈ a[p..q]. L’algorimo procede chiamando ricorsivamente se stesso sulla sottosequenza da p a q (cioè eseguirà, alla riga 4, la chiamata
B IN S EARCH (A,K,P,Q)). La dimensione della sottosequenza passata a questa chiamata è strettamente minore di i. Infatti, essendo p ≤ q < r, è immediato verificare
che 0 ≤ q−p+1 < r−p+1. Per ipotesi induttiva, sappiamo che se k ∈ a[p..q], allora
la chiamata B IN S EARCH (A,K,P,Q) restituirà un valore z tale che a[z] = k. Poiché3 Correttezza di algoritmi ricorsivi

9

la chiamata originaria B IN S EARCH (A,K,P,R) restituisce lo stesso valore risultantedalla chiamata B IN S EARCH (A,K,P,Q), avremo quindi che il valore z restituito sarà
tale che a[z] = k, come richiesto.
– Se la condizione è falsa, allora k > a[q]. Analogamente al caso precedente, sefosse vero che k ∈ a[p..r], allora la precondizione garantirebbe anche la verità dik ∈ a[q + 1..r]. L’algorimo procede chiamando ricorsivamente se stesso sulla sottosequenza da q + 1 a r (cioè eseguirà la chiamata B IN S EARCH (A,K,Q+1,R) ariga 6). Anche in questo caso, la dimensione della sottosequenza passata a questa
chiamata è strettamente minore di i. Infatti, essendo p ≤ q < r, è immediato
verificare che 0 ≤ r − (q + 1) + 1 < r − p + 1. Per ipotesi induttiva, sappiamo che se k ∈ a[q + 1..r], allora la chiamata B IN S EARCH (A,K,Q+1,R) restituirà
un valore z tale che a[z] = k. Poiché, anche in questo caso, la chiamata originaria B IN S EARCH (A,K,P,R) restituisce lo stesso valore risultante dalla chiamata
B IN S EARCH (A,K,Q+1,R), avremo quindi che il valore z restituito sarà tale che
a[z] = k, come richiesto.
In conclusione, ogni volta che l’algoritmo viene chiamato su una partizione di dimensionei ≥ 0 che contiene al suo interno il valore k, esso restituirà un valore z tale che a[z] = k.
Questo completa la dimostrazione della proprietà di correttezza parziale.
Esercizi[3.1] Si consideri il seguente algoritmo ricorsivo:
Algorithm ARB-S EARCH (T,K)
1
2
3
4
5
6
7
8
9

if (T 6= Nil) thenif (T→Key < K) thenR = ARB-S EARCH (T→SX ,K)else if (T→Key > K) thenR = ARB-S EARCH (T→DX ,K)
else
R=T
else
R = Nilreturn R

Si formalizzi la seguente proprietà di correttezza: supposto T un ABR, se K è una chiave
presente in T, l’algoritmo restituisce il puntatore ad un nodo di T che ha come chiave K.
Si dimostri, successivamente, la correttezza dell’algoritmo per induzione sull’altezza diT.

3 Correttezza di algoritmi ricorsivi

10[3.2] Si consideri il seguente algoritmo ricorsivo:
Algorithm E XP -R IC (A,M)
1
2
3
4
5
6
7
8
9

if (M > 0) thenif Pari(M) then
B=A∗A
R = E XP -R IC (B,M/2)
else
R = A ∗ E XP -R IC (A,M-1)
else
R=1
return R

Si dimostri, per induzione sulla dimensione di M, che, se M è un intero non negativo (eA è in valore reale qualsiasi), l’algoritmo restituisce A elevato ad esponente M.
[3.3] Si consideri il seguente algoritmo ricorsivo:
Algorithm C OMB (N,K)
1
2
3
4
5
6

if (K = 1) thenreturn N
else if (N = K) thenreturn 1
else
return C OMB (N-1,K-1) + C OMB (N-1,K)

Assumendo N e K interi non negativi con K ≤ N, si dimostri, per induzione
sulla diNmensione di N, che l’algoritmo restituisce il coefficiente binomiale K . (Si ricorda cheNN!
= K!·(N-K)!
).
K[3.4] Si consideri il seguente algoritmo ricorsivo:
Algorithm P OLY (A[],D,X)
1

return P OLY-R IC (A[],D,X,0)

Algorithm P OLY-R IC (A[],D,X,R)
1
2
3
4

if D ≥ 0 thenreturn P OLY-R IC (A[],D-1,X,A[D] + X ∗ R)
else
return R

Assumendo che D sia un valore intero non negativo e che l’array A[] contenga i valori a0 ,a1 , . . ., aD nelle posizioni A[0], A[1], . . . , A[D], si dimostri che P OLY (A[],D,X) calD
X
cola e restituisce il valoreai · xi . A tal fine, si suggerisce di formulare un’opportunai=0

4 Correttezza di algoritmi iterativi e invarianti di ciclo11

proprietà di correttezza per l’algoritmo P OLY-R IC (A[],D,X,R), da dimostrare per induzione su D, e di utilizzare la correttezza di quest’ultimo per stabilire la correttezzadell’algoritmo principale.

4

Correttezza di algoritmi iterativi e invarianti di ciclo

Nei paragrafi precedenti abbiamo visto come sia possibile effettuare la dimostrazione di correttezza di semplici algoritmi, ricorsivi e non, tramite una traduzione delle operazioni e dei
costrutti di controllo (sequenze, condizionali e ricorsione) in relazioni matematiche. Vediamo ora come procedere nel caso in cui l’algoritmo contenga costrutti iterativi. Come visto
sopra, la traduzione delle operazioni e dei costrutti di controllo in relazioni matematicheconsiste nell’esprimere l’effetto delle operazioni tramite opportune relazioni tra le variabilidell’algoritmo prima e dopo l’operazione. In presenza di costrutti iterativi il problema sicomplica, poiché le operazioni presenti nel corpo del costrutto vengono eseguite un numero
variabile di volte, che spesso dipendente dall’input. Si noti che l’esecuzione di un costrutto
iterativo è equivalente alla ripetizione in sequenza delle operazioni presenti nel suo corpo.
Possiamo, quindi, tradurre ciascuna variabile assegnata all’interno del corpo in un insiemedi variabili matematiche, ciascuna associata ad una particolare esecuzione dell’assegnamentodella variabile. Ciò è dovuto al fatto che un assegnamento all’interno di un costrutto iterativo è eseguito ad ogni iterazione ed è quindi necessario associare alla variabile dell’algoritmo
in esso assegnata una diversa variabile matemarica per ogni sua esecuzione. Chiaramente,
il numero di variabili matematiche necessarie per ogni variabile è potenzialmente infinito (ilcostrutto iterativo potrebbe, infatti, non terminare). Consideriamo, ad esmepio, il seguente
algoritmo iterativo:

4 Correttezza di algoritmi iterativi e invarianti di ciclo

12

Algorithm A RRAY S UM (A[],N)
1
2
3
4
5
6

S=0
I=1
while (I ≤ N ) do
S = S + A[I]I = I+1
return S

È facile osservare che, se il valore del parametro N è non superiore alla lunghezza dell’array
A[], l’algoritmo calcola la somma di tutti i valori contenuti tra la posizione 1 e la posizione N
all’interno dell’array A[] fornito in ingresso, restituendo, alla terminazione dell’algoritmo, ilvalore di tale somma nella variabile S. Tale proprietà di correttezza può essere espressa comesegue:
Precondizione: N numero intero con 1 ≤ N ≤ length(A).



N
X
Correttezza

A[j].

Postcondizione: Se z = A RRAY S UM (A[],N), allora z =j=1

Seguendo le considerazioni fatte all’inizio di questa sezione, assoceremo alla veriabile S e alla
variabile I le variabili matematiche s0 , s1 , s2 , . . . e le variabili i0 , i1 , i2 , . . ., rispettivamente.
Utilizzeremo una variabile matematica A per rappresentare l’array (è sufficiente una singola
variabile, poiché l’array non viene mai modificato). Come per gli esempi precedenti, la variabile indicizzata con 0 (ad esempio s0 ) corrisponde al valore della variabile dell’algoritmo
prima di ogni assegnamento e, in generale, la variabile indicizzata con k (ad esempio sk )corrisponde al valore della variabile dell’algoritmo immediatamente dopo il k–esimo assegnamento. In particolare, s1 (i1 , rispettivamente) corrisponde al valore della variabile S (I, rispettivamente) immediatamente prima dell’inizio della prima esecuzione del ciclo while. Più ingenerale, per k ≥ 1, sk (ik , rispettivamente) corrisponde al valore della variabile S (I, rispettivamente) immediatamente prima dell’inizio della k–esima esecuzione del ciclo while. Si noti,
inoltre, che il numero di variabili matematiche associate a ciascuna variabile dell’algoritmo èpotenzialmente infinito.
Come già osservato, sebbene l’esecuzione di un costrutto iterativo sia equivalente alla ripetizione in sequenza delle operazioni presenti nel suo corpo, non è in genere possibile (néragionevole) eliminare il costrutto iterativo, traducendolo in una seuqenza di istruzioni e procedere alla dimostrazione come nel caso di assenza di costrutti iterativi. La lunghezza dellasequenza corrispondente non è, infatti, predicibile in generale e potrebbe addirittura essere
infinita. Al fine di dimostrare la proprietà di correttezza sopra riportata, procederemo riconducendola alla dimostrazione di altre proprietà più semplici, la dimostrazione delle quali èpiù agevole, e la verità delle quali implica la verità della proprietà di correttezza. Tali proprietà più semplici sono riconducibili a proprietà del ciclo contenuto nell’algoritmo. L’idea
di base è quella di individuare una o più proprietà che vengono preservate ad ogni iterazione

4 Correttezza di algoritmi iterativi e invarianti di ciclo13

del ciclo iterativo. Ad esempio, il ciclo while dell’algoritmo A RRAY S UM ad ogni nuova
iterazione aggiunge al valore corrente della variabile S il valore contenuto in una specifica
locazione dell’array A[]. Analogamente, ad ogni iterazione, la variabile I viene incrementata
di una unità. In altre parole, I conta le iterazioni eseguite del ciclo, indicando all’inizio diogni iterazione il numero dell’iterazione successiva da eseguire. All’inizio di ogni iterazione,
la variabile S contiene, invece, la somma di tutti i valori dell’array contenuti nelle locazioni
comprese tra la locazione 1 e la locazione corrispondente al valore dell’espressione
I − 1.
Pik −1
Quindi, all’inizio della k-esima iterazione avremo che ik = k e sk = j=1 A[j]. Possiamo,
quindi, esprimere queste due proprietà nel modo seguente: all’inizio dell’iterazione k–esimadel ciclo:k−1X
ik = k e sk =
A[j]
(1)
j=1

In generale, una proprietà che viene preservata ad ogni iterazione di un ciclo prende il nome
invariante di ciclo. Come vedremo, gli invarianti di ciclo, se correttamente individuati, sono
di grande utilità al fine di dimostrare la proprietà di correttezza di un algoritmo iterativo. Supponiamo, ad esempio, che la proprietà (1) sia effettivamente un invariante del ciclo whiledell’alagoritmo A RRAY S UM (A[],N). Supponiamo, inoltre, che il ciclo termini immediatamente prima della kf in -esima iterazione (kf in indica, cioè, il numero dell’iterazione del cicloprima della quale la condizione del while è falsa) . Sarebbe di conseguenza verificato che:kf in −1

skf in =X

A[j] e ikf in = kf in

j=1

Poiché i valori delle variabili ik (per ogni k ≥ 1) sono valori interi non negativi, affinché
kf in sia il valore d’indice per cui il ciclo termina, è necessario che all’inizio dell’iterazione diindice kf in la condizione del while sia falsa. In altre parole, vale ikf in > n. Per l’invariante,
avremmo che ikf in = kf in e, quindi, kf in > n sarebbe vero. Ora, essendo stato eseguito con
successo il ciclo di indice kf in − 1, la condizione del ciclo doveva essere vera all’inizio diquella iterazione, cioè kf in − 1 ≤ n. Poiché però kf in è un valore intero, le relazioni kf in > ne kf in − 1 ≤ n implicano necessariamente che valgano kf in = n + 1 e kf in − 1 = n. Sotto
kf in −1X
queste condizioni, la prima parte dell’invariante (skf in =A[j]) diventa equivalente a
j=1

skf in =nX

A[j]. Poiché skf in è proprio il valore ritornato dall’algoritmo, la proprietà dij=1

correttezza è, dunque, verificata. Ciò mostra che, se riusciamo a dimostrare che la proprietà
(1) è effettivamente un invariante del ciclo, allora (se l’algoritmo termina) il valore restituito è
proprio quello corretto.
Resta ora, quindi, solo da dimostrare la verità dell’invariante ipotizzato. Di seguito è riportato
l’algoritmo dell’esempio, in cui è stato inserito l’invariante (1) nel punto in cui si suppone sia

4 Correttezza di algoritmi iterativi e invarianti di ciclo14

sempre verificato (k indica l’iterazione corrente del ciclo).
Algorithm A RRAY S UM (A[],N)
1
2

S=0
I=1

3

while (I ≤ N ) do

INV: {sk =k−1XA[j] e ik = k}

j=1

4
5
6

S = S + A[I]I = I+1
return S

Dimostriamo ora che la proprietà (1) è effettivamente un invariante del ciclo, che, cioè, viene
preservato ad ogni iterazione. Poiché il numero di cicli eseguiti è un numero intero non negativo, è possibile dimostrare per induzione sul numero di cicli eseguiti che l’invariante è verificato. In altre parole, dimostrare che è un invariante di ciclo significa dimostrare che laseguente proprietà è verificata:∀k ≥ 1, sk =k−1
XA[j] e ik = k.

j=1

La dimostrazione è per induzione su k (con k ≥ 1).
• Caso base Il caso base individuato è per k = 1. Questo caso corrisponde al caso in cui
la prima esecuzione del ciclo deve ancora avvenire. Dall’algoritmo risulta
P evidente che
s1 = 0 e i1 = 1. La proprietà (1) per k = 1, che corrisponde a s1 = 0j=1 A[j] = 0 e
i1 = 1, è quindi verificata.
• Caso induttivo Assumiamo, per ipotesi induttiva, che la proprietà (1)Pvalga prima dell’inik−1
zio dell’esecuzione del ciclo k-esimo. Dunque, che valga sk =j=1 A[j] e ik = k.
L’esecuzione della k-esima iterazione comporta l’esecuzione, nell’ordine, delle istruzioni
S = S + A[I] e I = I+1. Queste istruzioni, espresse in termini di relazioni matematiche,
corrispondono a sk+1 = sk + A[ik ] e ik+1 = ik + 1, rispettivamente. Sostituendo leespressioni equivalenti per sk e ik derivanti dall’ipotesi induttiva, otteniamo:
sk+1 =k−1
X

A[j] + A[k] e ik+1 = k + 1

j=1

Pk−1
P
Ma sk+1 = j=1
A[j] + A[k] = kj=1 A[j]. Segue che, prima dell’inizio del (k + 1)–
esimo ciclo, la seguente proprietà sarà verificata:sk+1 =kX
j=1

A[j] e ik+1 = k + 1

4 Correttezza di algoritmi iterativi e invarianti di ciclo

15

Questo assicura che la proprietà (1) è preservata ad ogni iterazione e che, quindi, essa è uninvariante del ciclo while.
La dimostrazione appena svolta, insieme a quella fatta precedentemente sotto l’ipotesi di terminazione dell’algoritmo alla kf in -esima iterazione, garantisce che se l’algoritmo, chiamato
con un valore di N minore o uguale alla dimensione dell’array A[], termina, allora la postcondizione richiesta è certamente soddisfatta.
Esercizi[4.1] Si definisca la proprietà di correttezza del seguente algoritmo e la si dimostri utilizzando
l’invariante di ciclo riportato:
Algorithm A LG -3(M)
1
2

P=1
I=1

3

while (I < 2 ∗ M) do INV: {pk =k−1Y(2 · j − 1) e ik = 2 · k − 1}

j=1

4
5
6

P=P∗I
I=I+2
return P[4.2] Si definisca la proprietà di correttezza del seguente algoritmo e la si dimostri utilizzando
l’invariante di ciclo riportato:
Algorithm B IN S EARCH -I TER (X,A[],N)
1
2
3
4
5
6
7
8
9

I=1
J=N
 n 
while (I < J) do INV: {0 ≤ jk − ik ≤ 2k−1
e se x ∈ A, allora (A[ik ] ≤ x ≤ A[jk ])}P = b(I + J) / 2c
if (X > A[P]) then
I=P+1
else
J=P
return I[4.3] Si consideri il seguente algoritmo che calcola il valore corrispondente al primo argomento A elevato all’esponente dato dal secondo argomento M. La precondizione è che
M sia un valore intero non negativo e che A sia un valore reale. Si ne dimostri la correttezza, utilizzando l’invariante di ciclo riportato.
Algorithm E XP (A,M)
1
2
3
4
5

B=A
E=M
R=1
e
while (E > 0) do INV: {rk · bkk = am }if (E è dispari) then

5 Terminazione

16

6
R=R*B
7
E=E-1
8
else
9
B=B*B
10
E=E/2
11 return R[4.4] Si assuma che D sia un valore intero non negativo e che l’array A[] contenga i valori a0 ,a1 , . . ., aD nelle posizioni A[0], A[1], . . . , A[D] (con D ≤ length(A)). Si definisca laproprietà di correttezza del seguente algoritmo e la si dimostri utilizzando l’invariante di
ciclo riportato:
Algorithm P OLY-I TER (A[],D,X)
1
2

T=0
I=D

3

while (I ≥ 0) do INV: {ik = d − (k − 1) e Tk =4
5
6

T = A[I] + (X ∗ T)
I=I-1
return T

k−2X

xj · aik +j+1 }j=05

Terminazione

Nelle sezioni precedenti ci siamo concentrati sulla dimostrazione della correttezza parziale
di algoritmi iterativi e ricorsivi. Al fine di garantire la correttezza totale è però necessario
dimostrare la terminazione dell’algoritmo per ogni istanza di ingresso che soddisfi la precondizione. Iniziamo ricordando alcune proprietà che saranno utili allo scopo.
Proposizione 1: Ogni sequenza strettamente crescente di interi a1 < a2 < . . . an < . . . non
possiede estremo superiore. Analogamente, ogni sequenza strettamente decrescente di interi
a1 > a2 > . . . an > . . . non possiede estremo inferiore.
Si noti che un’immediata conseguenza della proposizione 1 è che, se la sequenza è strettamente crescente, per qualsiasi valore intero K esiste certamente un indice x tale che ax > K.
Proposizione 2: Ogni sequenza di interi a1 , a2 , . . . , an , . . . per la quale ak+1 − ak ≥  (con
 > 0) non possiede estremo superiore. Analogamente, Ogni sequenza di interi a1 , a2 , . . . , an , . . .
per la quale ak+1 − ak ≤  (con  < 0) non possiede estremo inferiore.
Vediamo ora un’applicazione di queste propreità nella dimostrazione di terminazione del semplice algoritmo iterativo A RRAY S UM della sezione precedente. L’algortmo termina solamente5 Terminazione

17

se termina il ciclo while in esso contenuto e quest’ultimo termina quando la sua condizione
I ≤ N risulta falsa, cioè non appena si verifica I > N . In termini di relazioni tra le variabilimatematiche che abbiamo utilizzato per descrivere l’esecuzione dell’algoritmo, ciò si verifica
se esiste un intero k̂ (che rappresenta il numero della prossima iterazione del ciclo) tale percui ik̂ > n. Dalla precondizione dell’algoritmo sappiamo che n è un numero intero compreso
tra 1 e length(A) e che i1 = 1. L’invariante che abbiamo dimostrato nella sezione precedente, garantisce che all’inizio di ogni iterazione k–esima, varrà ik = k. Abbiamo, inoltre,
dimostrato che ik+1 = ik + 1. Da questo possiamo concludere che le esecuzioni del ciclo
while generano, implicitamente, una sequenza di valori interi i1 , i2 , . . . , ik , . . . tale che:0 < i1 < i2 < . . . < ik < . . .
Questa è, appunto, una sequenza di interi strettamente crescente e, per la proposizione 1 nonammette estremo superiore. Segue che esisterà un valore k̂ tale che ik̂ > n. Cioè esisterà
un’iterazione del ciclo (in particolare proprio la k̂–esima) all’inizio della quale la condizionedel while sarà falsa e obbligherà il ciclo (e in questo caso anche l’algoritmo) a terminare.
Se consideriamo k̂ il primo valore per cui ik̂ > n, questo significa che ik̂−1 ≤ n. In altre
parole, ik̂−1 ≤ n < ik̂ = ik̂−1 + 1. Poicé n è in valore intero, necessariamente deve essere
n = ik̂−1 , quindi ik̂ = n + 1. Dall’invariante, otteniamo quindi che k̂ = n + 1. Quindi,
l’algoritmo termina immediatamente prima della (n + 1)–esima iterazione, cioè dopo aver
eseguito esattamente n iterazioni del ciclo.
Consideriamo ora l’algoritmo dell’esecizio [4.2]. La condizione di terminazione dell’algoritmo
corrisponde alla condizione di terminazione del ciclo while in esso contenuto (I ≥ J). Il valoredelle variabili I e J prima della prima iterazione sono rappresentati da i1 = 1 e j1 = n. Èpossibile dimostrare per induzione sulle iterazioni k ≥ 1 del ciclo che ad ogni iterazione ladifferenza jk − ik è sempre un valore intero e che decresce strettamente ad ogni iterazione. In
altre parole, detto dk = jk − ik (per ogni k ≥ 1), possiamo dimostrare che la sequenza:
d1 , d2 , . . . , dz , . . .
è una sequenza di interi strettamente decrescente, tale per cui dk+1 < dk . Per k = 1 è
immediato verificare che d1 = j1 − i1 = n − 1 è un valore intero. Supponiamo ora che
dk sia un valore intero e che venga eseguita la k–esima iterazione del ciclo. Ciò avvienese è verificata la condizione del while, cioè se ik < jk . Il primo assegnamento determinakpk+1 = b ik +jc. Da ik < jk , sommando ik ad entrambi i membri, possiamo dedurre che
2kik + ik < ik + jk . Dividendo entrambi i membri per 2 otteniamo che ik < ik +j. Poiché ik è
2
un valore intero per ipotesi induttiva, per la definizione di base di un numero, avremo che:
ik + jkik ≤2

5 Terminazione

18

In maniera simile, partendo da ik < jk e sommando jk ad entrambi i membri e dividendo ilkkkrisultato per 2, possiamo dedurre che ik +j< jk . Poiché b ik +jc ≤ ik +j
per definizione di222
base di un numero, otteniamo che:
ik + jk< jk
2
Possiamo, quindi, concludere che:ik ≤ pk+1 < jk
Si possono ora verificare due casi, a seconda del valore della condizione dell’if:• nel primo caso (x > A[pk+1 ]). In questo caso, viene eseguito l’assegnamento I = P + 1,
che determina ik+1 = pk+1 + 1, mentre il valore di J rimane invariato, cioè jk+1 = jk .
Quindi, dk+1 = jk+1 − ik+1 = jk+1 − (pk+1 + 1). dk+1 è chiaramente un valore intero.
Poiché jk+1 = jk , avremo anche che dk+1 = jk − (pk+1 + 1). Sappiamo, inotre, che
ik ≤ pk+1 , quindi ik < pk+1 + 1. Da ciò segue immediatamente che:dk+1 = jk − (pk+1 + 1) < jk − ik = dk
• nel secondo caso (x ≤ A[pk+1 ]). In questo caso, viene eseguito l’assegnamento J = P,
che determina jk+1 = pk+1 , mentre il valore di I rimane invariato, cioè ik+1 = ik . In
modo simile al caso precedente, avremo che dk+1 = jk+1 − ik+1 = pk+1 − ik+1 è unvalore intero. Poiché ik+1 = ik , avremo anche che dk+1 = pk+1 − ik . Essendo, però,
pk+1 < jk , possimo concludere che
dk+1 = pk+1 − ik < jk − ik = dk
In entrambi i casi, possiamo concludere che dk+1 è un intero strettamente minore di dk . Quindi,la sequenza d1 , d2 , . . . , dz , . . . è un sequenza strettamente decrescente di interi con dk+1 −dk ≤−1, per ogni k ≥ 1. Per la proposizione 2, questa sequenza non ha limite inferiore. Esisteràquindi un valore k̂ tale che dk̂ < 1. Cioè, tale che dk̂ = jk̂ − ik̂ ≤ 0, che implica jk̂ ≥ ik̂ . Se k̂è il primo valore tale che dk̂ < 1, seque che il ciclo terminerà proprio immediatamente primadell’iterazione k̂–esima. Concludiamo, quindi, che l’algoritmo termina sicuramente per ogni
valore di N .
Consideriamo ora il problema della terminazione di algoritmi ricorsivi. Per la natura stessadella ricorsione, è necessario dimostrare che ogni chiamata all’algoritmo deve generare solo
sequenze di chiamate ricorsive annidate che raggiungono uno dei casi basi della ricorsione. A
tal fine, è spesso sufficiente dimostrare che la dimensione dell’input passato a tutte chiamate
effettuate da una qualsiasi chiamata all’algoritmo sia strettamente inferiore di quello ricevuto
dalla chiamata. Sotto l’ipotesi di poter esprimere la dimensione dell’input tramite un valore6 Dimostrazione di correttezza dell’algoritmo di Insertion Sort

19

intero, dimostrare questa proprietà per induzione su tale dimensionde garantisce che tutte lesequenze di chiamate annidate generate da una qualche chiamata all’algoritmo determinano
una sequenza di valori interi (le rispettive dimensioni degli input ricevuti) che è strettamente
decrescente. Le proposizioni 1 o 2 sopra riportate possono quindi essere applicate per concludere che l’algoritmo termina.
Consideriamo l’algoritmo B IN S EARCH della sezione 3. Nella sezione 3 è stata presentata
una dimostrazione della correttezza parziale per induzione sulla dimensione della porzione
dell’array contenuta tra il valore P e il valore R. In altre parole, la quantità intera su cuiviene fatta induzione è i = r − p + 1. La dimostrazione stessa evidenzia che ogni chiamata all’algoritmo, per un qualche valore di i ≥ 0 o termina immediatamente (per i casibase i = 0, 1) o genera una chiamata ricorsiva con parametri interi p e q, o una chiamata con
parametri interi q + 1 e r. In entrambi i casi, abbiamo già osservato che le dimensioni dellesequanze individuate da questi valori interi sono strettamente minori di i ma sempre ≥ 0 (siveda la dimostrazione in sezione 3). La proposizione 1 garantisce che la sequenza di dimensioni passate alle chiamate annidate non ha limite inferiore, quindi dopo un numero finito dichiamate annidate, la dimensione dell’input risulterà essere uguale a 1 o a 0, garantendo laterminazione.
Esercizi[5.1] Si dimostri la terminazione degli algoritmi ricorsivi riportati agli esercizi [3.1], [3.2],[3.3] e [3.4]. Per la terminazione dell’esercizio [3.1], si consideri per dimensione dell’input
l’altezza dell’albero in ingresso. Si noti, inoltre, che la condizione T = N IL, corrispondente ad uno dei casi base, si verifica quando l’altezza dell’abero T è 0.
[5.2] Si dimostri la terminazione degli algoritmi iterativi riportati agli esercizi [4.1], [4.3] e[4.4].

6

Dimostrazione di correttezza dell’algoritmo di Insertion Sort

In questa sezione finale, proveremo ad applicare le tecniche illustrate con semplici esempi
nelle sezioni precedenti alla dimostrazione di correttezza dell’algoritmo di ordimamento Insertion Sort. Di seguito è riportata la versione dell’algoritmo che analizzeremo:
Algorithm I NSERTION S ORT (A)12
3

for j := 2 to length(A) do INV1: {jk = k + 1 e Ak [1..k − 1] è permutazione ordinata di A1 [1..k]}key := A[j]i := j-1

6 Dimostrazione di correttezza dell’algoritmo di Insertion Sort

4
5
6
7

20



(k,z)
[jk − z + 1..jk ] ≥ keyk eik,z = jk − z e Awhile (i > 0 and A[i] > key) do INV2: A(k,z) [jk − z + 2..jk ] = A(k,1) [jk − z + 1..jk − 1] e (k,z)A[1..jk − z + 1] = A(k,1) [1..jk − z + 1]
A[i+1] := A[i]i := i -1
A[i+1] := key

dove Ak [] si riferisce alla configurazione dell’array A[] immediatamente prima della k-esima
esecuzione del ciclo for (A1 [] si riferisce alla configurazione iniziale dell’array, prima dellaprima esecuzione del ciclo for). Analogamente, jk denota il valore della variabile J primadella k-esima esecuzione del ciclo for. A(k,z) [] si riferisce alla configurazione dell’array durante la k-esima esecuzione del ciclo for e prima della z-esima esecuzione del ciclo while,
mentre ik,z denota il valore della variabile J durante la k–esima iterazione del for e primadella z-esima esecuzione del ciclo while. Per comodità, la notazione A[p..r] ≥ k verrà usata
come abbreviazione per la proprietà ∀p ≤ x ≤ r. A[x] ≥ k. Assumendo che Af in [] denoti la configuarazione dell’array al termine dell’algoritmo, possiamo esprimerela proprietà dicorrettezza come segue:

CorrettezzaPrecondizione: L’array A contiene valori numerici (interi o reali).
f indell’array A
Postcondizione: Se n = length(A) e A è la configurazione
al termine dell’algoritmo, allora ∀1 ≤ x < n. Af in [x] ≤ Af in [x + 1].

Come si può notare, l’algoritmo contiene due cicli annidati uno nell’altro. Al fine di procedere
alla dimostrazione di correttezza, dovremmo individuare un’opportuna proprietà invariante per
ciascun ciclo. L’idea di fondo dell’algoritmo è quella che, a ogni iterazione del ciclo for, laporzione ordinata dell’array viene estesa di una locazione, a partire dalla locazione 2 fino altermine dell’array (length(A)). Inizialmente, tale porzione ordinata è quella che contiene
la sola locazione 1. Questa proprietà è formalizzata dall’invariante INV1 riportato all’internodell’algoritmo.
Per quel che concerne il ciclo while interno, il suo ruolo è quello di traslare in avanti diuna posizione tutte le locazioni della porzione ordinata dell’array che contegono elementipiù grandi del valore key (originariamente contenuto nella locazione con la quale si vuole
estendere la porzione ordinata nel nuovo ciclo for). L’invariante INV2 riportato nell’algoritmo
esprime formalmente tale proprietà.
Dimostreremo la correttezza dell’algoritmo, dimostrando che gli invarianti di ciclo sono validi
e infine trarremo la conclusione da ciò che essi implicano alla terminazione dei cicli.
La dimostrazione dell’invariante INV1 è, come prevedibile, per induzione sul valore k ≥ 1
che indica la prossima esecuzione del ciclo. È immediato verificare che INV1 è verificate
immediatamente prima della prima dell’esecuzione del ciclo (caso base), in quanto k = 1,

6 Dimostrazione di correttezza dell’algoritmo di Insertion Sort

21

j1 = 2 = k + 1 e la porzione di array che va dalla locazione 1 alla locazione j1 − 1 = 1
contiene un solo elemento ed è quindi ordinata.
Al fine di mostrare che l’invariante INV1 è mantenuto durante ogni esecuzione del ciclo for(caso induttivo), è necessario esaminare il corpo del ciclo. Deve valere che, all’inizio dellak–esima esecuzione del for, Ak [1..k] è una permutazione ordinata di A1 [1..k]. Per mostrareciò sarà necessario esaminare prima l’invariante INV2. Dimostreremo questo invariante per
induzione sul numero z di esecuzioni del ciclo while, fissando un arbitrario valore di k ≥ 1.
L’invariante 2 è verificato ad ogni sua inizializzazione z = 1 (caso base), per qualsiasi k ≥ 1.
Infatti ik,1 = jk − 1 = jk − z, keyk = A(k,1) [jk ] e A(k,1) [ik,1 ] > keyk (condizione del while).
Quindi, per ogni elemento x contenuto A(k,1) [jk − z + 1..jk ], vale che x ≥ keyk . Poiché
non è ancora stata eseguita alcuna iterazione del ciclo while per il k dato, il terzo e quarto
congiuto dell’invariante INV 2 sono banalmente verificato. Infatti, per z = 1, gli intervalli[jk −z +2..jk ] e [jk −z +1..jk −1] sono entrambi vuoti, mentre il quarto congiunto corrisponde
proprio all’uguaglianza A(k,1) [1..jk ] = A(k,1) [1..jk ].
Ogni iterazione z > 1 del while mantiene questo invariante (caso induttivo). Infatti:• se viene eseguito il corpo del for, allora deve valere A(k,z) [ik,z ] > keyk . Per ipotesi
induttiva, ik,z = jk − z, ergo A(k,z) [jk − z] > keyk .
• l’assegnamento A[i+1] := A[i] sposta il valore di A in posizione ik,z = jk − z (chesi sa essere > keyk ) in posizione ik,z + 1 = jk − z + 1, lasciando tutti gli altri emenenti
inalterati. Quindi, valgono le seguenti:
A(k,z+1) [1..jk − z] = A(k,z) [1..jk − z]
A(k,z+1) [jk − z + 1] = A(k,z) [jk − z]
A(k,z+1) [jk − z + 2..jk ] = A(k,z) [jk − z + 2..jk ]• da A(k,z+1) [1..jk −z] = A(k,z) [1..jk −z] e dall’ipotesi induttiva che garantisce A(k,z) [1..jk −z] = A(k,1) [1..jk − z], otteniamo:
A(k,z+1) [1..jk − z] = A(k,1) [1..jk − z](2)• da A(k,z+1) [jk − z + 1] = A(k,z) [jk − z] e A(k,z+1) [jk − z + 2..jk ] = A(k,z) [jk − z + 2..jk ],e dall’ipotesi induttiva che garantisce A(k,z) [jk − z + 2..jk ] = A(k,1) [jk − z + 1..jk − 1]e A(k,z) [jk − z] = A(k,1) [jk − z], otteniamo:
A(k,z+1) [jk − z + 1..jk ] = A(k,1) [jk − z..jk − 1](3)

• inoltre, sempre per ipotesi induttiva, A(k,z+1) [jk −z+2..jk ] = A(k,z) [jk −z+2..jk ] ≥ keykche, insieme a A(k,z+1) [jk − z] = A(k,z) [jk − z] > keyk (per la condizione del while e

6 Dimostrazione di correttezza dell’algoritmo di Insertion Sort

22

A(k,z+1) [jk − z + 1] = A(k,z) [jk − z] (per effetto dell’assegnamento), otteniamo che:A(k,z+1) [jk − z..jk ] ≥ keyk

(4)

• infine, dopo l’assegnamento i := i-1, avremmo che ik,z+1 = ik,z − 1 = jk − z − 1 =jk − (z + 1).
È immediato quindi verificare che (2), (3) e (4) sono equivalenti, rispettivamente, alle seguenti:
A(k,z+1) [1..jk − (z + 1) + 1] = A(k,1) [1..jk − (z + 1) + 1]
A(k,z+1) [jk − (z + 1) + 2] = A(k,1) [jk − (z + 1) + 1..jk − 1]
A(k,z+1) [jk − (z + 1) + 1..jk ] ≥ keyk

(5)
(6)
(7)

Queste tre equazioni, insieme al fatto che ik,z+1 = jk − (z + 1), garantiscono che l’invarianteè sempre verificato all’inizio della (z + 1)–esima iterazione del ciclo while.
Utilizzando le considerazioni della sezione 5, possiamo concludere che il ciclo while termina sicuramente per qualsiasi k ≥ 1. Infatti, il valore iniziale di i, ik,1 è posto a jk che ècertamente un valore intero positivo. Ad ogni iterazione del ciclo, i viene decrementata di
una unità, generando una sequenza strettamente decrescente di numeri interi. Quindi, se nontermina prima a causa della condizione A[i] ≤ key, il ciclo while termina poiché, per laproposizione 1, esiste almeno un valore di z per cui ik,z ≤ 0. Possiamo quindi concludereche. al termine del ciclo, i sarà certamente maggiore o uguale a 0. È facile convincersi che ilpirmo valore di z per cui si verifica ik,z ≤ 0, è quello per cui ik,z = 0.
Sia ẑ il valore di z per cui il ciclo termina. Avremo, quindi, che ik,ẑ ≥ 0. Ne segue che:
• Ak,ẑ [jk − ẑ + 2...jk ] = Ak,1 [jk − ẑ + 1...jk − 1]
• Ak,ẑ [jk − ẑ + 1..jk ] ≥ keyk (= Ak,1 [jk ])• i = 0 oppure Ak,ẑ [jk − ẑ] ≤ keyk (condizione d’uscita del while)• Ak,ẑ [1..jk − ẑ] = Ak,1 [1..jk − ẑ]
Possiamo ora procedere a dimostrare il caso induttivo per l’invariante INV1 del ciclo for.
Assumendo l’invariante vero immediatamente prima della k-esima iterazione del for, avremo
che Ak [1..jk − 1] = Ak,1 [1..jk − 1] è una permutazione ordinata di A1 [1..jk − 1]. La variabile
keyk è uguale a Ak [jk ] e ik,z a jk − 1.
Al termine del ciclo while, viene eseguito l’assegnamento A[i+1] = key, che modifica
solo la locazione ik,ẑ + 1 = dell’array Ak,ẑ . Ciò determina la configurazione Ak+1 dell’array
immediatamente prima dell’inizio dell’esecuzione (k+1)–esima del ciclo for. Avremo quindi:

6 Dimostrazione di correttezza dell’algoritmo di Insertion Sort

23

1. Ak+1 [jk − ẑ + 2..jk )] = Ak [jk − ẑ + 1..jk − 1]
2. Ak+1 [jk − ẑ + 1] = keyk (assegnamento A[i+1] = key)
3. Ak+1 [jk − ẑ + 2..jk ] ≥ keyk (= A1 [jk ])4. Ak+1 [1..jk − ẑ] = Ak [1..jk − ẑ]
5. jk − ẑ = 0 oppure jk − ẑ > 0 e Ak [jk − ẑ] ≤ keyk (condizione di terminazione del while)
Da 1. e dall’ipotesi induttiva abbiamo che Ak+1 [jk − ẑ + 2..j] è ordinata, da 4. e dall’ipotesi
induttiva abbiamo che Ak+1 [1..jk − ẑ] è ordinata. Consideriamo i due casi previsti dalla 5.:• se jk − ẑ = 0 allora jk − ẑ + 1 = 1 e jk − ẑ + 2 = 2. In questo caso, la 3. è equivalente
a Ak+1 [2..jk ] ≥ keyk e la 2. a Ak+1 [1] = keyk . È immediato verificare che Ak+1 [1..jk ] è
quindi una permutazione ordinata di Ak [1..jk ] e, per ipotesi induttiva, anche di A1 [1..jk ]come desiderato;• se jk − ẑ > 0 allora Ak [jk − ẑ] ≤ keyk . Quindi, vale che Ak [jk − ẑ] = Ak+1 [jk − ẑ] ≤keyk ≤ Ak+1 [jk − ẑ + 2..jk ]. Ancora una volta, possiamo concludere che Ak+1 [1..jk ] è
quindi una permutazione ordinata di Ak [1..jk ] e, per ipotesi induttiva, anche di A1 [1..jk ]come desiderato;
Poichè jk = k + 1 e j viene incrementato prima dell’inizio della prossima iterazione, in
entrambi i casi ottemiamo che all’inizio della (k + 1)–esima iterazione del ciclo for, jk+1 =(k+1)+1 e Ak+1 [1..k+1] è una permutazione ordinata di A1 [1..k+1], cioè proprio l’invariante
INV1.
Questo completa la dimostrazione dell’invariante INV1.
Si noti che il ciclo for termina sicuramente per quel valore di k = f in tale che jf in = n+1. Per
la validità dell’invariante avremo, quindi, che f in = n e che ”Af in [1..n] è una permutazione
ordinata A1 [1..n]”, che completa la dimostrazione di correttezza dell’algoritmo.