Factorisation LU — Résolution et applications

Objectifs d'apprentissage

À la fin de cette leçon, vous serez en mesure de :

  • Résoudre un système linéaire en utilisant une factorisation LU existante
  • Comprendre et appliquer le stockage compact de L et U
  • Implémenter l'algorithme complet de factorisation LU avec résolution
  • Calculer le déterminant d'une matrice à partir de sa factorisation LU
  • Intégrer le pivotage dans la factorisation LU

Prérequis

  • Factorisation LU — Principes
  • Substitution avant et arrière

Résolution en deux étapes

Principe

Une fois la factorisation A=LUA = L \cdot U obtenue, résoudre Ax=bA \cdot x = b devient :

LUx=bL \cdot U \cdot x = b

On pose y=Uxy = U \cdot x et on résout en deux étapes :

🚨

Méthode de résolution

  1. Substitution avant : Résoudre Ly=bL \cdot y = b pour trouver yy
  2. Substitution arrière : Résoudre Ux=yU \cdot x = y pour trouver xx

Chaque étape coûte O(n2/2)O(n^2/2), soit un total de O(n2)O(n^2) par système résolu.


Exemple détaillé

Résolvons le système Ax=bA \cdot x = b avec :

A=(211433879),b=(41024)A = \begin{pmatrix} 2 & 1 & 1 \\ 4 & 3 & 3 \\ 8 & 7 & 9 \end{pmatrix}, \quad b = \begin{pmatrix} 4 \\ 10 \\ 24 \end{pmatrix}

La factorisation LU (calculée dans la leçon précédente) est :

L=(100210431),U=(211011002)L = \begin{pmatrix} 1 & 0 & 0 \\ 2 & 1 & 0 \\ 4 & 3 & 1 \end{pmatrix}, \quad U = \begin{pmatrix} 2 & 1 & 1 \\ 0 & 1 & 1 \\ 0 & 0 & 2 \end{pmatrix}

Étape 1 : Résoudre Ly=bL \cdot y = b

(100210431)(y1y2y3)=(41024)\begin{pmatrix} 1 & 0 & 0 \\ 2 & 1 & 0 \\ 4 & 3 & 1 \end{pmatrix} \begin{pmatrix} y_1 \\ y_2 \\ y_3 \end{pmatrix} = \begin{pmatrix} 4 \\ 10 \\ 24 \end{pmatrix}

Par substitution avant :

  • y1=4y_1 = 4
  • y2=1024=2y_2 = 10 - 2 \cdot 4 = 2
  • y3=244432=24166=2y_3 = 24 - 4 \cdot 4 - 3 \cdot 2 = 24 - 16 - 6 = 2

Donc y=(4,2,2)Ty = (4, 2, 2)^T.

Étape 2 : Résoudre Ux=yU \cdot x = y

(211011002)(x1x2x3)=(422)\begin{pmatrix} 2 & 1 & 1 \\ 0 & 1 & 1 \\ 0 & 0 & 2 \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ x_3 \end{pmatrix} = \begin{pmatrix} 4 \\ 2 \\ 2 \end{pmatrix}

Par substitution arrière :

  • x3=2/2=1x_3 = 2/2 = 1
  • x2=(211)/1=1x_2 = (2 - 1 \cdot 1)/1 = 1
  • x1=(41111)/2=2/2=1x_1 = (4 - 1 \cdot 1 - 1 \cdot 1)/2 = 2/2 = 1

Solution : x=(1,1,1)Tx = (1, 1, 1)^T


Avantage pour plusieurs seconds membres

Quand on doit résoudre plusieurs systèmes avec la même matrice :

Méthode1 systèmek systèmes
Gauss répétén3/3n^3/3kn3/3k \cdot n^3/3
LU + résolutionn3/3+n2n^3/3 + n^2n3/3+kn2n^3/3 + k \cdot n^2

Pour n=1000n = 1000 et k=100k = 100 systèmes :

  • Gauss répété : ≈ 33 milliards d'opérations
  • LU : ≈ 433 millions d'opérations (76× plus rapide !)

Stockage compact

Principe

Puisque LL a des 1 sur la diagonale (qu'on n'a pas besoin de stocker) et UU a des 0 sous la diagonale, on peut stocker les deux matrices dans un seul tableau de la taille de AA :

(u11u12u13l21u22u23l31l32u33)\begin{pmatrix} u_{11} & u_{12} & u_{13} \\ l_{21} & u_{22} & u_{23} \\ l_{31} & l_{32} & u_{33} \end{pmatrix}

Algorithme avec stockage compact

lu_compact.pypython
import numpy as np

def lu_decomposition_compact(A):
  """
  Factorisation LU avec stockage compact (in-place).
  La matrice A est modifiée pour contenir L (sous la diagonale)
  et U (diagonale et au-dessus).
  """
  n = len(A)
  LU = A.astype(float).copy()

  for k in range(n - 1):
      # Vérifier le pivot
      if abs(LU[k, k]) < 1e-12:
          raise ValueError(f"Pivot nul à la position ({k}, {k})")

      # Calculer les multiplicateurs et les stocker dans L
      for i in range(k + 1, n):
          LU[i, k] = LU[i, k] / LU[k, k]

          # Mettre à jour U
          for j in range(k + 1, n):
              LU[i, j] -= LU[i, k] * LU[k, j]

  return LU

def lu_solve_compact(LU, b):
  """
  Résout Ax = b en utilisant la factorisation LU compacte.
  """
  n = len(b)
  x = b.astype(float).copy()

  # Substitution avant (Ly = b)
  for i in range(1, n):
      for j in range(i):
          x[i] -= LU[i, j] * x[j]

  # Substitution arrière (Ux = y)
  for i in range(n - 1, -1, -1):
      for j in range(i + 1, n):
          x[i] -= LU[i, j] * x[j]
      x[i] /= LU[i, i]

  return x

# Exemple
A = np.array([[2, 1, 1],
            [4, 3, 3],
            [8, 7, 9]], dtype=float)
b = np.array([4, 10, 24], dtype=float)

LU = lu_decomposition_compact(A)
print("LU compact =")
print(LU)

x = lu_solve_compact(LU, b)
print(f"\nSolution : x = {x}")

Factorisation LU avec pivotage

Nécessité du pivotage

Comme pour l'élimination de Gauss, le pivotage est nécessaire pour :

  1. Éviter les divisions par zéro (pivot nul)
  2. Améliorer la stabilité numérique (pivot petit)

Factorisation PA = LU

Avec pivotage partiel, on obtient :

PA=LUP \cdot A = L \cdot U

PP est une matrice de permutation (enregistrant les échanges de lignes).

lu_pivotage.pypython
import numpy as np

def lu_decomposition_pivot(A):
  """
  Factorisation LU avec pivotage partiel.
  Retourne LU (compact), et perm (vecteur de permutation).
  """
  n = len(A)
  LU = A.astype(float).copy()
  perm = list(range(n))  # Permutation initiale [0, 1, 2, ...]

  for k in range(n - 1):
      # Pivotage partiel : trouver le plus grand pivot
      max_idx = k + np.argmax(np.abs(LU[k:, k]))

      if max_idx != k:
          # Échanger les lignes
          LU[[k, max_idx]] = LU[[max_idx, k]]
          perm[k], perm[max_idx] = perm[max_idx], perm[k]

      if abs(LU[k, k]) < 1e-12:
          raise ValueError("Matrice singulière")

      # Élimination
      for i in range(k + 1, n):
          LU[i, k] /= LU[k, k]
          for j in range(k + 1, n):
              LU[i, j] -= LU[i, k] * LU[k, j]

  return LU, perm

def lu_solve_pivot(LU, perm, b):
  """
  Résout Ax = b avec la factorisation PA = LU.
  """
  n = len(b)

  # Appliquer la permutation à b
  pb = np.array([b[perm[i]] for i in range(n)], dtype=float)

  # Substitution avant
  for i in range(1, n):
      for j in range(i):
          pb[i] -= LU[i, j] * pb[j]

  # Substitution arrière
  for i in range(n - 1, -1, -1):
      for j in range(i + 1, n):
          pb[i] -= LU[i, j] * pb[j]
      pb[i] /= LU[i, i]

  return pb

# Exemple avec pivot nécessaire
A = np.array([[0, 1, 2],
            [1, 2, 3],
            [2, 1, 1]], dtype=float)
b = np.array([5, 10, 6], dtype=float)

LU, perm = lu_decomposition_pivot(A)
print(f"Permutation : {perm}")
x = lu_solve_pivot(LU, perm, b)
print(f"Solution : x = {x}")

Calcul du déterminant

Le déterminant de AA se calcule facilement à partir de la factorisation LU :

det(A)=det(L)det(U)=1i=1nuii=i=1nuii\det(A) = \det(L) \cdot \det(U) = 1 \cdot \prod_{i=1}^{n} u_{ii} = \prod_{i=1}^{n} u_{ii}

Avec pivotage, il faut tenir compte du signe :

det(A)=(1)si=1nuii\det(A) = (-1)^s \prod_{i=1}^{n} u_{ii}

où :

  • nn est la dimension de la matrice
  • ss est le nombre d'échanges de lignes effectués lors du pivotage
  • uiiu_{ii} sont les éléments diagonaux de UU
determinant_lu.pypython
def determinant_lu(LU, num_swaps=0):
  """
  Calcule le déterminant à partir de la factorisation LU.
  """
  diag_product = 1.0
  for i in range(len(LU)):
      diag_product *= LU[i, i]
  return ((-1) ** num_swaps) * diag_product

Avantages et inconvénients de LU

AvantagesInconvénients
Efficace pour plusieurs seconds membresN'exploite pas la symétrie de la matrice
Stockage compact possiblePivotage plus complexe à gérer
Calcul du déterminant facilePas adapté aux matrices creuses
Préserve la structure de la matriceRequiert mineurs principaux non nuls (sans pivotage)

Résumé

La factorisation LU permet de résoudre efficacement plusieurs systèmes linéaires :

  1. Factorisation : A=LUA = L \cdot U (une seule fois, O(n3/3)O(n^3/3))
  2. Résolution : Ly=bL \cdot y = b puis Ux=yU \cdot x = y (pour chaque bb, O(n2)O(n^2))

Points clés :

  • Stockage compact : L et U dans une seule matrice
  • Pivotage : PA=LUP \cdot A = L \cdot U pour la stabilité
  • Déterminant : produit de la diagonale de U

Pour aller plus loin

La prochaine leçon abordera les systèmes rectangulaires (plus d'équations que d'inconnues ou l'inverse) et la méthode des moindres carrés pour trouver la meilleure solution approximative.