Preuve d`algorithme.

publicité
Preuve d’Exactitude d’un Algorithme
1- Invariant de Boucle :
Un invariant de boucle est une propriété vérifiée tout au long de l'exécution de la
boucle. Il est intéressant lorsqu'il permet de conclure l'exactitude de l'algorithme au moment
de sa terminaison.
Exemple :
Soit la boucle en C suivante :
for(i = 1 ; i <= n ; i++)
for(j = n ; j >= i ; j--)
printf(“%d %d”, i, j);
On peut donc extraire plusieurs invariants de boucle qui maintiennent leur validités au
long de l’exécution de la boucle :
-
1 < 2
-
i ≤ n
-
1 ≤ j
-
i ≤ j
2- Preuve d’Exactitude :
Une preuve d’exactitude d’un algorithme par invariant de boucle utilise la démarche
suivante :
1-
Nous prouvons tout d’abord que l’algorithme s’arrête en montrant qu’une
condition d’exécution de boucle finit par ne plus être réalisée.
2-
Nous exhibons alors un invariant de boucle, c’est-à-dire une propriété P qui,
si elle est valide avant l’exécution d’un tour de boucle, est aussi valide après
l’exécution du tour de boucle.
3-
Nous vérifions alors que les conditions initiales rendent la propriété P vraie
en entrée du premier tour de boucle. Nous en concluons que cette propriété
est vraie en sortie du dernier tour de boucle. Un bon choix de la propriété P
prouvera qu’on a bien produit l’objet recherché. La difficulté de cette
méthode réside dans la détermination de l’invariant de boucle. Quand on l’a
trouvé il est en général simple de montrer que c’est bien un invariant de
boucle.
Donc, après qu’on détermine l’invariant de la boucle, on établie une démonstration
(similaire que celle par récurrence) que notre algorithme est correct comme suit :
1- 1ère étape : Initialisation : On montre que l’invariant est valide avant la première
itération de la boucle.
2- 2ème étape : Maintenance : On suppose que l’invariant est vrai avant l’exécution de la
ième itération, est on montre qu’il reste valide après l’exécution de cette itération.
3- 3ème étape : Terminaison : Lorsque la boucle se termine, l’invariant nous donne une
propriété utile pour nous aider à démontrer que l’algorithme est correct.
Exemple :
Soit l’algorithme « Tri par Insertion » suivant :
for(i = 2 ; i <= n ; i++) {
cle = A[i];
/* clé à insérer dans A[1..i-1]*/
j = i -1;
while ((j > 0) && (A[j] > cle)) {
A[j + 1] = A[j];
j = j – 1;
}
A[j + 1] = cle;
}
Supposons que la tableau A lorsque i = 5 était comme suit :
1
2
2
3
3
7
4
12
5
6
6
1
7
10
8
13
On veut donc insérer la clé 6 dans le sous-tableau [2, 3, 7, 12] :
A[4] = A[5]
1
2
2
3
3
7
4
12
5
12
6
1
7
10
8
13
4
7
5
12
6
1
7
10
8
13
A[3] = A[4]
1
2
2
3
3
7
A[3] = cle = 6
1
2
3
4
5
6
7
8
2
3
6
7
12
1
10
13
Maintenant, on veut démontrer l’exactitude de cet algorithme en général. Il faut donc
choisir un bon invariant de boucle qui nous aidera à démontrer l’exactitude.
Alors, soit l’invariant suivant : « A[1..i-1] est trié » .
Initialisation : Au début de l’exécution de l’algorithme de tri par insertion, et lorsque i = 2,
le sous-tableau A[1..i-1] contient un seul élément A[1]. Alors, cet évident qu’un tableau
d’un seul élément est trié.
Maintenance : Supposons qu’avant l’exécution de l’itération pour i que l’invariant était
valide (A[1..i-1] est trié). On veut montrer qu’à la fin d’exécution de cette itération, et
lorsque i s’incrémente, l’invariant restera toujours valide ;i .e. A[1..i] est trié.
On a donc A[1..i-1] trié avant l’exécution de la boucle interne. Il faut ici
démontrer qu’après l’exécution de la boucle interne que le nouveau sous-tableau A[1..i]
est trié. On peut facilement fixer un deuxième invariant pour la boucle interne comme suit :
« A[1..j] est trié
ET A[j+1..i] est trié
ET tous les éléments du A[j+1..i]sont supérieurs ou égale à cle»
(il reste comme exercice à domicile)
Terminaison : A la fin d’exécution de l’algorithme, l’invariant reste toujours valide
( A[1..i-1] est trié). On sait aussi que la valeur de i pour qu’on puisse sortir de la boucle
doit être égale à n+1. Alors, à la fin d’exécution on a A[1..n]trié.
Téléchargement