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 zz une quantité inconnue que l'on souhaite déterminer. Dans bien des cas, le calcul direct de zz 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 z^\hat{z} de zz, 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 zz en une seule étape, on construit une suite d'approximations successives z(0),z(1),z(2),z^{(0)}, z^{(1)}, z^{(2)}, \ldots 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 : k=0k = 0
  • On choisit une solution initiale z(0)z^{(0)}

Boucle itérative

Le cœur de l'algorithme est une boucle forte qui répète les opérations suivantes :

Pour k=0,1,2,k = 0, 1, 2, \ldots, on calcule la nouvelle approximation selon la règle :

z(k+1)=f(z(k))z^{(k+1)} = f\left(z^{(k)}\right)

ff 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

algorithme_iteratif.pypython
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, k

Schéma conceptuel

Le flux d'exécution peut être représenté ainsi :

ÉtapeActionÉtat
1Initialisationk=0k = 0, choix de z(0)z^{(0)}
2Calcul itératifz(k+1)=f(z(k))z^{(k+1)} = f(z^{(k)})
3Test d'arrêtConvergence atteinte ?
4aSi ouiRetourner z(k+1)z^{(k+1)}
4bSi nonkk+1k \leftarrow k + 1, retour à l'étape 2

La fonction d'itération

La fonction ff 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 ff a pour objectif de transformer une estimation z(k)z^{(k)} en une estimation z(k+1)z^{(k+1)} plus proche de la solution exacte zz.

f:z(k)z(k+1)f : z^{(k)} \longmapsto z^{(k+1)}

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

TypeFormuleContexte d'utilisation
Moyennef(z)=z+c2f(z) = \frac{z + c}{2}Lissage, interpolation
Correction additivef(z)=z+αrf(z) = z + \alpha \cdot rDescente de gradient
Normalisationf(z)=zzf(z) = \frac{z}{\|z\|}Calcul de vecteurs propres
Mise à jour par résiduf(z)=zA1(Azb)f(z) = z - A^{-1}(Az - b)Résolution de systèmes linéaires

Exemple concret : la méthode de Babylone

Pour calculer la racine carrée de aa, on utilise la fonction d'itération :

f(z)=12(z+az)f(z) = \frac{1}{2}\left(z + \frac{a}{z}\right)
racine_babylone.pypython
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.4142135623730951

Solution initiale

Le choix de la solution initiale z(0)z^{(0)} 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èmeConséquenceExemple
Estimation trop éloignéeConvergence lenteMéthode de Newton avec point initial loin de la racine
Zone de divergenceL'algorithme divergePoint fixe instable
Bassin d'attraction différentConvergence 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

StructureNotationExemple
ScalairexRx \in \mathbb{R}Une température, une racine
VecteurxRn\mathbf{x} \in \mathbb{R}^nCoordonnées, coefficients
MatriceARm×nA \in \mathbb{R}^{m \times n}Système d'équations, transformations
TenseurTRn1×n2×n3T \in \mathbb{R}^{n_1 \times n_2 \times n_3}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
operations_base.pypython
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    # Normalisation

Erreur numérique d'estimation

À chaque itération, l'approximation z(k)z^{(k)} diffère de la vraie solution zz. Cette différence constitue l'erreur d'estimation.

Définition formelle

L'erreur à l'itération kk est définie par :

Δz(k)=zz(k)\Delta z^{(k)} = z - z^{(k)}

Types d'erreur

TypeFormuleInterprétation
Erreur absolueΔz(k)=zz(k)|\Delta z^{(k)}| = |z - z^{(k)}|Écart en valeur absolue
Erreur relativeΔz(k)z\frac{|\Delta z^{(k)}|}{|z|}É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 zz, donc on ne peut pas calculer l'erreur réelle Δz(k)\Delta z^{(k)}.

On utilise alors une erreur observable, basée sur la différence entre deux itérations successives :

δz(k)=z(k+1)z(k)\delta z^{(k)} = z^{(k+1)} - z^{(k)}
💡

À retenir

L'erreur observable δz(k)\delta z^{(k)} 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 zz.


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 :

Δz(k)g(k)|\Delta z^{(k)}| \leq g(k)

g(k)g(k) est une fonction décroissante de kk.

Exemples de décroissance

Type de convergenceBorne sur l'erreurComportement
LinéaireΔz(k)Crk|\Delta z^{(k)}| \leq C \cdot r^k0<r<10 < r < 1, décroissance géométrique
QuadratiqueΔz(k+1)CΔz(k)2|\Delta z^{(k+1)}| \leq C \cdot |\Delta z^{(k)}|^2Doublement des décimales exactes
SuperlinéaireΔz(k+1)CΔz(k)p|\Delta z^{(k+1)}| \leq C \cdot |\Delta z^{(k)}|^p1<p<21 < p < 2

Compromis fondamental

Il existe un lien direct entre :

  • Le nombre d'itérations kk
  • La précision atteinte Δz(k)|\Delta z^{(k)}|
  • 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 kmaxk_{\max} sur le nombre d'itérations :

k<kmaxk < k_{\max}

Seuil sur l'erreur observable

On vérifie si la variation entre deux itérations est suffisamment petite :

z(k+1)z(k)<ε|z^{(k+1)} - z^{(k)}| < \varepsilon

Seuil sur l'erreur relative

Pour éviter les problèmes d'échelle :

z(k+1)z(k)z(k)<ε\frac{|z^{(k+1)} - z^{(k)}|}{|z^{(k)}|} < \varepsilon

Comparaison des approches

CritèreAvantageInconvénient
Nombre maximalGarantit la terminaisonNe garantit pas la précision
Seuil absoluSimple à implémenterDépend de l'échelle du problème
Seuil relatifIndépendant de l'échelleProblème si z(k)0z^{(k)} \approx 0
💡

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.

criteres_arret.pypython
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 False

Convergence

La notion de convergence est centrale dans l'analyse des algorithmes itératifs.

Définition

On dit que la suite (z(k))k0(z^{(k)})_{k \geq 0} converge vers zz si :

limkz(k)=z\lim_{k \to \infty} z^{(k)} = z

En termes pratiques, cela signifie que l'erreur z(k)z|z^{(k)} - z| devient arbitrairement petite lorsque kk augmente.

Convergence locale et globale

TypeDéfinitionImplication pratique
LocaleConvergence garantie si z(0)z^{(0)} est proche de zzNécessite une bonne initialisation
GlobaleConvergence pour tout choix de z(0)z^{(0)}Plus robuste, moins courant

Point fixe

La solution zz est souvent un point fixe de la fonction d'itération, c'est-à-dire :

f(z)=zf(z) = z

Intuitivement, lorsque la suite atteint la solution, elle y reste car l'application de ff 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 ff au voisinage de zz soit inférieure à 1 en valeur absolue :

f(z)<1|f'(z)| < 1
💡

Intuition

Si f(z)<1|f'(z)| < 1, les petites erreurs sont contractées à chaque itération : l'algorithme se rapproche de la solution. Si f(z)>1|f'(z)| > 1, 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 ff est bien définie et calculable
  • Il existe un point fixe zz tel que f(z)=zf(z) = z
  • Le point fixe est stable (attractif)
  • La solution initiale est dans le bassin d'attraction

Cas problématiques

SituationSymptômeCause possible
Divergencez(k)|z^{(k)}| \to \inftyPoint fixe instable ou mauvaise initialisation
OscillationLa suite alterne entre plusieurs valeursCycle attractif au lieu d'un point fixe
StagnationConvergence extrêmement lenteMauvais 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, (a+b)+c=a+(b+c)(a + b) + c = a + (b + c) toujours. En arithmétique flottante, ce n'est plus garanti.

paradoxe_associativite.pypython
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.0

Explication : Dans le premier cas, a+b=0a + b = 0 exactement, puis 0+1=10 + 1 = 1. Dans le second cas, b+cbb + c \approx b car cc est absorbé par la magnitude de bb, puis a+b=0a + b = 0.

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é » :

1020+11020=0(en flottant)10^{20} + 1 - 10^{20} = 0 \quad \text{(en flottant)}

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.

annulation_catastrophique.pypython
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")
xMéthode naïveMéthode stableValeur exacte
10410^{-4}0.499999999...0.499999999...≈ 0.5
10810^{-8}0.0 (faux !)0.499999999...≈ 0.5
101210^{-12}0.0 (faux !)0.499999999...≈ 0.5

Explication : Pour x=108x = 10^{-8}, on a cos(x)0.99999999999999995...\cos(x) \approx 0.99999999999999995... en double précision. La soustraction 1cos(x)1 - \cos(x) 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 :

ordre_sommation.pypython
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èneImpact sur les itérationsPrécaution
AbsorptionLes petites corrections peuvent être ignoréesVérifier l'ordre de grandeur des termes
AnnulationPerte de précision dans les différencesUtiliser des formulations alternatives
AccumulationLes erreurs s'additionnent à chaque itérationLimiter le nombre d'itérations, utiliser des formules stables

Résumé

💡

Points essentiels

Architecture : Initialisation → Boucle itérative → Condition d'arrêt

Équation fondamentale : z(k+1)=f(z(k))z^{(k+1)} = f(z^{(k)})

Fonction d'itération : Transforme une estimation en une meilleure estimation

Convergence : La suite tend vers la solution lorsque kk \to \infty

Point fixe : La solution vérifie f(z)=zf(z) = z

Équations clés

ConceptFormule
Itérationz(k+1)=f(z(k))z^{(k+1)} = f(z^{(k)})
ErreurΔz(k)=zz(k)\Delta z^{(k)} = z - z^{(k)}
Critère d'arrêtz(k+1)z(k)<ε|z^{(k+1)} - z^{(k)}| < \varepsilon
Point fixef(z)=zf(z) = z
Stabilitéf(z)<1|f'(z)| < 1

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.