Création de matrices¶

On représentera une matrice comme une liste de liste de nombres, où chaque sous liste correspondra à une ligne.

Une première tentative naïve pour créer la matrice nulle pourrait être la suivante.

In [1]:
n,m = 5, 6
ligne = [0]*m
print(ligne)
[0, 0, 0, 0, 0, 0]
In [2]:
matrice = [ligne]*n
matrice
Out[2]:
[[0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0]]

Problème : toutes les lignes pointent vers la même liste, en en modifiant une on les modifient toutes.

In [3]:
matrice[0][1] = 1
matrice
Out[3]:
[[0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0]]

Modification : on va créer les lignes les unes après les autres.

Remarque : on peut créer une nouvelle liste vide via l'appel de list.

In [4]:
def matrice_nulle(n,m):
    resultat = list()
    for i in range(n):
        nouvelle_ligne = list()
        for j in range(m):
            nouvelle_ligne.append(0)
        resultat.append(nouvelle_ligne)
    return resultat
        
    
In [5]:
M = matrice_nulle(5,6)
M
Out[5]:
[[0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0]]
In [6]:
M[0][1] = 1
M
Out[6]:
[[0, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0]]

Modification : on peut se débarasser des variables intermédiaires superflues et rendre le code plus concis de la façon suivante.

In [7]:
def matrice_nulle(n,m):
    """Renvoit la matrice nulle à n lignes et m colonnes"""
    return [[0 for j in range(m)] for i in range(n)]
In [8]:
matrice_nulle(6,6)
Out[8]:
[[0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0]]

Remarque : cette syntaxe est suffisamment souple pour obtenir des nouvelles fonctions de création. On peut en effet rajouter un test dans la compréhension de liste.

In [9]:
def matrice_identite(n):
    """Renvoit la matrice identité de taille nxn"""
    return  [[1 if i==j else 0 for j in range(n) ] for i in range(n)]
In [10]:
matrice_identite(6)
Out[10]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]

Remarque : L'ajout d'une logique supplémentaire (if ...) commence à rendre la lecture difficile on peut choisir de découpler la construction de la matrice de la logique créant les coefficients, on aboutit alors à la fonction plus générale suivante.

In [11]:
def matrice(n,m,fonction):
    """Renvoit la matrice nxm où le coefficient ixj est donnée par fonction(i,j)"""
    return [[fonction(i,j) for j in range(m)] for i in range(n)]
In [12]:
def auxiliaire(i,j):
    return abs(i-j)
In [13]:
matrice(7,6, auxiliaire)
Out[13]:
[[0, 1, 2, 3, 4, 5],
 [1, 0, 1, 2, 3, 4],
 [2, 1, 0, 1, 2, 3],
 [3, 2, 1, 0, 1, 2],
 [4, 3, 2, 1, 0, 1],
 [5, 4, 3, 2, 1, 0],
 [6, 5, 4, 3, 2, 1]]

On notera qu'on aurait pu ramener matrice_nulle et matrice_identite à un appel de matrice_fonction avec une fonction auxiliaire bien choisie.

On peut constater que l'on a usage de fonctions anonymes qui ne sont appelé qu'une seule fois dans matrice_fonction, ce qui est possible via la syntaxe suivante.

In [14]:
import random as rd
In [15]:
matrice(5, 5, lambda i,j: rd.randint(-5,5))
Out[15]:
[[-3, 0, -4, -2, -5],
 [-3, -4, -4, -1, -4],
 [5, 1, 4, -1, 3],
 [3, -5, -1, -5, 5],
 [5, 4, -5, 2, -2]]

Manipulation algébrique de base¶

In [16]:
def dimensions(A):
    """Retourne le nombres de lignes et de colonnes de la matrice A"""
    return len(A), len(A[0])
In [17]:
dimensions(matrice_nulle(6,5))
Out[17]:
(6, 5)
In [18]:
def copie(A):
    """Renvoit une copie de la matrice A"""
    na, ma = dimensions(A)
    return matrice(na, ma, lambda i,j: A[i][j])
In [19]:
A = matrice(6,6, lambda i,j:abs(i-j))
A
Out[19]:
[[0, 1, 2, 3, 4, 5],
 [1, 0, 1, 2, 3, 4],
 [2, 1, 0, 1, 2, 3],
 [3, 2, 1, 0, 1, 2],
 [4, 3, 2, 1, 0, 1],
 [5, 4, 3, 2, 1, 0]]
In [20]:
B = copie(A)
B
Out[20]:
[[0, 1, 2, 3, 4, 5],
 [1, 0, 1, 2, 3, 4],
 [2, 1, 0, 1, 2, 3],
 [3, 2, 1, 0, 1, 2],
 [4, 3, 2, 1, 0, 1],
 [5, 4, 3, 2, 1, 0]]
In [21]:
B == A
Out[21]:
True
In [22]:
B is A
Out[22]:
False

Somme¶

In [23]:
def somme(A,B):
    """Renvoit une nouvelle matrice comportant la somme A+B"""
    na, ma = dimensions(A)
    if (na, ma) != dimensions(B):
        return "Matrices incompatibles"
    else:
        return  matrice(na, ma, lambda i,j: A[i][j]+B[i][j])
In [24]:
A, B = matrice(6,6, lambda i,j: i), matrice(6,6, lambda i,j:j)
print(A)
print(B)
[[0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2], [3, 3, 3, 3, 3, 3], [4, 4, 4, 4, 4, 4], [5, 5, 5, 5, 5, 5]]
[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]
In [25]:
somme(A,B)
Out[25]:
[[0, 1, 2, 3, 4, 5],
 [1, 2, 3, 4, 5, 6],
 [2, 3, 4, 5, 6, 7],
 [3, 4, 5, 6, 7, 8],
 [4, 5, 6, 7, 8, 9],
 [5, 6, 7, 8, 9, 10]]
In [26]:
C = matrice_nulle(5,5)
somme(A,C)
Out[26]:
'Matrices incompatibles'

Exercice : On a choisit de réutiliser la fonction matrice on pourra trouver d'autres façons de coder la somme.

Produit¶

In [27]:
def produit(A,B):
    """Retourne le produit matriciel A*B"""
    na, ma = dimensions(A)
    nb, mb = dimensions(B)
    if ma != nb:
        return "Matrices incompatibles"
    else:
        return [[sum(A[i][k]*B[k][j] for k in range(ma)) for j in range(mb)] for i in range(na)]
In [28]:
A = matrice_identite(6)
A
Out[28]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [29]:
B = matrice(6,6, lambda i,j: abs(i-j))
B
Out[29]:
[[0, 1, 2, 3, 4, 5],
 [1, 0, 1, 2, 3, 4],
 [2, 1, 0, 1, 2, 3],
 [3, 2, 1, 0, 1, 2],
 [4, 3, 2, 1, 0, 1],
 [5, 4, 3, 2, 1, 0]]
In [30]:
produit(A,B)
Out[30]:
[[0, 1, 2, 3, 4, 5],
 [1, 0, 1, 2, 3, 4],
 [2, 1, 0, 1, 2, 3],
 [3, 2, 1, 0, 1, 2],
 [4, 3, 2, 1, 0, 1],
 [5, 4, 3, 2, 1, 0]]

Exercice : on pourra chercher à coder le produit en réutilisant la fonction matrice.

Manipulation des lignes et echelonnage¶

On devra compenser dans les manipulations le fait que Python commence à compter à zéro.

In [31]:
def transposition(i,j,A):
    """On échange les i-ièmes et j-ièmes lignes de A"""
    if i==j:
        pass
    else:
        A[i-1], A[j-1] = A[j-1], A[i-1]
In [32]:
A = matrice_identite(6)
A
Out[32]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [33]:
transposition(3,4,A)
In [34]:
A
Out[34]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]

On notera qu'on a fait le choix de modifier la matrice plutôt que d'en créer une nouvelle contenant le résultat de la manipulation. Ceci dans un souci de rapidité.

In [35]:
def transvection(k,i,x,A):
    """On effectue la transvection k-ième ligne = k-ième ligne+ x*i-ième ligne de A"""
    if k==i:
        return "Opération invalide"
    na, ma = dimensions(A)
    A[k-1] = [A[k-1][j]+x*A[i-1][j] for j in range(ma)]
    
In [36]:
A
Out[36]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [37]:
transvection(3,4, -2, A)
In [38]:
A
Out[38]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, -2, 1, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [39]:
def dilatation(i,x,A):
    """La i-ième ligne de A est multipliée par x"""
    if x == 0:
        return "Opération invalide"
    na, ma = dimensions(A)
    A[i-1] = [x*A[i-1][j] for j in range(ma)]
In [40]:
A
Out[40]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, -2, 1, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [41]:
dilatation(1, 2, A)
In [42]:
A
Out[42]:
[[2, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, -2, 1, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]

On notera qu'on a écarter les opérations invalides dans transvection/dilatation, c'est à dire celles qui correspondent à une multiplication à gauche par une matrice potentiellement non-inversible.

In [43]:
def recherche(j,A):
    """Renvoit l'indice de la première ligne sous la j-ième ayant un coefficient non nul sur la j-ième colonne,
    renvoit False s'il n'y en a pas.
    """
    na, ma = dimensions(A)
    for i in range(j,na):
        if A[i][j-1] != 0:
            return i+1
    return False
In [44]:
A = matrice_identite(6)
A
Out[44]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [45]:
recherche(2, A)
Out[45]:
False
In [46]:
transvection(3,2,1, A)
In [47]:
A
Out[47]:
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 1, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]
In [48]:
recherche(2, A)
Out[48]:
3

On va remplacer print par display spécifique au notebook jupyter afin d'avoir un meilleur affichage des matrices.

In [49]:
from IPython.display import display
In [50]:
def echelonnage(A):
    na, ma = dimensions(A)
    B = copie(A)
    for i in range(na):
        if B[i][i] == 0:
            indice = recherche(i+1, B)
            if indice is False:
                continue
            else:
                transposition(i+1, indice, B)
                print("Transposition {} {}".format(i+1, indice))
        dilatation(i+1, 1/B[i][i], B)
        print("Dilatation ligne {}".format(i+1))
        for j in range(i+1, na):
            coefficient = B[j][i]
            transvection(j+1, i+1, -coefficient, B)
            print("Transvection {} via {}".format(j+1, i+1))
        display(B)
            
    return B
In [51]:
A = matrice(5,5,lambda i,j:rd.randint(0,1))
A
Out[51]:
[[1, 0, 1, 0, 1],
 [0, 1, 1, 1, 1],
 [0, 1, 1, 0, 1],
 [0, 0, 1, 0, 0],
 [1, 0, 1, 1, 1]]
In [52]:
echelonnage(A)
Dilatation ligne 1
Transvection 2 via 1
Transvection 3 via 1
Transvection 4 via 1
Transvection 5 via 1
[[1.0, 0.0, 1.0, 0.0, 1.0],
 [0.0, 1.0, 1.0, 1.0, 1.0],
 [0.0, 1.0, 1.0, 0.0, 1.0],
 [0.0, 0.0, 1.0, 0.0, 0.0],
 [0.0, 0.0, 0.0, 1.0, 0.0]]
Dilatation ligne 2
Transvection 3 via 2
Transvection 4 via 2
Transvection 5 via 2
[[1.0, 0.0, 1.0, 0.0, 1.0],
 [0.0, 1.0, 1.0, 1.0, 1.0],
 [0.0, 0.0, 0.0, -1.0, 0.0],
 [0.0, 0.0, 1.0, 0.0, 0.0],
 [0.0, 0.0, 0.0, 1.0, 0.0]]
Transposition 3 4
Dilatation ligne 3
Transvection 4 via 3
Transvection 5 via 3
[[1.0, 0.0, 1.0, 0.0, 1.0],
 [0.0, 1.0, 1.0, 1.0, 1.0],
 [0.0, 0.0, 1.0, 0.0, 0.0],
 [0.0, 0.0, 0.0, -1.0, 0.0],
 [0.0, 0.0, 0.0, 1.0, 0.0]]
Dilatation ligne 4
Transvection 5 via 4
[[1.0, 0.0, 1.0, 0.0, 1.0],
 [0.0, 1.0, 1.0, 1.0, 1.0],
 [0.0, 0.0, 1.0, 0.0, 0.0],
 [-0.0, -0.0, -0.0, 1.0, -0.0],
 [0.0, 0.0, 0.0, 0.0, 0.0]]
Out[52]:
[[1.0, 0.0, 1.0, 0.0, 1.0],
 [0.0, 1.0, 1.0, 1.0, 1.0],
 [0.0, 0.0, 1.0, 0.0, 0.0],
 [-0.0, -0.0, -0.0, 1.0, -0.0],
 [0.0, 0.0, 0.0, 0.0, 0.0]]

Exercices :

  • Tester de manière plus développé la fonction ci-dessus
  • enlever les affichages une fois debuggué
  • écrire une fonction calculant le rang d'une matrice
  • écrire une fonction inversant une matrice
  • écrire une fonction effectuant une décomposition LU
  • écrire une fonction effectuant la décomposition de Cholesky