Principes généraux des algorithmes itératifs
Introduction
Dans de nombreuses situations en informatique et en mathématiques appliquées, il est impossible — ou trop coûteux — de calculer directement la valeur exacte d'une quantité recherchée. On a alors recours à des méthodes itératives : des algorithmes qui construisent progressivement une approximation de la solution, en l'améliorant à chaque étape.
Cette leçon présente l'architecture générale de ces algorithmes, les concepts fondamentaux qui les sous-tendent, ainsi que les conditions nécessaires à leur bon fonctionnement.
Problème général
Soit une quantité inconnue que l'on souhaite déterminer. Dans bien des cas, le calcul direct de est soit impossible (solution d'une équation non linéaire), soit trop coûteux (système de grande dimension).
On cherche alors à construire une approximation de , suffisamment proche de la vraie valeur pour être utile en pratique.
Solution approchée
Une solution approchée est une estimation de la solution exacte, obtenue par un processus de calcul qui s'arrête après un nombre fini d'opérations. L'objectif est de contrôler l'écart entre l'approximation et la vraie solution.
L'idée fondamentale est la suivante : plutôt que de chercher en une seule étape, on construit une suite d'approximations successives qui se rapprochent progressivement de la solution.
Architecture générale d'un algorithme itératif
Tout algorithme itératif suit une structure canonique composée de trois phases distinctes.
Initialisation
L'algorithme commence par définir un point de départ :
- On initialise le compteur d'itérations :
- On choisit une solution initiale
Boucle itérative
Le cœur de l'algorithme est une boucle forte qui répète les opérations suivantes :
Pour , on calcule la nouvelle approximation selon la règle :
où est la fonction d'itération qui transforme l'estimation courante en une estimation améliorée.
Condition d'arrêt
L'algorithme s'arrête lorsqu'un des critères suivants est satisfait :
- Le nombre maximal d'itérations est atteint
- Un critère de convergence est vérifié
Pseudo-code
def algorithme_iteratif(z_initial, f, epsilon, max_iter):
"""
Algorithme itératif générique.
Paramètres :
z_initial : estimation initiale
f : fonction d'itération
epsilon : seuil de convergence
max_iter : nombre maximal d'itérations
Retourne :
z_hat : approximation finale
k : nombre d'itérations effectuées
"""
z_courant = z_initial
k = 0
while k < max_iter:
z_nouveau = f(z_courant)
# Vérification du critère de convergence
if abs(z_nouveau - z_courant) < epsilon:
return z_nouveau, k + 1
z_courant = z_nouveau
k += 1
return z_courant, kSchéma conceptuel
Le flux d'exécution peut être représenté ainsi :
| Étape | Action | État |
|---|---|---|
| 1 | Initialisation | , choix de |
| 2 | Calcul itératif | |
| 3 | Test d'arrêt | Convergence atteinte ? |
| 4a | Si oui | Retourner |
| 4b | Si non | , retour à l'étape 2 |
La fonction d'itération
La fonction est le cœur de tout algorithme itératif. On l'appelle aussi opérateur d'itération ou application itérative.
Rôle de la fonction d'itération
La fonction a pour objectif de transformer une estimation en une estimation plus proche de la solution exacte .
La qualité de l'algorithme dépend entièrement du choix de cette fonction : une bonne fonction d'itération garantit une convergence rapide vers la solution.
Exemples de fonctions d'itération
| Type | Formule | Contexte d'utilisation |
|---|---|---|
| Moyenne | Lissage, interpolation | |
| Correction additive | Descente de gradient | |
| Normalisation | Calcul de vecteurs propres | |
| Mise à jour par résidu | Résolution de systèmes linéaires |
Exemple concret : la méthode de Babylone
Pour calculer la racine carrée de , on utilise la fonction d'itération :
def racine_babylone(a, epsilon=1e-10, max_iter=100):
"""
Calcul de la racine carrée par la méthode de Babylone.
"""
z = a / 2 # Estimation initiale
for k in range(max_iter):
z_nouveau = 0.5 * (z + a / z)
if abs(z_nouveau - z) < epsilon:
return z_nouveau
z = z_nouveau
return z
# Exemple : racine de 2
print(racine_babylone(2)) # Affiche : 1.4142135623730951Solution initiale
Le choix de la solution initiale est une étape cruciale qui influence directement le comportement de l'algorithme.
Importance du choix initial
Une solution initiale bien choisie peut :
- Accélérer considérablement la convergence
- Garantir que l'algorithme converge vers la bonne solution
- Réduire le coût de calcul global
Attention
Une mauvaise initialisation peut entraîner une convergence lente, voire une divergence complète de l'algorithme. Dans certains cas, elle peut aussi conduire à une solution différente de celle recherchée.
Conséquences d'une mauvaise initialisation
| Problème | Conséquence | Exemple |
|---|---|---|
| Estimation trop éloignée | Convergence lente | Méthode de Newton avec point initial loin de la racine |
| Zone de divergence | L'algorithme diverge | Point fixe instable |
| Bassin d'attraction différent | Convergence vers une autre solution | Équation avec plusieurs racines |
Stratégies de choix
En pratique, on peut utiliser différentes approches :
- Utiliser une estimation grossière basée sur le contexte du problème
- Appliquer une méthode rapide mais peu précise pour obtenir un point de départ
- Exploiter les propriétés mathématiques du problème (symétrie, bornes connues)
Structures de données utilisées
Les méthodes itératives manipulent des structures de données simples mais fondamentales.
Types de données
| Structure | Notation | Exemple |
|---|---|---|
| Scalaire | Une température, une racine | |
| Vecteur | Coordonnées, coefficients | |
| Matrice | Système d'équations, transformations | |
| Tenseur | Images, données multidimensionnelles |
Opérations élémentaires
Les algorithmes itératifs reposent sur des opérations arithmétiques de base :
- Addition et soustraction : mise à jour des estimations
- Multiplication et division : calcul de facteurs de correction
- Produit matriciel : transformation linéaire des vecteurs
import numpy as np
# Scalaire
x = 3.14
# Vecteur
v = np.array([1.0, 2.0, 3.0])
# Matrice
A = np.array([[1, 2], [3, 4]])
# Opérations courantes
w = A @ v[:2] # Produit matrice-vecteur
norme = np.linalg.norm(v) # Norme euclidienne
v_normalise = v / norme # NormalisationErreur numérique d'estimation
À chaque itération, l'approximation diffère de la vraie solution . Cette différence constitue l'erreur d'estimation.
Définition formelle
L'erreur à l'itération est définie par :
Types d'erreur
| Type | Formule | Interprétation |
|---|---|---|
| Erreur absolue | Écart en valeur absolue | |
| Erreur relative | Écart proportionnel à la solution |
Erreur réelle et erreur observable
Un problème pratique se pose : on ne connaît généralement pas la vraie valeur , donc on ne peut pas calculer l'erreur réelle .
On utilise alors une erreur observable, basée sur la différence entre deux itérations successives :
À retenir
L'erreur observable est un indicateur pratique de la convergence, mais elle ne garantit pas que l'on soit proche de la vraie solution. Elle mesure la stabilisation de la suite, pas nécessairement sa proximité avec .
Bornes sur l'erreur
Dans l'analyse des algorithmes itératifs, on cherche souvent à établir des bornes sur l'erreur d'estimation.
Principe général
L'erreur peut être majorée par une fonction du nombre d'itérations :
où est une fonction décroissante de .
Exemples de décroissance
| Type de convergence | Borne sur l'erreur | Comportement |
|---|---|---|
| Linéaire | , décroissance géométrique | |
| Quadratique | Doublement des décimales exactes | |
| Superlinéaire |
Compromis fondamental
Il existe un lien direct entre :
- Le nombre d'itérations
- La précision atteinte
- Le coût de calcul (temps, mémoire)
Plus on effectue d'itérations, plus l'approximation est précise, mais plus le calcul est coûteux. Le choix du critère d'arrêt reflète ce compromis.
Conditions d'arrêt
Un algorithme itératif doit disposer de critères clairs pour déterminer quand s'arrêter.
Stratégies courantes
Nombre maximal d'itérations
On fixe une limite sur le nombre d'itérations :
Seuil sur l'erreur observable
On vérifie si la variation entre deux itérations est suffisamment petite :
Seuil sur l'erreur relative
Pour éviter les problèmes d'échelle :
Comparaison des approches
| Critère | Avantage | Inconvénient |
|---|---|---|
| Nombre maximal | Garantit la terminaison | Ne garantit pas la précision |
| Seuil absolu | Simple à implémenter | Dépend de l'échelle du problème |
| Seuil relatif | Indépendant de l'échelle | Problème si |
Bonne pratique
En pratique, on combine généralement plusieurs critères : on s'arrête dès que l'un d'entre eux est satisfait. Cela garantit à la fois la terminaison et une précision minimale.
def critere_arret(z_nouveau, z_courant, k, epsilon, max_iter):
"""
Vérifie si l'algorithme doit s'arrêter.
Retourne True si l'un des critères est satisfait.
"""
# Critère 1 : nombre maximal d'itérations
if k >= max_iter:
return True
# Critère 2 : convergence absolue
if abs(z_nouveau - z_courant) < epsilon:
return True
# Critère 3 : convergence relative
if abs(z_courant) > epsilon:
if abs(z_nouveau - z_courant) / abs(z_courant) < epsilon:
return True
return FalseConvergence
La notion de convergence est centrale dans l'analyse des algorithmes itératifs.
Définition
On dit que la suite converge vers si :
En termes pratiques, cela signifie que l'erreur devient arbitrairement petite lorsque augmente.
Convergence locale et globale
| Type | Définition | Implication pratique |
|---|---|---|
| Locale | Convergence garantie si est proche de | Nécessite une bonne initialisation |
| Globale | Convergence pour tout choix de | Plus robuste, moins courant |
Point fixe
La solution est souvent un point fixe de la fonction d'itération, c'est-à-dire :
Intuitivement, lorsque la suite atteint la solution, elle y reste car l'application de ne la modifie plus.
Condition de stabilité
Pour qu'un point fixe soit stable (attractif), une condition suffisante est que la dérivée de au voisinage de soit inférieure à 1 en valeur absolue :
Intuition
Si , les petites erreurs sont contractées à chaque itération : l'algorithme se rapproche de la solution. Si , les erreurs sont amplifiées : l'algorithme diverge.
Conditions d'application et limites
Les algorithmes itératifs ne fonctionnent pas dans toutes les situations. Il est important de connaître leurs hypothèses et leurs limites.
Hypothèses nécessaires
Pour garantir le bon fonctionnement d'un algorithme itératif, on suppose généralement :
- La fonction d'itération est bien définie et calculable
- Il existe un point fixe tel que
- Le point fixe est stable (attractif)
- La solution initiale est dans le bassin d'attraction
Cas problématiques
| Situation | Symptôme | Cause possible |
|---|---|---|
| Divergence | Point fixe instable ou mauvaise initialisation | |
| Oscillation | La suite alterne entre plusieurs valeurs | Cycle attractif au lieu d'un point fixe |
| Stagnation | Convergence extrêmement lente | Mauvais conditionnement du problème |
Notions de robustesse
Stabilité : l'algorithme reste borné et ne diverge pas vers l'infini.
Sensibilité : mesure de l'impact des petites perturbations (erreurs d'arrondi, données bruitées) sur le résultat.
Conditionnement : propriété intrinsèque du problème qui indique sa difficulté numérique. Un problème mal conditionné amplifie les erreurs, même avec un bon algorithme.
Attention
Un algorithme peut être mathématiquement correct mais numériquement instable. Les erreurs d'arrondi en arithmétique flottante peuvent s'accumuler et dégrader la solution, particulièrement pour les problèmes mal conditionnés.
Paradoxes de l'arithmétique flottante
L'arithmétique des ordinateurs réserve des surprises qui peuvent sembler paradoxales. Ces phénomènes ne sont pas des bugs, mais des conséquences directes de la représentation finie des nombres réels en mémoire.
L'addition n'est pas associative
En mathématiques, toujours. En arithmétique flottante, ce n'est plus garanti.
a = 1e20
b = -1e20
c = 1.0
# Ordre 1 : (a + b) + c
resultat_1 = (a + b) + c
print(f"(a + b) + c = {resultat_1}") # Affiche : 1.0
# Ordre 2 : a + (b + c)
resultat_2 = a + (b + c)
print(f"a + (b + c) = {resultat_2}") # Affiche : 0.0Explication : Dans le premier cas, exactement, puis . Dans le second cas, car est absorbé par la magnitude de , puis .
Le phénomène d'absorption
Quand on additionne un très grand nombre et un très petit nombre, le petit peut être « absorbé » :
alors que mathématiquement le résultat devrait être 1.
L'annulation catastrophique
C'est le phénomène le plus insidieux. Quand on soustrait deux nombres presque égaux, on perd des chiffres significatifs.
import numpy as np
def f_naive(x):
"""Calcul naïf de (1 - cos(x)) / x² pour x petit."""
return (1 - np.cos(x)) / x**2
def f_stable(x):
"""Version stable utilisant l'identité 1 - cos(x) = 2 sin²(x/2)."""
return 2 * (np.sin(x/2) / x)**2
# Valeur théorique pour x → 0 : limite = 1/2
x = 1e-8
print(f"Méthode naïve : {f_naive(x)}") # Résultat erroné !
print(f"Méthode stable : {f_stable(x)}") # ≈ 0.5
print(f"Valeur exacte : 0.5")| x | Méthode naïve | Méthode stable | Valeur exacte |
|---|---|---|---|
| 0.499999999... | 0.499999999... | ≈ 0.5 | |
| 0.0 (faux !) | 0.499999999... | ≈ 0.5 | |
| 0.0 (faux !) | 0.499999999... | ≈ 0.5 |
Explication : Pour , on a en double précision. La soustraction annule presque tous les chiffres significatifs, ne laissant que du « bruit numérique ».
L'ordre de sommation compte
Pour sommer une série de nombres, l'ordre peut affecter le résultat :
import numpy as np
# Série harmonique : 1 + 1/2 + 1/3 + ... + 1/n
n = 10_000_000
# Somme dans l'ordre croissant (grands d'abord)
somme_croissante = sum(1/k for k in range(1, n+1))
# Somme dans l'ordre décroissant (petits d'abord)
somme_decroissante = sum(1/k for k in range(n, 0, -1))
print(f"Grands d'abord : {somme_croissante:.15f}")
print(f"Petits d'abord : {somme_decroissante:.15f}")
print(f"Différence : {abs(somme_croissante - somme_decroissante):.2e}")Bonne pratique
Pour minimiser les erreurs d'arrondi lors d'une sommation, il est préférable d'additionner les termes du plus petit au plus grand. Cela évite que les petits termes soient absorbés par l'accumulateur.
Implications pour les algorithmes itératifs
Ces paradoxes ont des conséquences directes sur les méthodes numériques :
| Phénomène | Impact sur les itérations | Précaution |
|---|---|---|
| Absorption | Les petites corrections peuvent être ignorées | Vérifier l'ordre de grandeur des termes |
| Annulation | Perte de précision dans les différences | Utiliser des formulations alternatives |
| Accumulation | Les erreurs s'additionnent à chaque itération | Limiter le nombre d'itérations, utiliser des formules stables |
Résumé
Points essentiels
Architecture : Initialisation → Boucle itérative → Condition d'arrêt
Équation fondamentale :
Fonction d'itération : Transforme une estimation en une meilleure estimation
Convergence : La suite tend vers la solution lorsque
Point fixe : La solution vérifie
Équations clés
| Concept | Formule |
|---|---|
| Itération | |
| Erreur | |
| Critère d'arrêt | |
| Point fixe | |
| Stabilité |
Idées fondamentales
Les algorithmes itératifs constituent une famille puissante de méthodes numériques. Leur efficacité repose sur trois piliers : le choix judicieux de la fonction d'itération, une initialisation appropriée et des critères d'arrêt bien calibrés. La compréhension de la convergence et de ses conditions permet d'utiliser ces algorithmes de manière éclairée et d'anticiper leurs limites.