• Aucun résultat trouvé

Etant donné qu’il est, en pratique généralement impossible de tester complètement le bon fonc- tionnement d’un algorithme, il faut se rabattre sur des méthodes analytiques pour le vérifier.

Essayons de prouver que le calcul du pgcd par la méthode d’Euclide, donné à la figure suivante donne le bon résultat en supposant qu’au départ x >= 0 et y >0.

def pgcd(x, y):

""" calcule le pgcd(x,y)"""

while (y > 0):

x,y = y,x % y

return x

Deux problèmes doivent être résolus. Il faut s’assurer:

1. que pour tout couple de données (x,y) possible la fonction pgcd se termine, 2. que si cette fonction se termine, elle donne le bon résultat.

Nous dirons que pour faire la preuve totale d’un algorithme, il faut vérifier le point 2., c’est-à- dire en faire sa preuve partielle et prouver le point 1., c’est-à-dire vérifier la terminaison. Pour un algorithme sans boucle ni appel de fonction, la preuve totale est relativement simple puisque l’on peut suivre, instruction par instruction, son évolution en découpant les différents cas possibles suivant les tests effectués tout au long de son exécution.

Le problème provient des boucles où le même code peut être utilisé plusieurs fois, le nombre de répétitions variant suivant les données.

La formule logique qui décrit la situation des boucles est appelée invariant. Intuitivement, l’invariant d’une while (ou d’un for) donne l’état du programme chaque fois que l’on effectue le test associé (que le résultat du test soit vrai ou faux). Ainsi la formule donnée dans l’exemple de la recherche séquentielle est un invariant. Formellement, pour que cette formule soit un invariant correct trois conditions doivent être remplies:

Warning: Pour effectuer la preuve partielle d’une boucle, il faut tout d’abord trouver un invariant de boucle c’est-à-dire une affirmation ou assertion vraie à chaque fois que l’exécution du programme atteint le début de chaque itération de la boucle et qui permet de déduire le résultat voulu, si l’exécution de la boucle while se termine.

Une formule I est un invariant si:

1. I est vrai au début de l’exécution de la boucle while au moment où l’exécution du programme atteint cet endroit.

2. Ayant I vrai et la condition C de continuation du while également vraie (et donc que le corps du while est évalué une fois de plus), il faut pouvoir vérifier qu’après l’exécution d’une itération du corps la boucle while, l’invariant I est à nouveau vrai.

3. Enfin pour effectuer la preuve partielle, cet invariant, composé avec le fait que la condition C de continuation du while devienne fausse, doit permettre de vérifier que la boucle while a effectivement ‘’calculé” le résultat R qui lui était demandé.

Essayons de dégager une interprétation intuitive de la notion d’invariant. Si l’on regarde l’ensemble des situations dans lesquelles se trouve un algorithme au début de chaque itéra- tion d’une boucle, on remarque que les situations évoluent tout en ayant des points communs. Effectivement, la boucle est une façon de réaliser de façon itérative un certain traitement qui ne peut être réalisé en une fois. L’invariant sera généralement une façon d’exprimer que le résultat escompté(à la terminaison de la boucle) est égal à ce qui a déjà été réalisé lors des précédentes itérations de la boucle plus ce qui reste encore à réaliser. Au début de la boucle, ce qui est

réalisé est vide, on aura alors Résultat = Ce qui reste à réaliser. A la terminaison de la boucle, la condition d’arrêt de la boucle permet de vérifier que ce qui reste à réaliser est vide. On aura alors Résultat = Ce qui est réalisé et la preuve partielle pour la boucle sera réalisée.

Formellement pour un code while C: instruction , les trois conditions suivantes doivent être vraies :

code avant le while⇒ I

I∧ C + corps du while ex´ecute une it´eration ⇒ I I∧ ¬C ⇒ R

Pour exprimer l’invariant de la boucle while de la fonction pgcd, il faut utiliser des notations permettant de distinguer les valeurs initiales de x et de y de leur valeur courante.

Nous dénoterons x_i , y_i les valeurs respectivement de x et y après la ième itération, (initiale- ment x = x_0 , y = y_0) et x, y leur valeur courante.

L’invariant vaut alors:

Invariant: (pgcd(x0, y0) = pgcd(x, y))∧ (x ≥ 0) ∧ (y ≥ 0)

Cette formule exprime le fait que pour calculer pgcd(x_0 , y_0 ) il reste à calculer pgcd(x,y) (ici on peut oublier ce qui a déjà été réalisé). Le progrès provient du fait qu’il est plus simple de calculer pgcd(x,y) que pgcd(x_0 , y_0 ).

Vérifier un invariant se fait par récurrence. Pour le pgcd,

• Base: : Initialement, l’invariant est trivialement vrai puisque x=x_0 et y=y_0 avec x_0 et y_0 >= 0.

• Induction:: Si la formule est vraie au début de la ième itération et que la boucle ef- fectue une itération supplémentairealors l’invariant est vrai au début de la i+1ème itération.

En effet, on peut montrer que

x > 0∧ y > 0 ⇒ pgcd(x, y) = pgcd(y, x % y) De plus

x > 0∧ y > 0 ⇒ x %y ≥ 0

Et donc, étant donné qu’après chaque itération, les nouvelles valeurs de x et y valent respectivement l’ancienne valeur de y et l’ancienne valeur de x modulo l’ancienne valeur de y (ce que l’on peut noter x_i = y_(i-1) , y_i = x_(i-1) % y_(i-1)) on voit que l’invariant est vrai au début de la i+1ème itération.

Pour compléter la preuve partielle il faut montrer que l’invariant et la terminaison implique le résultat.

En effet:

(pgcd(x0, y0) = pgcd(x, y))∧ (x ≥ 0) ∧ (y ≥ 0) ∧ (y ≤ 0) ⇒ pgcd(x0, y0) = x

Enfin, pour compléter la preuve totale il faut encore montrer que la boucle se termine effective- ment.

Ayant x_0 > 0 et y_0 >= 0 on peut voir qu’à chaque itération, la valeur (entière) y diminue d’au moins une unité. De ce fait le test (y > 0) sera assurément faux un moment donné.

Fonction de terminaison

En général, on vérifie qu’une boucle se termine toujours en mettant en évidence une fonction de terminaison, c’est-à-dire une fonction f à valeur entière qui dépend de la valeur des variables et telle que:

• la valeur de la fonction décroît strictement d’au moins une unité à chaque itération; • si l’invariant de la boucle est vrai, f est positif ou nul.

L’invariant étant par définition vrai au début de chaque itération de la boucle, on est alors assuré de la terminaison de celle-ci. Comme pour la recherche d’un invariant correct, la recherche d’une telle fonction de terminaison ne possède pas de méthode systématique.