Les variantes de boucle
Qu'est-ce qu'une variante de boucle ?
Lorsque nous concevons des algorithmes itératifs, nous devons nous assurer qu'ils se terminent effectivement. La variante de boucle est un outil fondamental pour prouver la terminaison d'une boucle.
Une variante de boucle (ou fonction de terminaison) est une expression associée à une boucle qui possède les propriétés suivantes :
- Elle est évaluée à une valeur entière positive ou nulle.
- Sa valeur décroît strictement à chaque itération de la boucle.
- Elle fournit une borne inférieure garantissant que la boucle ne peut pas s'exécuter indéfiniment.
Alors que l'invariant de boucle nous aide à prouver l'exactitude partielle ("si la boucle se termine, elle produit le bon résultat"), la variante de boucle nous aide à prouver la terminaison ("la boucle se termine effectivement").
Invariant vs Variante
| Concept | Rôle | Propriété | Exemple |
|---|---|---|---|
| Invariant de boucle | Prouve l'exactitude du résultat | Propriété vraie avant et après chaque itération | \(s = 1 + 2 + \dots + (i-1)\) |
| Variante de boucle | Prouve la terminaison | Valeur positive qui décroît strictement | \(n - i\) (nombre d'itérations restantes) |
Invariant et variante sont complémentaires : l'invariant garantit que si la boucle s'arrête, le résultat est correct ; la variante garantit que la boucle s'arrête. Ensemble, ils fournissent une preuve complète d'exactitude.
Exemple 1 : Boucle for simple
Considérons le programme de somme des entiers déjà étudié :
def somme(n):
s = 0
for i in range(1, n+1):
s += i
return sVariante de boucle :
où \(i\) est le compteur de boucle courant.
Vérification des propriétés :
- Positivité : Au début de la boucle, \(i = 1\), donc \(v = n\). Pendant la boucle, \(i \le n\), donc \(v \ge 1\). La variante est toujours positive.
- Décroissance stricte : À chaque itération, \(i\) est incrémenté de 1, donc \(v\) diminue exactement de 1. La valeur décroît strictement.
- Borne inférieure : Lorsque \(v\) atteint 0 (c'est-à-dire \(i = n+1\)), la boucle se termine. On a une garantie de terminaison.
Exemple 2 : Recherche dichotomique
Considérons l'algorithme de recherche dichotomique dans un tableau trié :
def recherche_dichotomique(tab, x):
debut = 0
fin = len(tab) - 1
while debut <= fin:
milieu = (debut + fin) // 2
if tab[milieu] == x:
return milieu
elif tab[milieu] < x:
debut = milieu + 1
else:
fin = milieu - 1
return -1Variante de boucle :
qui représente la taille de l'intervalle de recherche courant.
Vérification des propriétés :
- Positivité : La condition de boucle \(\text{debut} \le \text{fin}\) garantit que \(v \ge 1\).
- Décroissance stricte: À chaque itération, on remplace l'intervalle courant par sa moitié gauche ou droite :
- Si on cherche dans la moitié gauche : \(\text{fin}\) devient \(\text{milieu} - 1\)
- Si on cherche dans la moitié droite : \(\text{debut}\) devient \(\text{milieu} + 1\)
- Borne inférieure : La taille de l'intervalle ne peut pas devenir négative. Lorsqu'elle atteint 0, la condition de boucle \(\text{debut} \le \text{fin}\) devient fausse et la boucle se termine.
Exemple 3 : Algorithme d'Euclide (PGCD)
L'algorithme d'Euclide calcule le plus grand commun diviseur de deux entiers :
def pgcd(a, b):
while b != 0:
r = a % b
a = b
b = r
return aVariante de boucle :
la valeur du second paramètre.
Vérification des propriétés :
- Positivité : La condition de boucle \(b \neq 0\) garantit que \(v > 0\).
- Décroissance stricte : À chaque itération, \(b\) devient \(a \mod b\). Par définition du modulo, \(0 \le a \mod b < b\). Donc la nouvelle valeur de \(b\) est strictement inférieure à l'ancienne.
- Borne inférieure : \(b\) est un entier positif qui décroît strictement. Il ne peut pas décroître indéfiniment ; il finira par atteindre 0, ce qui termine la boucle.
Exemple 4 : Parcours en profondeur (DFS) avec pile explicite
Considérons un parcours en profondeur d'un graphe utilisant une pile :
def dfs_iteratif(graphe, depart):
visites = set()
pile = [depart]
while pile:
sommet = pile.pop()
if sommet not in visites:
visites.add(sommet)
for voisin in graphe[sommet]:
if voisin not in visites:
pile.append(voisin)
return visitesVariante de boucle :
Ici, la terminaison est garantie par le fait que :
- Chaque sommet ne peut être ajouté à la pile que s'il n'a pas déjà été visité.
- Le nombre total de sommets est fini.
où \(|S|\) est le nombre total de sommets du graphe.
Vérification des propriétés :
- Positivité : \(v\) est positif tant qu'il reste des sommets à visiter.
- Décroissance stricte : À chaque fois qu'on visite un nouveau sommet (ajout à l'ensemble visites), \(v\) diminue de 1. Bien que la pile puisse contenir plusieurs éléments, le nombre de sommets non visités décroît strictement à chaque nouvelle découverte.
- Borne inférieure : \(v\) ne peut pas devenir négatif. Lorsqu'il atteint 0, tous les sommets accessibles ont été visités et la pile finira par se vider.
Cas particuliers et pièges à éviter
Une variante de boucle mal choisie peut ne pas garantir la terminaison :
- Variante non strictement décroissante : Si la variante peut stagner, la boucle pourrait ne pas progresser.
- Variante non bornée inférieurement : Une variante qui peut décroître indéfiniment sans atteindre de borne ne prouve pas la terminaison.
- Variante qui dépend de données modifiées ailleurs : Il faut s'assurer que la variante est affectée par la boucle elle-même.
Contre-exemple : boucle infinie
def boucle_dangereuse(n):
i = 0
while i < n:
# Oubli d'incrémenter i !
# La variante (n - i) ne décroît jamais
print(i)Ici, la variante \(v = n - i\) ne décroît pas car \(i\) n'est pas modifié. La boucle est infinie.
Récapitulatif des variantes étudiées
| Algorithme | Variante | Justification de la décroissance |
|---|---|---|
| Somme des entiers | \(n - i + 1\) | \(i\) augmente de 1 à chaque itération |
| Recherche dichotomique | \(\text{fin} - \text{debut} + 1\) | L'intervalle est au moins divisé par 2 |
| Algorithme d'Euclide | \(b\) | \(a \mod b < b\) |
| DFS itératif | \(|S| - |\text{visites}|\) | Nombre de sommets non visités décroît |
Comment trouver une variante ?
- Identifier ce qui progresse : Cherchez une quantité qui s'approche d'une limite à chaque itération (compteur, taille d'intervalle, valeur numérique, etc.).
- Vérifier la positivité : Assurez-vous que cette quantité est positive dans la boucle.
- Vérifier la décroissance stricte : Montrez que la quantité diminue à chaque itération, quel que soit le chemin d'exécution.
- Établir une borne inférieure : Identifiez la valeur minimale que peut atteindre la variante (souvent 0).
- Vérifier le lien avec la condition d'arrêt : La variante atteint sa borne exactement quand la condition de boucle devient fausse.
Exercices d'application
Exercice 1 : Puissance itérative
def puissance(x, n):
resultat = 1
for _ in range(n):
resultat *= x
return resultatQuestion : Quelle est la variante de boucle ? Démontrez les trois propriétés.
Variante : \(v = n - i\) où \(i\) est le compteur implicite de la boucle (nombre d'itérations déjà effectuées).
- Positivité : Initialement \(i = 0\), donc \(v = n\). Tant que \(i < n\), \(v > 0\).
- Décroissance : À chaque itération, \(i\) augmente de 1, donc \(v\) diminue de 1.
- Borne : Lorsque \(v = 0\), on a effectué \(n\) itérations et la boucle se termine.
Exercice 2 : Tri à bulles
def tri_bulles(tab):
n = len(tab)
for i in range(n-1):
for j in range(n-1-i):
if tab[j] > tab[j+1]:
tab[j], tab[j+1] = tab[j+1], tab[j]
return tabQuestion : Identifiez les variantes pour les deux boucles imbriquées.
Boucle externe : \(v_1 = n - 1 - i\)
- Positif pour \(i < n-1\)
- Décroît de 1 à chaque itération
Boucle interne : \(v_2 = n - 1 - i - j\)
- Positif pour \(j < n-1-i\)
- Décroît de 1 à chaque itération de la boucle interne
Les deux boucles se terminent car leurs variantes sont des entiers positifs qui décroissent strictement.
Les variantes de boucle sont essentielles pour prouver la terminaison des algorithmes itératifs.
- Une variante est une expression entière positive qui décroît strictement à chaque itération.
- Elle complète l'invariant de boucle (qui prouve l'exactitude partielle) pour fournir une preuve complète d'exactitude.
- Les variantes courantes incluent : compteurs, tailles d'intervalles, valeurs numériques, nombres d'éléments restants.
- La décroissance stricte garantit qu'on ne peut pas boucler indéfiniment.
- La borne inférieure (souvent 0) correspond à la condition d'arrêt de la boucle.
Comparaison finale : Invariant vs Variante
| Propriété | Invariant de boucle | Variante de boucle |
|---|---|---|
| Objectif | Prouver l'exactitude du résultat | Prouver la terminaison |
| Nature | Propriété logique (vrai/faux) | Valeur numérique (positive) |
| Évolution | Doit rester vrai (stable) | Doit décroître strictement |
| Condition initiale | Vrai avant la boucle | Positive au début |
| Condition finale | Donne le résultat attendu | Atteint sa borne inférieure |
| Utilisation conjointe | Invariant + Variante = Preuve complète d'exactitude | |
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.