Preuves de propriétés et de terminaison de programme |
La vérification de propriétés de programmes utilise largement les raisonnements par induction. L'ambition n'est pas ici de faire une théorie des preuves de programmes mais simplement de montrer sur quelques exemples l'utilisation des raisonnements par induction dans ce domaine. Essentiellement, la récurrence est utile tant dans les boucles itératives que dans les procédés récursifs (procédures, fonctions, clauses . . . ).
Considérons le programme suivant :
- PROGRAMME carré
- VAR a,b,c : entier
- DÉBUT
- LIRE a
- SI a < 0 ALORS a := -a FINSI
- b := a
- c := O
- TANT QUE b > 0 FAIRE
- c := c + a
- b := b - 1
- FIN TANTQUE
- AFFICHER c
- FIN
Nous allons montrer que ce programme calcule et affiche a2. La première chose à vérifier est que l'on finira par sortir de la boucle. On procède comme suit
Avant d'entrer dans la boucle, la valeur de b est un entier positif ou nul.
La valeur de b est décrémentée à chaque passage dans la boucle. Cette valeur finira donc par s'annuler.
Lorsque la valeur de b s'annule, on sort de la boucle.
On vient de prouver la terminaison de ce programme.
Un fois que l'on a prouvé que la boucle se termine, il faut s'assurer
que le résultat est celui escompté. Pour ce faire, on montre
que la propriété I(a, b, c):
"a2 = c + b * a"
est vraie à chaque passage dans la boucle. Notons bn
et cn les valeurs des variables b et c après
le n-ième passage dans la boucle. Soit P(n) la
propriété
"a2 = cn + bn * a".
Il s'agit donc de vérifier que pour tout n dans N,
P(n). On procède par récurrence.
On a bo = a et c0 = 0 donc P(0) est vraie.
Soit n dans N, supposons P(n). On a bn+1 = bn-1 et Cn+1 = Cn + a. On en déduit :
Cn+l+bn+l *a=cn+a+(bn-1)*a=cn+bn*a=a2
ce qui prouve que P(n + 1) est vraie.
Lorsqu'on sort de la boucle on a donc à la fois b = O et I(a,b,c). Donc a2 = c + O * a = c ce qui prouve que le résultat du programme est a2.
De façon plus générale, considérons
la boucle suivante où B désigne une expression booléenne
et S une suite d'instructions
TANT QUE B FAIRE S FINTANTQUE
Pour montrer que la boucle termine, on lui associe une
expression à valeurs dans un ensemble bien fondé. On montre
ensuite que l'exécution de S fait décroître
strictement la valeur de cette expression. Comme il n'y a pas de suite infinie
strictement décroissante dans un ensemble bien fondé, on en
déduit que la boucle se termine.
Pour montrer qu'une propriété P
est vérifiée à la sortie de la boucle, on procède
comme suit. On introduit une propriété annexe I appelée
invariant de boucle. On montre ensuite que I est vérifiée
avant l'entrée dans la boucle et qu'elle est conservée à
chaque passage dans la boucle (c'est-à-dire si I est vraie
avant l'exécution de S elle reste vraie après). Ceci constitue
une preuve par récurrence du fait que la propriété
I est vérifiée quel que soit le nombre de passages
dans cette boucle. Finalement, on prouve que
¬B et I implique P
Puisque la propriété I est vraie après chaque passage dans la boucle, que l'on a prouvé que la boucle termine et qu'à ce moment B est fausse, ceci prouve bien que la propriété P est vraie à la sortie de la boucle.
EXERCICE : Montrer que le programme puissance
se termine et affiche comme résultat an (avec la convention 00
= 1).
PROGRAMME puissance VAR a,n,r : entier DÉBUT LIRE a,n SI n < O et a = O ALORS AFFICHER résultat indéfini SINON r := 1 TANT QUE n < O FAIRE r := r / a n := n+1 FIN TANTQUE TANT QUE n > O FAIRE r := r * a n := n-1 FIN TANTQUE AFFICHER r FIN SI FIN
Le cas des programmes récursifs est légèrement plus compliqué car il peut y avoir plusieurs appels récursifs dans un même programme et il peut y avoir aussi plusieurs cas où le résultat s'obtient sans appel récursif. Cependant, il se traite de façon analogue. Considérons la procédure suivante qui affiche l'écriture infixe d'un arbre binaire. On rappelle que la notation infixe (comme la notation polonaise inversée en est une autre, voir HP), la notation telle que l'opérateur est entre ses arguments.
Pour prouver sa terminaison, on considère par exemple la fonction
h: AB N qui donne la hauteur d'un arbre
binaire. La valeur de h(x) décroit strictement à
chaque appel récursif, c'est-à-dire
h(filsGauche(x) < h(x)) et h(filsDroit(x)) < h(x).
Puisqu'il n'y a pas de suite infinie strictement décroissante dans N (voir ensembles bien fondés), on en déduit qu'il n'y a qu'un nombre fini d'appels récursifs. Par conséquent, la procédure infixe se termine toujours.
Il faut remarquer que le choix de la fonction h est arbitraire.
On aurait aussi bien pu choisir la fonction n:AB N qui donne
le nombre de noeuds d'un arbre ou même la fonction id: AB
AB. En effet, la relation "être sous-arbre de"
est une relation d'ordre bien fondée
sur l'ensemble des arbres binaires AB. De plus, elle vérifie
filsGauche(x) < x et filsDroit(x) < x ce qui prouve que la procédure
infixe se termine.
De façon générale, pour prouver la terminaison d'un programme récursif on lui associe une expression à valeur dans un ensemble bien fondé. Le plus souvent, cette expression est uniquement fonction des arguments du programme récursif. Soit V la valeur de l'expression appliquée aux arguments du programme (V = h(x) dans l'exemple ci-dessus). Il faut alors vérifier que pour chaque appel récursif, la valeur de cette expression pour les paramètres de l'appel (h(filsGauche(x)) et h(filsDroit(x)) dans l'exemple ci-dessus) est strictement inférieure à V. On peut alors conclure que le programme termine.
De même, pour prouver une propriété d'un programme récursif, on lui associe une propriété P qui relie les arguments du programme à son résultat. On montre alors directement que P est vérifiée dans tous les cas où le programme se termine sans appel récursif. Puis en supposant la propriété vraie pour tous les appels récursifs du programme on prouve qu'elle est encore vraie à la fin du programme. Il s'agit donc encore d'une preuve par induction du fait que P est vraie à la fin du programme quels qu'en soient les arguments.
Considérons par exemple la fonction suivante:
Montrons que n > 0, cette fonction calcule
an. On considère donc la propriété P: "puissance(a,n)
= an" et on prouve qu'elle est vérifiée au
retour de la fonction.
Il y a deux cas où la fonction retourne directement une valeur: si n = 0, le résultat est alors 1 = a0 et si n = 1, le résultat est alors a = a1. La propriété P est donc vérifiée dans ces deux cas.
Si n est pair, le résultat est puissance(a * a, n/2). Or, par l'hypothèse d'induction on a puissance (a * a, n/2) = (a * a)n/2 = an. La propriété P est donc encore vérifiée.
De même, si n est impair, le résultat est a * puissance(a*a,(n- 1)/2). Par l'hypothèse d'induction on a
puissance(a * a, (n - 1)/2) = (a * a)(n-1)/2 = an-1.
On en déduit que le résultat est a * an-1 = an et la propriété P est encore vérifiée.
CQFD.
EXERCICE. Montrer que n
N, I'appel
Fact(n) de la fonction Fact définie ci-dessous,
se termine et calcule n!