Exercice 2 : Invariants de boucle
Concours MP2I - Informatique
Section titled “Concours MP2I - Informatique”Correction détaillée : Invariants de boucle et Logique de Hoare
Section titled “Correction détaillée : Invariants de boucle et Logique de Hoare”Introduction et Rappels de Cours
Section titled “Introduction et Rappels de Cours”Dans le cadre de la vérification de programmes impératifs, la méthode de Hoare repose sur l’utilisation de triplets de Hoare notés , où est la précondition, un bloc de code et la postcondition.
Pour une boucle while B do C, la preuve de correction partielle et totale repose sur la notion d’invariant de boucle :
- Initialisation : L’invariant doit être vrai avant d’entrer dans la boucle ().
- Conservation : Si l’invariant est vrai et que la condition de boucle est vérifiée, l’exécution du corps de la boucle maintient l’invariant ().
- Terminaison : À la sortie de la boucle, si l’invariant est vrai et la condition fausse, la postcondition est établie (), et une fonction de variant (à valeurs dans un ensemble bien fondé, typiquement ) doit strictement décroître à chaque itération.
Partie I : Étude d’un algorithme de calcul arithmétique
Section titled “Partie I : Étude d’un algorithme de calcul arithmétique”On considère la fonction suivante, écrite en syntaxe algorithmique proche de OCaml/C, qui calcule le produit de deux entiers naturels et en utilisant uniquement des additions, des décalages de bits (divisions par 2) et des tests de parité. Cet algorithme est connu sous le nom de multiplication russe (ou éthiopienne).
// Precondition : a >= 0 et b >= 0// Postcondition : ret = a * bint multiplication_russe(int a, int b) { int x = a; int y = b; int res = 0; while (x > 0) { if (x % 2 == 1) { res = res + y; } x = x / 2; y = y * 2; } return res;}Question 1
Section titled “Question 1”Formaliser l’invariant de boucle permettant de prouver la correction partielle de cet algorithme.
Correction: **
L’invariant doit lier les variables locales de la boucle (x, y, res) aux paramètres d’entrée (a, b). Intuitivement, au cours des divisions successives de x par 2 et des multiplications de y par 2, la valeur totale reste invariante et égale au produit initial .
Proposons l’invariant :
Question 2
Section titled “Question 2”Démontrer que cet invariant est inductif, c’est-à-dire qu’il est vérifié avant d’entrer dans la boucle et qu’il est préservé par une itération. Correction: **
-
Initialisation : Juste avant la boucle, , et . On a bien : De plus, les préconditions garantissent et , donc et . L’invariant est donc vérifié.
-
Conservation : Supposons vrai au début d’une itération, et la condition de boucle vérifiée. Notons les valeurs avant l’itération, et les valeurs après l’itération du corps de la boucle.
Le corps de la boucle effectue :
- Si est impair () : et impair .
- Si est pair () : et pair .
Dans les deux cas, la division entière par 2 donne (en division entière). Parallèlement, .
Regardons la quantité :
- Si est pair :
- Si est impair :
Dans tous les cas, . De plus, comme et est entier, , et puisque . L’invariant est donc préservé.
Question 3
Section titled “Question 3”Prouver la terminaison de la boucle en définissant un variant de boucle adéquat. Correction: ** Soit le variant .
- D’après l’invariant, à chaque entrée dans le corps de la boucle.
- La condition de boucle est .
- À chaque itération, est remplacé par . Puisque , on a nécessairement .
Le variant est un entier strictement positif qui décroit strictement à chaque itération et est minoré par 0. Par conséquent, la boucle termine en un nombre fini d’itérations (au maximum ).
Question 4
Section titled “Question 4”En déduire la correction totale du programme et donner sa complexité temporelle dans le pire des cas en fonction de . Correction: ** À la sortie de la boucle, la condition de boucle est fausse, donc . Combiné avec l’invariant , on en déduit . En injectant dans l’invariant : La postcondition est bien établie.
Complexité : À chaque étape, la variable est divisée par 2. Le nombre d’itérations est donc de l’ordre de . Les opérations à l’intérieur de la boucle (test de parité, division par 2, multiplication par 2, addition) s’effectuent en temps constant en arithmétique machine standard. La complexité temporelle totale est donc en .
Partie II : Analyse critique des résultats et limites
Section titled “Partie II : Analyse critique des résultats et limites”On souhaite implémenter cet algorithme en langage C pour des types entiers à taille fixe (int sur 32 bits signés, représentant des entiers dans l’intervalle ).
Question 5
Section titled “Question 5”Que se passe-t-il si la précondition n’est plus respectée, ou si les valeurs manipulées dépassent les bornes de représentation de la machine ? Discuter de la validité de l’invariant dans ce contexte. Correction: **
- Extension aux négatifs : Si , la preuve algébrique de l’invariant reste mathématiquement vraie dans . Toutefois, les conditions de positivité ( et ) doivent être adaptées car peut devenir négatif ().
- Dépassement de capacité (Overflow) : En informatique, les entiers ont une taille fixe. Si dépasse , l’opération ou provoquera un dépassement de capacité (overflow). En C, le comportement des entiers signés en cas d’overflow est indéfini (undefined behavior). L’invariant algébrique est alors mis en défaut dans l’arithmétique modulo de la machine, invalidant de fait la correction du programme.
Bilan de l’examinateur
Section titled “Bilan de l’examinateur”Note de correction : Les candidats devront veiller à bien dissocier la correction partielle (validité de l’invariant) de la terminaison (monotonie et minoration du variant). La rigueur dans la manipulation des assertions logiques (implications, conjonctions) est particulièrement attendue en MP2I.