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.
n,m = 5, 6
ligne = [0]*m
print(ligne)
matrice = [ligne]*n
matrice
Problème : toutes les lignes pointent vers la même liste, en en modifiant une on les modifient toutes.
matrice[0][1] = 1
matrice
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
.
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
M = matrice_nulle(5,6)
M
M[0][1] = 1
M
Modification : on peut se débarasser des variables intermédiaires superflues et rendre le code plus concis de la façon suivante.
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)]
matrice_nulle(6,6)
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.
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)]
matrice_identite(6)
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.
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)]
def auxiliaire(i,j):
return abs(i-j)
matrice(7,6, auxiliaire)
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.
import random as rd
matrice(5, 5, lambda i,j: rd.randint(-5,5))
def dimensions(A):
"""Retourne le nombres de lignes et de colonnes de la matrice A"""
return len(A), len(A[0])
dimensions(matrice_nulle(6,5))
def copie(A):
"""Renvoit une copie de la matrice A"""
na, ma = dimensions(A)
return matrice(na, ma, lambda i,j: A[i][j])
A = matrice(6,6, lambda i,j:abs(i-j))
A
B = copie(A)
B
B == A
B is A
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])
A, B = matrice(6,6, lambda i,j: i), matrice(6,6, lambda i,j:j)
print(A)
print(B)
somme(A,B)
C = matrice_nulle(5,5)
somme(A,C)
Exercice : On a choisit de réutiliser la fonction matrice
on pourra trouver d'autres façons de coder la somme.
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)]
A = matrice_identite(6)
A
B = matrice(6,6, lambda i,j: abs(i-j))
B
produit(A,B)
Exercice : on pourra chercher à coder le produit en réutilisant la fonction matrice.
On devra compenser dans les manipulations le fait que Python commence à compter à zéro.
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]
A = matrice_identite(6)
A
transposition(3,4,A)
A
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é.
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)]
A
transvection(3,4, -2, A)
A
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)]
A
dilatation(1, 2, A)
A
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.
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
A = matrice_identite(6)
A
recherche(2, A)
transvection(3,2,1, A)
A
recherche(2, A)
On va remplacer print
par display
spécifique au notebook jupyter afin d'avoir un meilleur affichage des matrices.
from IPython.display import display
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
A = matrice(5,5,lambda i,j:rd.randint(0,1))
A
echelonnage(A)
Exercices :