Terminaison et correction
Summary
Terminaison
Prouver la terminaison d'un algorithme signifie démontrer que l'algo termine.
i) Pour les fonctions récursives
- Mettre des
assert("s'assurer que") au début, pour traiter uniquement les cas qui nous intéressent - Trouver un variant de boucle, c'est-à-dire une variable ou somme de variables qui :
- Lorsque le variant vaut
ou moins, la fonction ne fait aucun appel récursif - Sinon, le variant décroit strictement
- Lorsque le variant vaut
ii) Pour les boucles while(condition)
C'est exactement la même chose sauf la condition (1.) qui change légèrement :
- Trouver un variant de boucle, c'est-à-dire une variable ou somme de variables qui :
- Lorsque le variant vaut
ou moins, la condition n'est plus respectée - Sinon, le variant décroit strictement
- Lorsque le variant vaut
iii) Pour les boucles for
Les boucles for terminent toujours.
Correction
Prouver la correction d'un algorithme signifie démontrer que l'algo renvoie bien ce qu'on veut qu'il renvoie.
Il faut toujours faire la terminaison après la correction !
i) Pour les fonctions récursives (pas besoin d'invariant de boucle ici)
On a déjà démontré la terminaison. Il suffit donc de démontrer :
- Les cas initiaux sont corrects
- En supposant les appels récursifs corrects, expliquer pourquoi la fonction est correcte
ii) Pour les boucles while(condition) (invariant de boucle)
- Pour ne pas t'emmêler les pinceaux, je te conseille de toujours rédiger les deux phrases suivantes :
- On a déjà démontré la terminaison : le test
conditionest donc effectuéfois. - Pour toute variable
v, on notesa valeur lorsque le test conditionest effectué pour lafois.
- Invariant de boucle
Il faut que tu trouves un prédicat
iii) Pour les boucles for (invariant de boucle)
- Je te conseille de commencer par rédiger :
- Invariant de boucle
Notre
Il faut que tu trouves un prédicat
Exercices
Exercice 1 : Terminaison (récursif)
Soit la fonction suivante :
def somme_n(n):
assert n >= 0
if n == 0:
return 0
return n + somme_n(n-1)
- Définir un variant de récursion et montrer que la fonction termine pour tout entier
. - Vérifier que la fonction renvoie bien la somme des
premiers entiers.
Exercice 2 : Terminaison (boucle while)
Écrire une fonction qui calcule le plus grand commun diviseur (PGCD) de deux entiers a et b avec l’algorithme d’Euclide :
def pgcd(a, b):
while b != 0:
a, b = b, a % b
return a
- Identifier un variant de boucle et démontrer que la boucle termine.
- Proposer un invariant de boucle pour montrer que l’algorithme est correct.
Exercice 3 : Terminaison (boucle for)
Écrire une fonction qui calcule la factorielle d’un entier
def factorielle(n):
f = 1
for i in range(1, n+1):
f *= i
return f
- Montrer que la boucle
fortermine toujours. - Proposer un invariant de boucle et démontrer la correction de l’algorithme.
Exercice 4 : Invariant de boucle avancé
On souhaite trier une liste L de taille
def tri_insertion(L):
for i in range(1, len(L)):
x = L[i]
j = i
while j > 0 and L[j-1] > x:
L[j] = L[j-1]
j -= 1
L[j] = x
- Définir un variant de boucle pour la boucle
while. - Proposer un invariant de boucle pour la boucle
for. - Montrer que l’algorithme termine et est correct.
Exercice 5 : Analyse combinée
Pour chaque fonction ou boucle ci-dessous, indiquer :
- La terminaison (variant de boucle ou récursion).
- L’invariant de boucle (si applicable) pour démontrer la correction.
a) Fonction qui retourne le plus petit élément d’une liste :
def minimum(L):
min_val = L[0]
for x in L:
if x < min_val:
min_val = x
return min_val
b) Fonction récursive qui calcule la suite de Fibonacci :
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
Quelques redactions type pour la correction
Le plus dur est encore que tu aies l'intuition pour pouvoir exhiber un invariant de boucle.
Mais ça va venir avec l'entraînement.