Tous le code Python proposé est accessible dans un notebook Jupyter interactif.

Notations

On adopte les notations suivantes :

  • $\cT_n^{++}(\dR)$ désigne l’ensemble des matrices triangulaires supérieures de $\cM_n(\dR)$ à coefficients diagonaux strictement positifs.
  • $\langle\cdot,\cdot\rangle$ désigne le produit scalaire usuel sur $\cM_{n,1}(\dR)$ i.e. $\forall(X,Y)\in\cM_{n,1}(\dR)^2$, $\langle X,Y\rangle=X^\top Y$.

Théorème

Soit $A\in GL_n(\dR)$. Alors il existe $Q\in O_n(\dR)$ et $R\in\cT_n^{++}(\dR)$ telles que $A=QR$.

Intérêt

Dès lors que l’on a une décomposition QR d’une matrice $A$, il devient trivial de résoudre un sytème $AX=Y$. En effet ce système équivaut à $RX=Q^{-1}Y=Q^\top Y$. Le calcul de $Q^\top$ est immédiat et ce dernier système est triangulaire : il se résout donc très simplement.

Rappels sur le procédé d’orthonormalisation de Gram-Schmidt

Si $(f_1,\dots,f_n)$ est une base d’un espace euclidien $E$ de dimension $n$, on peut la transformer en une base orthonormée $(e_1,\dots,e_n)$ en appliquant le procédé suivant :

  • on norme le premier vecteur $f_1$ en un vecteur $e_1$ ;
  • une fois construits les vecteurs $e_1,\dots,e_k$, on norme le projeté orthogonal de $f_{k+1}$ sur $\vect(e_1,\dots,e_k)^\perp$ pour obtenir le vecteur $e_{k+1}$.

Du point de vue du calcul, cela revient à construire par récurrence les vecteurs $f_1,\dots,f_n$ via la relation de récurrence

\[\forall k\in\lb0,n-1\rb,\; e_{k+1}=\frac{\tilde e_{k+1}}{\|\tilde e_{k+1}\|} \text{ où }\tilde e_{k+1}=f_{k+1}-\sum_{j=1}^k\langle f_{k+1},e_j\rangle e_j\]

Remarque. On convient que la somme est nulle lorsque $k=0$.

En plus du fait que $(e_1,\dots,e_n)$ est une base orthonormée de $E$, on vérifie aisément que

\[\forall k\in\lb1,n\rb,\;\left(\vect(e_1,\dots,e_k)=\vect(f_1,\dots,f_k)\right)\wedge\left(\langle e_k,f_k\rangle>0\right)\]

La décomposition QR via le procédé de Gram-Schmidt

Notons $f_1,\dots,f_n$ les colonnes de la matrice $A\in GL_n(\dR)$. Comme la matrice $A$ est inversible, la famille $(f_1,\dots,f_n)$ est une base de $\cM_{n,1}(\dR)$. On lui applique le procédé d’orthonormalisation de Gram-Schmidt pour obtenir une base orthonormée (pour le produit scalaire canonique) $(e_1,\dots,e_n)$ de $\cM_{n,1}(\dR)$.

Notons $Q$ la matrice dont les colonnes sont $e_1,\dots,e_n$. Comme $(e_1,\dots,e_n)$ est une base orthonormée, $Q$ est une matrice orthogonale. En notant $R$ la matrice de la base $(f_1,\dots,f_n)$ dans la base $(e_1,\dots,e_n)$, on a alors bien $A=QR$.

De plus, on rappelle que

\[\forall k\in\lb1,n\rb,\;\left(\vect(e_1,\dots,e_k)=\vect(f_1,\dots,f_k)\right)\wedge\left(\langle e_k,f_k\rangle>0\right)\]

donc la matrice $R$ est triangulaire supérieure et ses coefficients diagonaux sont strictement positifs. En effet, comme $(e_1,\dots,e_n)$ est une base orthonormée, $R_{i,j}=\langle f_j,e_i\rangle$ pour tout $(i,j)\in\lb1,n\rb^2$ et notamment $R_{k,k}=\langle f_k,e_k\rangle>0$ pour tout $k\in\lb1,n\rb$.

Algorithme en Python

import numpy as np

def QR_gram_schmidt(A):
    Q = np.zeros_like(A)
    R = np.zeros_like(A)
    for i in range(A.shape[1]):
        dp = Q[:, :i].T@A[:, i]
        R[:i, i] = dp
        Q[:, i] = A[:, i]-Q[:, :i]@dp
        Q[:, i] = Q[:, i]/np.linalg.norm(Q[:, i])
        R[i, i] = Q[:, i].T@A[:, i]
    return Q, R

Méthode de Householder

Dans ce qui suit, $E$ désigne un espace euclidien de dimension $n$ et $\cB$ une base orthonormée de $E$.

Matrices de Householder

Soient $n$ un vecteur non nul de $E$ et $s$ la réflexion par rapport à l’hyperplan $\vect(n)^\perp$. Le projeté orthogonal d’un vecteur $x\in E$ sur $\vect(n)$ est $\frac{\langle n,x\rangle}{|n|^2}n$. On en déduit que

\[s(x)=x-2\frac{\langle n,x\rangle}{\|n\|^2}n\]

Si on note $N$ sa matrice de $n$ dans la base $\cB$, alors la matrice de $s$ dans la base $\cB$ est donc

\[I_n-2\frac{NN^\top}{N^\top N}\]

Une telle matrice est appelée une matrice de Householder.

Réflexion échangeant deux vecteurs de même norme

Soient $E$ un espace euclidien de dimension $n$ ainsi que $u$ et $v$ deux vecteurs distincts et de même norme. Il existe alors une (unique) réflexion $s$ échangeant $u$ et $v$. Il s’agit de la réflexion par rapport à l’hyperplan $\vect(v-u)^\perp$.

Notamment si $S$, $U$ et $V$ sont respectivement les matrices de $s$, $u$ et $v$ dans cette base $\cB$, alors

\[S=I_n-2\frac{(U-V)(U-V)^\top}{(U-V)^\top(U-V)}\]

Application à la décomposition QR

On procède à un raisonnement par récurrence. Il est clair que toute matrice de $GL_1(\dR)$ admet une décomposition $QR$. Supposons que ce soit vrai pour toute matrice de $GL_{n-1}(\dR)$, où $n$ est un entier supérieur ou égal à $2$.

Soit $A\in GL_n(\dR)$. Notons $U$ sa première colonne (nécessairement non nulle). Comme les vecteurs $U$ et $V=(|U|,0,\dots,0)^\top$ ont même norme, il existe une matrice de réflexion $S$ telle que $SU=V$. La première colonne de $SA$ est alors $V$. Notons $\tilde A$ la matrice $SA$ dont on a supprimé les premières lignes et premières colonnes. Autrement dit

\[SA=\begin{pmatrix} \|U\|&\begin{matrix}\star&\dots&\star\end{matrix}\\ \begin{matrix}0\\\vdots\\0\end{matrix}&\tilde A \end{pmatrix}\]

Comme $S$ et $A$ sont inversibles, $\det(SA)=|U|\det(\tilde A)\neq0$ donc $\tilde A\in GL_{n-1}(\dR)$. Il existe donc une matrice $\tilde Q\in O_{n-1}(\dR)$ et $\tilde R\in\cT_{n-1}^{++}(\dR)$ telle que $\tilde A=\tilde Q\tilde R$. Puisque $S=S^{-1}$ ($S$ est une matrice de réflexion), on a alors $A=QR$ avec

\[Q=S\begin{pmatrix} 1&\begin{matrix}0&\dots&0\end{matrix}\\ \begin{matrix}0\\\vdots\\0\end{matrix}&\tilde Q \end{pmatrix}\in O_n(\dR) \qquad\text{et}\qquad R=\begin{pmatrix} \|U\|&\begin{matrix}\star&\dots&\star\end{matrix}\\ \begin{matrix}0\\\vdots\\0\end{matrix}&\tilde R \end{pmatrix}\in\cT_n^{++}(\dR)\]

On en déduit également un algorithme :


Entrée Une matrice $A\in GL_n(\dR)$

  • Initialisation
    • $R\gets A$
    • $Q\gets I_n$
  • Pour $k$ variant de $1$ à $n$ :
    • On extrait la première colonne $U\in\cM_{n-k+1,1}(\dR)$ de la matrice extraite $(R_{i,j})_{k\leq i,j\leq n}$
    • Si $U_1\neq|U|$
      • $V\gets(|U|,0,\dots,0)$.
      • $S\gets I_{n-k+1}-2\frac{(U-V)(U-V)^\top}{(U-V)^\top(U-V)}$
      • $Q_1\gets\begin{pmatrix}I_{k-1}&0\newline0&S\end{pmatrix}$
      • $Q\gets QQ_1$
      • $R\gets Q_1R$

Renvoyer $Q$ et $R$


On définit d’abord une fonction calculant la matrice de Householder associée à un vecteur $U$.

import numpy as np

def matrice_householder(U):
    return np.eye(U.shape[0]) - 2 * (U@U.T) / (U.T@U)

On implémente alors un algorithme itératif calculant la décomposition QR d’une matrice inversible $A$.

def QR_householder(A):
    n = A.shape[0]
    R = A.copy()
    Q = np.eye(n)
    for k in range(n):
        U = R[k:, k].copy()
        norme_U = np.linalg.norm(U)
        if U[0] != norme_U:
            U[0] -= norme_U
            S = matrice_householder(U)
            Q[:, k:] = Q[:, k:]@S
            R[k:, :] = S@R[k:, :]
    return Q, R

On peut également proposer une fonction récursive.

def QR_householder_recu(A):
    if A.shape[0] == 1:
        return (np.matrix([[1]]), A) if A[0, 0] > 0 else (np.matrix([[-1]]), -A)
    U = A[:, 0].copy()
    norme_U = np.linalg.norm(U)
    if U[0] != norme_U:
        U[0] -= norme_U
        Q = matrice_householder(U)
        R = Q@A
    else:
        Q = np.eye(A.shape[0])
        R = A
    Q1, R1 = QR_householder_recu(R[1:, 1:])
    Q[:, 1:] = Q[:, 1:]@Q1
    R[1:, 1:] = R1
    return Q, R