Listes et matrices

\(\newcommand{\ds}{\displaystyle}\) \(\newcommand{\Frac}{\ds\frac}\) \(\renewcommand{\r}{\mathbb{ R}}\) \(\newcommand{\C}{\mathbb{ C}}\) \(\newcommand{\n}{\mathbb{ N}}\) \(\newcommand{\z}{\mathbb{ Z}}\) \(\newcommand{\Q}{\mathbb{ Q}}\) \(\newcommand{\N}{\mathbb{ N}}\) \(\newcommand{\n}{\mathbb{ N}}\) \(\newcommand{\ol}{\overline}\) \(\newcommand{\abs}[1]{\left| \,{#1} \right|}\) \(\newcommand{\pv}{\;;\;}\) \(\newcommand{\ens}[1]{\left\{ {#1} \right\}}\) \(\newcommand{\mens}[1]{\setminus\left\{ {#1} \right\}}\) \(\newcommand{\Par}[1]{\left({#1}\right)}\) \(\newcommand{\pe}[1]{\left\lfloor {#1} \right\rfloor}\) \(\newcommand{\trans}[1]{\,^t\!{#1}}\)

Listes et matrices

Cours

Notion de liste

En Python, une liste est une structure de données modifiable représentant une « succession » d’objets :

1
2
L = [2020, 42, 2038]
print(L)
3
[2020, 42, 2038]
  • Ligne 1 : L désigne une liste, formée de trois éléments. Une paire de crochets entoure les éléments de la liste et ces éléments sont séparés par des virgules.
  • Ligne 2 : La fonction print est le moyen le plus simple d’obtenir un affichage sommaire d’une liste L.

La liste est la structure couramment utilisée pour représenter des « listes » au sens usuel du terme, comme une liste de mots :

1
2
L = ["Printemps", "Été", "Automne", "Hiver"]
print(L)
3
['Printemps', 'Été', 'Automne', 'Hiver']

Les éléments d’une liste peuvent aussi être des expressions utilisant des variables :

1
2
3
x=4
L = [2020, 10 * x + 2, 2038]
print(L)
4
[2020, 42, 2038]

Les listes ci-dessus sont des listes dites littérales : elles sont définies en explicitant ses éléments dans le code-source, placés entre crochets, un par un et en les séparant par des virgules.

Indexation des éléments d’une liste

Une propriété fondamentale des listes est qu’on accède directement à chaque élément d’une liste par un indice entier. Les différents éléments de la liste sont indexés par les entiers 0, 1, 2, etc. jusqu’à la fin de la liste.

1
2
3
4
L = [2020, 42, 2038]
print(L[0])
print(L[1])
print(L[2])
5
6
7
2020
42
2038
  • Ligne 2 : les éléments de la liste sont indexés, en commençant par 0.
  • Lignes 2-4 : on peut accéder à chaque élément de la liste L par son indice entouré d’une paire de crochets.

Attention ! l’indexation commence à 0 et non à 1.

Si une liste contient \(n\) éléments, ses indices sont tous les entiers entre 0 et \(n-1\), bornes 0 et \(n-1\) incluses, par exemple, une liste de 4 éléments est indexée par les entiers 0, 1, 2 et 3. L’élément d’indice i de la liste L est l’objet L[i]. L’indice 0 correspond toujours au premier élément de la liste et l’indice \(n-1\) d’une liste L de longueur \(n\) correspond toujours au dernier élément de L.

Tenter d’accéder à un indice se référant à un élément hors de la liste déclenche une erreur de type IndexError :

1
2
L = [2020, 42, 2038]
print(L[3])
3
4
5
6
Traceback (most recent call last):
  File "liste_indice_impossible.py", line 2, in <module>
    print(L[3])
IndexError: list index out of range
  • Ligne 1 : les indices de la liste L sont : 0, 1 et 2
  • Ligne 2 : on essaye d’accéder à un indice de L qui n’existe pas

La tentative d’accès à un élément d’une liste par un indice entier impossible est appelé un débordement d’indice ou aussi dépassement d’indice.

Nombre d’éléments d’une liste

La fonction built-in len appliquée à une liste renvoie le nombre d’éléments de la liste :

1
2
3
t = [81, 12, 65, 31]
n = len(t)
print(n)
4
4
  • Ligne 2 : n est le nombre d’éléments de la liste t.

Une liste peut contenir un nombre quelconque d’éléments ; en particulier une liste peut contenir un seul élément et même aucun élément. La liste littérale [] ne contient aucun élément :

1
2
3
4
L= [42]
print(len(L))
L= []
print(len(L))
5
6
1
0
  • Ligne 1 : liste ayant un seul élément.
  • Ligne 3 : la liste vide de longueur nulle (ligne 6).

Opérations sur les éléments d’une liste

Les éléments d’une liste L sont de la forme L[i]. Cette notation peut être utilisée pour des opérations comme on le ferait avec des variables :

1
2
3
t = [5, 3, 25, 3]
x = t[0] * t[1] + t[2] *t[3]
print(x)
4
90

Dans une certaine mesure, on peut considérer une liste comme une suite ordonnée de variables.

Appartenance à une liste

L’opérateur in permet de tester l’appartenance d’un élément à une liste :

1
2
3
L = [65, 31, 9, 32, 81, 82, 46, 12]
print(75 in L)
print(81 in L)
4
5
False
True

L’opérateur in renvoie un booléen. Le mot in est un mot-clé du langage Python.

Pour tester l’appartenance de a à une liste L, Python parcourt toute la liste depuis le début (l’indice 0) et jusqu’à ce que l’élément a soit trouvé ou que la fin de L soit rencontrée. Cela signifie que le test d’appartenance, bien que tenant sur une seule instruction, peut être très coûteux si L est longue.

Non-appartenance à une liste

L’opérateur not in permet de tester la non-appartenance d’un élément à une liste :

1
2
3
L = [65, 31, 9, 32, 81, 82, 46, 12]
print(75 not in L)
print(81 not in L)
4
5
True
False

L’opérateur not in renvoie un booléen. L’expression a not in L a même valeur de vérité que not a in L :

1
2
3
L = [65, 31, 9, 32, 81, 82, 46, 12]
print(75 not in L)
print(not 75 in L)
4
5
True
True

Le test de non-appartenance est basé sur le même principe que le test d’appartenance : un parcours de la liste depuis son début et arrêt du parcours lorsque la réponse est connue.

Modifier les éléments d’une liste

C’est une propriété fondamentale des listes : il est possible d’en modifier les éléments. Pour cela :

  • on accède à l’élément par son indice
  • on modifie l’élément par affectation.

Voici un exemple

1
2
3
4
5
L = [65, 31, 9, 32, 81, 82, 46, 12]
print(L)

L[4] = 75
print(L)
6
7
[65, 31, 9, 32, 81, 82, 46, 12]
[65, 31, 9, 32, 75, 82, 46, 12]
  • Ligne 4 : modification d’un élément de la liste par affectation ;
  • Ligne 7 : comparer, la liste L a été modifiée.

Le fait de pouvoir modifier le contenu d’une liste se traduit en disant qu’une liste est un objet mutable. Si L est une liste, on peut non seulement changer ses éléments, mais lui en adjoindre, ou lui en supprimer et donc modifier le nombre d’éléments de la liste.

Comme pour une variable, on peut affecter ou réaffecter un élément d’une liste :

1
2
3
t = [5, 3, 25, 3]
t[0] = 10 * t[0]
print(t)
4
[50, 3, 25, 3]
  • Ligne 2 : réaffectation de l’élément t[0].

Alias de liste

Le code suivant :

1
2
L = [2020, 42, 2038]
M = L

peut donner l’illusion qu’on dispose de deux listes et qu’on aurait cloné L en M. Ce n’est pas le cas, M n’est qu’un alias de L, un renommage. C’est un code complètement différent de

1
2
L = [2020, 42, 2038]
M = [2020, 42, 2038]

Dans le premier code, toute modification de la liste par l’un des identificateurs se verra si on accède à la liste par l’autre identificateur, par exemple :

1
2
3
4
5
6
7
8
9
L = [2020, 42, 2038]
M = L

print("L =", L)
print("M =", M)

M[0]="XXXXX"
print("L =", L)
print("M =", M)
10
11
12
13
L = [2020, 42, 2038]
M = [2020, 42, 2038]
L = ['XXXXX', 42, 2038]
M = ['XXXXX', 42, 2038]

Tandis qu’avec le deuxième code, seule une liste est modifiée :

1
2
3
4
5
6
7
8
9
L = [2020, 42, 2038]
M = [2020, 42, 2038]

print("L =", L)
print("M =", M)

M[0]="XXXXX"
print("L =", L)
print("M =", M)
10
11
12
13
L = [2020, 42, 2038]
M = [2020, 42, 2038]
L = [2020, 42, 2038]
M = ['XXXXX', 42, 2038

Liste de listes

Les éléments d’une liste peuvent être d’autres listes :

1
2
3
4
5
t = [[65, 31], [], [9, 32, 81], [82], [46, 12]]
L = t[2]
print(L)
print(L[1])
print(t[2][1])
6
7
8
[9, 32, 81]
32
32
  • Ligne 1 : t est une liste dont les 5 éléments sont des listes
  • Ligne 2 : comme pour n’importe quelle liste, on accède aux éléments de t par indexation
  • Ligne 4 : L étant elle-même une liste, on accède à l’un de ses éléments par indexation
  • Ligne 5 : il n’est pas nécessaire de passer par la variable intermédiaire L pour accéder à un élément d’une liste de t, on peut utiliser une double indexation. La notation t[2][1] est équivalente à (t[2])[1].

Pour représenter des relevés de températures, des notes par trimestre et par matières, on utilise des liste de listes, des listes de listes de listes, etc.

Notion de tableau 2D

Soit à écrire un code qui puisse implémenter la structure de données assimilée à un tableau formé de lignes et de colonnes, par exemple un tableau tel que celui-ci :

2 6 9
3 3 2
4 7 5
0 0 7

Voici un code résolvant le problème :

1
2
3
t=[[2, 6, 9], [3, 3, 2], [4, 7, 5], [0, 0, 7]]
print(t[2])
print(t[2][1])
4
5
[4, 7, 5]
7
  • Ligne 1 : on implémente un tableau 2D comme une liste de listes, chaque liste représentant une ligne du tableau. Ici, t est une liste composée de 4 listes, et chacune des 4 listes possède 3 éléments entiers.
  • Lignes 2 et 4 : chaque élément de t est une liste. Donc t[i] représente la ligne i du tableau, chaque ligne étant indexée à partir de 0, donc t[2] est la 3ème ligne
  • Lignes 3 et 5 : l’élément du tableau t situé à la ligne i et à la colonne j vaut t[i][j].

Accès aux dimensions d’un tableau codé en Python

A partir de la structure de données précédente, on cherche à accéder aux nombres de lignes et de colonnes du tableau 2D suivant :

2 6 9
3 3 2
4 7 5
0 0 7

Il y a \(n=4\) lignes et \(p=3\) colonnes.

Voici le code Python :

1
2
3
4
t=[[2, 6, 9], [3, 3, 2], [4, 7, 5], [0, 0, 7]]
n=len(t)
p=len(t[0])
print(n,p)
5
4 3
  • Ligne 2 : le nombre de lignes est le nombre d’éléments de t
  • Ligne 3 : le nombre de colonnes est le nombre d’éléments de n’importe quelle ligne (puisque toutes les lignes ont même nombre d’éléments), et donc comme il y a au moins une ligne dans un tableau 2D, on cherche le nombre d’éléments de la première ligne (indexée par 0).
  • Ligne 4 : affichage des dimensions.

Accès à une colonne complète

On accède facilement à une ligne d’un tableau. Par exemple, la 2e ligne du tableau t est t[1].

On ne peut pas accéder à une colonne aussi facilement. On indexe les colonnes à partir de zéro et on reprend le tableau t ci-dessus :

2 6 9
3 3 2
4 7 5
0 0 7

Cherchons à accéder à la colonne d’indice 2, constituée de 9, 2, 5, 7. Les éléments de cette colonne sont les éléments du tableau de la forme t[i][2]i est un indice de ligne et qui varie donc de 0 à 3, d’où le code suivant :

t=[[2, 6, 9], [3, 3, 2], [4, 7, 5], [0, 0, 7]]
col = 2
print(t[0][col], t[1][col], t[2][col], t[3][col])
9 2 5 7

Listes en compréhension

Étant donné une liste d’entiers telle que t=[5, 2, 0, 3, 7, 10], on cherche à construire la liste L dont les éléments sont 10 fois les éléments de t, c’est-à-dire [50, 20, 0, 30, 70, 100].

Voici un code Python répondant au problème :

1
2
3
4
t = [5, 2, 0, 3, 7, 10]
print(t)
L = [10 * x for x in t]
print(L)
5
6
[5, 2, 0, 3, 7, 10]
[50, 20, 0, 30, 70, 100]

A la ligne 3, est définie une liste L dite liste en compréhension.

Une liste en compréhension L a la syntaxe minimale suivante

[expr for x in t]

  • la paire de crochets, les mots-clefs for et in sont obligatoires
  • t est, par exemple, une liste (voir plus bas pour d’autres possibilités)
  • x est l’élément courant qui parcourt la liste t ; x est appelé variable de contrôle de la liste en compréhension
  • expr est une expression qui dépend en général de x et dont la valeur est placée dans L

Si t est un conteneur (une liste, une chaîne, etc), la liste en compréhension L avec la syntaxe ci-dessus a toujours même nombre d’éléments que le conteneur t.

Intérêt d’une liste en compréhension : générer une liste en une seule expression et non en une ou plusieurs instructions. L’intérêt des listes en compréhension est avant tout leur compacité d’édition dans le code et leur bonne lisibilité.

Vocabulaire : la documentation officielle en français a utilisé jusqu’à la version 3.9 de Python le terme assez incompréhensible de « compréhension de liste » pour finalement adopter le terme de « liste en compréhension ». On rencontre aussi parfois le terme de liste en intension censé s’opposer à l’expression liste en extension ce dernier signifiant, grosso modo, liste littérale, voir cette discussion.

Liste en compréhension et boucle for

Une liste en compréhension admet un équivalent créé avec une boucle for mais nécessitant un code plus long.

Soit la liste en compréhension L suivante :

t = [5, 2, 0, 3, 7, 10]
L = [10 * x for x in t]
print(L)
[50, 20, 0, 30, 70, 100]

Elle pourrait être obtenue avec une boucle for :

t = [5, 2, 0, 3, 7, 10]
L = []
for x in t:
    L.append(10*x)
print(L)
[50, 20, 0, 30, 70, 100]
  • La liste L est obtenue en trois lignes de code au lieu d’une.

Création d’un tableau 2D initialisé

Une liste en compréhension est un moyen très simple de créer un conteneur pour un tableau ayant \(n\) lignes et \(p\) colonnes. Le code ci-dessous crée un tableau à 2 lignes et 3 colonnes initialisé avec des 0 :

L = [[0 for j in range(3)] for i in range(2)]
print(L)
L[1][2]=42
print(L)
[[0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, 42]]
  • On crée une liste de 2 lignes, chaque ligne étant une liste de 3 éléments. L’indice i parcourt les lignes et l’indice j parcourt chaque colonne de chaque ligne.

Au passage, comme l’élément le plus interne dans les listes est immuable (il s’agit de l’entier 0), on peut même simplifier la syntaxe :

L = [[0]*3 for i in range(2)]
print(L)
L[1][2]=42
print(L)
[[0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, 42]]

Confusion possible

Attention toutefois que le code suivant lui ne donne pas le résultat attendu :

L = [[0]*3]*2
print(L)
L[1][2]=42
print(L)
[[0, 0, 0], [0, 0, 0]]
[[0, 0, 42], [0, 0, 42]]

Code équivalent

Voici l’équivalent du code correct donné en début de section sans utiliser de liste en compréhension et en utilisant deux boucles for imbriquées. On notera que le code est plus long :

L=[]
for i in range(2):
    lig = []
    for j in range(3):
        lig.append(0)
    L.append(lig)
print(L)
[[0, 0, 0], [0, 0, 0]]

Listes en compréhension et la clause if

Il existe une variante dans la syntaxe des listes de compréhension utilisant une clause if.

Par exemple, étant donné une liste L d’entiers, soit à construire la liste t obtenue en ne gardant que les entiers \(x\) de L tel que \(x>42\) :

t = [65, 31, 9, 32, 81, 82, 46, 12]
L = [x for x in t if x >= 42]
print(L)
[65, 81, 82, 46]
  • x varie dans t et est inséré dans la liste L en construction si \(x\geq 42\).

On pourrait même écrire tout le code en une seule ligne.

L’équivalent avec une boucle for serait le suivant :

1
2
3
4
5
6
t = [65, 31, 9, 32, 81, 82, 46, 12]
L= []
for x in t:
    if x >= 42:
        L.append(x)
print(L)

Listes en compréhension imbriquées

Des listes en compréhension peuvent être imbriquées. Néanmoins la liste n’est pas forcément créée dans l’ordre où on s’y attendrait.

Soit à créer une liste formée de tous « mots » commençant par une lettre A ou B et suivie d’un chiffre parmi 1, 2 ou 3. On peut utiliser une liste en compréhension :

1
2
L = [x+y for x in "AB" for y in "123"]
print(L)
3
['A1', 'A2', 'A3', 'B1', 'B2', 'B3']

Le résultat ligne 3 montre que l’on fixe d’abord la lettre, c’est-à-dire les éléments du for lexicalement le plus interne (ici for x) puis que y varie. Ce n’est pas forcément très intuitif.

Le résultat est plus facilement compréhensible si la liste en compréhension est interprétée par deux boucles for imbriquées exactement dans l’ordre où on lit les apparitions des for dans la liste en compréhension, ce qui donne ici :

L = []
for x in "AB":
    for y in "123":
        L.append(x+y)
print(L)
['A1', 'A2', 'A3', 'B1', 'B2', 'B3']
  • la boucle for x apparaît avant la boucle for y, dans le même ordre que la liste en compréhension [x+y for x in "AB" for y in "123"].

Notion de matrice

Une matrice est un objet mathématique s’assimilant à un tableau à double entrée contenant des objets (en général des nombres) en sorte que chaque objet de la matrice soit accessible à l’aide d’un couple d’indices entiers. En pratique, on se représente une matrice par un tableau 2D.

Ainsi, on peut représenter une matrice \(M\) à coefficients entiers ayant 2 lignes et 3 colonnes par le tableau :

\(M=\begin{pmatrix} 13&12&31\\81&77&42\end{pmatrix}\)

En mathématiques, l’usage est de placer des parenthèses autour du tableau. En mathématiques encore, l’élément de la matrice \(M\) placé à la \(i\)-ème ligne et à la \(j\)-ème colonne est noté \(M_{i,j}\) avec un double indice. Par exemple, avec la matrice ci-dessus, on a \(M_{2,3}=42\) qui est l’élément à la deuxième ligne et troisième colonne.

Dans un contexte mathématique, l’indexation des lignes commence à 1 et non à 0. Dans un contexte de programmation Python (mais aussi Java, C/C++, Javascript, etc), l’indexation des lignes et des colonnes commence à zéro.

Conventionnellement, étant donné une matrice ou encore un terme d’une matrice, on donne d’abord l’indice de ligne PUIS l’indice de colonne. Sauf contexte particulier, je désignerai toujours le nombre de lignes d’une matrice \(M\) par \(n\) et le nombre de colonnes par \(p\)\(n,p\) sont des entiers strictement positifs et on dit alors que \(M\) est de taille \(n\times p\). Ci-dessus, la matrice donnée en exemple est de taille \(2\times 3\).

Ce qui distingue conceptuellement les matrices de simples tableaux 2D est qu’on associe à une matrice un certain nombre d’opérations mathématiques comme l’addition ou le produit.

Les matrices sont des outils très utilisés dans certains domaines de l’informatique qui nécessitent de la théorie des graphes ou de la recherche opérationnelle.

Matrices égales

Deux matrices \(M\) et \(N\) sont considérées comme égales lorsque :

  • elles ont le même nombre de lignes \(n\)
  • elles ont le même nombre de colonnes \(p\)
  • pour chaque indice \(i\in\{1, \dots, n\}\) et \(j\in\{1, \dots, p\}\) on a \(M_{i,j}=N_{i,j}\).

Matrice carrée, diagonale principale

Une matrice carrée est une matrice de taille \(n\times n\) (autant de lignes que de colonnes) et on parle dans ce cas de matrice carrée d’ordre \(n\).

On appelle diagonale d’une matrice carrée \(M\) la liste des coefficients situés sur la diagonale commençant en haut à gauche, à l’indice \((0, 0)\), et se terminant en bas à droite, à l’indice \((n-1, n-1)\). Les termes de la diagonale de \(M\) sont les \(M_{i, i}\). Cette diagonale est dite principale. L’autre diagonale est dite secondaire.

Les matrices sous le module Numpy

Numpy est un module Python utilisé pour faire efficacement du calcul sur des tableaux multidimensionnels. Son usage est très répandu en calcul numérique et en apprentissage automatique, souvent couplé à d’autres bibliothèques Python.

Nous ne l’utiliserons que pour créer, manipuler et afficher commodément des matrices (des tableaux à deux dimensions). Nous n’utiliserons pas les opérations que met Numpy à disposition pour faire du calcul sur des matrices. La plupart du temps les matrices seront à coefficients entiers.

Accès à Numpy

Numpy n’est pas un module standard Python, donc pour l’utiliser, il faut que la bibliothèque ait été installée dans l’environnement de travail (et une fois installée, il faudra l’importer dans son fichier de travail).

Si vous utilisez Python en ligne avec Google Colab ou Cocalc, Numpy est déjà installé et directement utilisable.

Si vous utilisez l’environnement de bureau Anaconda, Numpy aura été installé.

Sinon, vous pouvez installer Numpy via pip en ligne de commande sous les 3 OS de bureau majoritaires. La commande est tout simplement

pip install numpy

La documentation officielle pour l’installation est ICI.

Importation de Numpy

Pour travailler avec Numpy, la méthode la plus usuelle consiste à l’importer en plaçant la ligne suivante dans votre code :

import numpy as np

(noter que l’initiale de Numpy s’écrit ici en minuscule).

Ensuite, pour utiliser une fonctionnalité de Numpy, il suffira de la préfixer par np, par exemple

import numpy as np

u=np.ones((3, 2), dtype='int')
print(u)
[[1 1]
 [1 1]
 [1 1]]

où la fonction ones est appelée préfixée par np.

Création de matrices sous Numpy

Pour définir une matrice sous Numpy, on définit en fait un tableau (array) à deux dimensions. Il existe de nombreuses façons de le faire et on n’en donnera ici que deux.

Si on connaît les coefficients de la matrice, sous forme d’une liste de listes de nombres, on les transmet en argument au contructeur np.array et on obtient une matrice :

1
2
3
4
5
6
import numpy as np

L=[[5, 81], [12,2030], [21, 12]]
M=np.array(L)

print(M)
7
8
9
[[   5   81]
 [  12 2030]
 [  21   12]]

La liste donnée doit comporter des listes toutes de la même dimension.

L’autre procédé consiste à créer une matrice ayant la dimension souhaitée mais remplie de zéros :

1
2
3
4
5
6
7
import numpy as np

M=np.zeros((3, 4),  dtype='int')
N=np.zeros((2, 3))
print(M)
print()
print(N)
 8
 9
10
11
12
13
[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]

[[0. 0. 0.]
 [0. 0. 0.]]

Cette matrice est dite matrice nulle.

Si on précise l’option dtype=int, la matrice sera considérée comme matrice à coefficients entiers. Sinon, c’est le comportement par défaut, la matrice est à coefficients flottants.

La taille est donnée par une liste ou un tuple (n, p) de deux entiers désignant d’abord le nombre n de lignes puis le nombre p de colonnes.

Ultérieurement, on peut « remplir » la matrice avec des coefficients que l’on choisira en fonction des besoins.

Dimensions d’une matrice sous Numpy

Les dimensions d’une matrice s’obtiennent avec l’attribut shape de la matrice :

import numpy as np

L=[[5, 81], [12,2030], [21, 12]]
M=np.array(L)

dim=M.shape
print(dim)
print(dim[0])
print(dim[1])
(3, 2)

qui renvoie un tuple composé du nombre de lignes et du nombre de colonnes (noter que shape n’est pas suivi d’une paire de parenthèses, ce n’est pas une méthode).

Une matrice carrée d’ordre \(\mathtt{n}\) est simplement une matrice qui a même nombre \(\mathtt{n}\) de lignes et de colonnes.

Accès aux éléments d’une matrice

L’indexation des lignes et des colonnes, comme pour une liste, commence par 0. Pour accéder à l’élément de la matrice M situé en ligne d’indice i et en colonne d’indice j, on utilise la notation M[i,j] :

import numpy as np

L=[[4, 9, 13, 7], [14, 20, 4, 16], [7, 3, 15, 1]]
M=np.array(L)
print(M)

print(M[1,2])
[[ 4  9 13  7]
 [14 20  4 16]
 [ 7  3 15  1]]
4

On peut aussi modifier une matrice à une position ligne x colonne donnée, en écrasant la valeur présente par une valeur donnée. Il suffit d’affecter l’élément de la matrice concerné :

import numpy as np

L=[[4, 9, 13, 7], [14, 20, 4, 16], [7, 3, 15, 1]]
M=np.array(L)
print(M)

print()
print(M[1,2])
print()

M[1,2]=1000
print(M)
[[ 4  9 13  7]
 [14 20  4 16]
 [ 7  3 15  1]]
4

Accès à une ligne donnée

En fait, une matrice Numpy est un tableau de tableaux 1D. Donc, si M est une matrice Numpy, M[i] donne accès à la ligne d’indice i :

import numpy as np

L=[[4, 9, 13, 7], [14, 20, 4, 16], [7, 3, 15, 1]]
M=np.array(L)
print(M)

print()
print(M[2])
[[ 4  9 13  7]
 [14 20  4 16]
 [ 7  3 15  1]]

[ 7  3 15  1]

Il est possible d’accéder à une colonne donnée mais ce ne sera pas considéré dans ce cours.

Matrice aléatoire

Pour faire des tests ou obtenir des exemples, il peut être utile de savoir générer une matrice aléatoire de taille donnée. Voici un exemple :

import numpy as np

M=np.random.randint(10, 21, size=(2, 3))
print(M)
[[20 20 12]
 [15 14 14]]

Ici, on a généré une matrice à coefficients entiers aléatoires entre 10 (inclus) et 21 (exclu) et de dimension 2 x 3.

Parcours de tous les coefficients d’une matrice

Pour parcourir une matrice coefficient par coefficient, on utilise deux boucles for imbriquées. On peut parcourir la matrice suivant ses lignes ou suivant ses colonnes :

import numpy as np

n,p =2,3
M=np.random.randint(10, 21, size=(n, p))
print(M)
print()

# Parcours par lignes
for i in range(n):
    for j in range(p):
        print(M[i,j])
    print("...........")
print("---------------")
print(M)
print()
# Parcours par colonnes
for j in range(p):
    for i in range(n):
        print(M[i,j])
    print("...........")
[[10 14 13]
 [13 13 13]]

10
14
13
...........
13
13
13
...........
---------------
[[10 14 13]
 [13 13 13]]

10
13
...........
14
13
...........
13
13
...........

Parcours sans indice

On peut aussi parcourir une matrice par ligne et sans utiliser d’indices :

import numpy as np

n,p =2,3
M=np.random.randint(10, 21, size=(n, p))
print(M)
print()

# Parcours par lignes
# sans indice

for L in M:
    for z in L:
        print(z)
    print("...........")
[[20 15 18]
 [19 16 13]]

20
15
18
...........
19
16
13
...........

Comparer deux matrices

Pour comparer deux matrices M et N, on n’utilise pas l’opération M == N mais la fonction array_equal :

import numpy as np

u=np.ones((3, 2), dtype='int')
v=np.ones((3, 2))

print(u)
print()
print(v)
print()

print(np.array_equal(u, v))
[[1 1]
 [1 1]
 [1 1]]

[[1. 1.]
 [1. 1.]
 [1. 1.]]

True

Exercice type : Éléments central ou centraux

Cet exercice est corrigé en vidéo ICI

On se donne une liste non vide d’entiers L. On appelle éléments centraux d’une liste :

  • l’élément situé au milieu de la liste si la liste contient un nombre impair d’éléments
  • les deux éléments centraux de la liste si la liste contient un nombre pair d’éléments.

Par exemple :

Liste Éléments centraux
[31, 12, 81, 9, 65] 81
[31, 12, 81, 9, 65, 32] 81 9
[31, 12] 31 12
[81] 81

On demande d’écrire un code qui partant d’une liste L affiche l’élément central ou les éléments centraux.

Solution

Si la liste contient un nombre impair \(\mathtt{n}\) d’entiers, par exemple la liste suivante pour \(\mathtt{n=15}\) :

1
31 12 81 99 65 44 58 63 42 21 33 16 14 81 50

alors il y a un unique élément central. Quel est son indice \(\mathtt{i}\) ? Dans notre exemple, c’est l’élément qui vaut 63 et son indice est 7. Dans le cas général, comme

\(\mathtt{ n= k + 1 + k}\)

on voit bien que l’élément central est le \(\mathtt{k+1}\)-ème élément de la liste et donc son indice \(\mathtt{i}\) est 1 de moins que son rang c’est-à-dire \(\mathtt{i=k}\).

Comme \(\mathtt{n=2k+1}\), on a \(\mathtt{n//2= k}\) donc l’élément central de \(\mathtt{L}\) est \(\mathtt{L[n//2]}\).

Si la liste contient un nombre pair \(\mathtt{n}\) d’entiers, par exemple la liste suivante pour \(\mathtt{n=16}\) :

1
31 12 81 99 65 44 58 63 42 21 33 16 14 81 50 96

alors il y a deux éléments centraux. Quel sont leurs indices ? Dans notre exemple, ce sont les éléments qui valent 63 et 42 donc d’indice 7 et 8. Dans le cas général, comme

\(\mathtt{ n= k + k}\)

on voit bien que les éléments centraux sont aux rangs \(\mathtt{k}\) et \(\mathtt{k+1}\) et donc d’indices \(\mathtt{k-1}\) et \(\mathtt{k}\). Or \(\mathtt{k}\) représente \(\mathtt{n//2}\) et donc les éléments centraux sont

\(\mathtt{L[n//2 - 1]}\) et \(\mathtt{L[n//2]}\).

D’où le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
L = [31, 12]
L=[81]
L = [31, 12, 81, 9, 65]
L = [31, 12, 81, 9, 65, 32]

n=len(L)
if n%2==1:
    print(L[n//2])
else :
    print(L[n//2-1], L[n//2])
11
81 9

Exercice type : Générer la matrice identité

On appelle matrice-identité d’ordre \(n\) la matrice carrée d’ordre \(n\) dont tous les coefficients sont nuls sauf ceux de la diagonale qui valent 1.

Écrire un code qui partant d’un entier \(\mathtt{n>0}\) génère la matrice-identité In. Pour cela, on créera à l’aide de Numpy une matrice remplie de zéros et ayant la taille qu’il faut et on placera des 1 sur la diagonale principale de cette matrice

Une fois le code demandé écrit, on prendra en compte qu’en Numpy, la matrice identité s’obtient avec la fonction eye que l’on consultera pour voir comment elle fonctionne. Vérifier sur des exemples avec cette dernière fonction que votre code renvoie la bonne réponse.

Solution

Voici le code commenté :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import numpy as np

n=5
In=np.zeros((n, n), dtype='int')

for i in range(n):
    In[i,i]=1

print(In)
Jn=np.eye(5, dtype='int')

print(np.array_equal(In, Jn))
13
14
15
16
17
18
[[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]]
True
  • Ligne 1 : notre matrice sera une matrice Numpy, donc on importe ce module.
  • Ligne 3 : la taille de la matrice
  • Ligne 4 : on crée une matrice Numpy In, carrée de taille n x n d’où l’argument (n, n). On utilise la fonction np.zeros qui crée une matrice formée uniquement de coefficients 0. L’argument dtype="int" aura pour conséquence que ses coefficients seront vus comme des entiers ce qui produira un affichage plus sobre. Noter les guillemets autour de int mais en fait ne sont pas indispensables ici.
  • Lignes 6-7 : Il s’agit d’écrire les 1 sur la diagonale. Pour cela, il est inutile de parcourir toute la matrice In, il suffit de se placer aux bons endroits (la diagonale principale) et de placer le 1 voulu. Le bon endroit est chaque position (i, i) de la matrice.
  • Ligne 7 : noter qu’on accède à la position avec un double indice i, i qui pourrait encore s’écrire (i, i)
  • Lignes 9 et 13-17 : on affiche le résultat et on voit qu’il correspond à ce qui était attendu.
  • Ligne 10 : on demande à Numpy de créer lui-même la même matrice avec la fonction eye
  • Ligne 12 : Python nous confirme que notre matrice In et la matrice produite par Numpy sont bien égales (noter l’usage de la fonction np.array_equal).

Exercices

Liste de trois éléments

On donne un entier \(x\) et on compte de 10 en 10 trois fois à partir de x. Construire la liste \(L\) qui contient les trois nombres obtenus. Par exemple, si x=42, la liste est formée des trois entiers 42, 52 et 62.

Liste : premier et dernier

On se donne une liste d’entiers L. Afficher sur une même ligne et séparés par une espace le premier élément de la liste et le dernier élément de la liste. Par exemple,

  • si L = [31, 12, 81, 9, 65] alors l’affichage est : 31 65
  • si L = [42] alors l’affichage est : 42 42

Liste : \(k\)-ème élément

On se donne une liste d’entiers L. On suppose que la liste L contient au moins deux éléments. On donne un entier \(k\geq 1\). Afficher le \(k\)-ème élément de la liste si cet élément existe sinon, on affiche le message rang invalide.

Exemples d’exécution :

  • L = [31, 12, 81, 9, 65], k = 3 -> 81
  • L = [31, 12, 81, 9, 65], k = 42 -> rang invalide

Liste des solutions de l’équation du second degré

Soit l’équation du second degré \(ax^2+bx+c=0\). On supposera que \(a,b,c\) sont des entiers, que \(a\) est non nul et que l’inconnue \(x\) est un nombre réel.

On rappelle le code Python de la résolution d’une équation du second degré :

a = 2
b = -3
c = 1
delta = b*b -4*a*c

if delta >0:
    print("2 solutions distinctes :")
    print("x1 =", (-b - delta**0.5)/(2*a))
    print("x2 =", (-b + delta**0.5)/(2*a))
else:
    if delta == 0:
        print("une seule solution")
        print("x =", -b/(2*a))
    else:
        print("Aucune solution")

ou mieux, avec une instruction elif:

a = 2
b = -3
c = 1
delta = b*b -4*a*c

if delta >0:
    print("2 solutions distinctes :")
    print("x1 =", (-b - delta**0.5)/(2*a))
    print("x2 =", (-b + delta**0.5)/(2*a))
elif delta == 0:
    print("une seule solution")
    print("x =", -b/(2*a))
else:
    print("Aucune solution")

Adapter le code précédent pour construire la liste S contenant toutes les solutions de l’équation. Par exemple,

  • si a = 2, b=-3 et c = 1 alors S = [1, 0.5]
  • si a = 4, b=4 et c = 1 alors S = [-0.5]
  • si a = 4, b=4 et c = 4 alors S est la liste vide

Changer certains éléments d’une liste

On donne une liste L d’entiers ayant au moins 3 valeurs. Modifier cette liste pour que les éléments \(x\) de la liste qui sont

  • en première position
  • en deuxième position
  • en dernière position

soient remplacés par \(10x\). Par exemple, si L = [31, 12, 81, 9, 65] alors, après modification, on aura L = [310, 120, 81, 9, 650].

Echanger les extrémités d’une liste

Cette question nécessite d’adapter aux éléments d’une liste le code d’échange des valeurs de deux variables.

Soit une liste L. Modifier L pour que son premier élément et son dernier élément soient échangés. Par exemple, si L = [31, 12, 9, 65, 81] alors après échange, on aura L = [81, 12, 9, 65, 31].

Kubernetes -> K8s

On donne une liste, par exemple L=['P', 'y', 't', 'h', 'o', 'n'] et on demande de construire une nouvelle liste LL, dans notre exemple, ce sera LL=['P', 4, 'n']. La règle de construction de la liste LL est précisée ci-dessous :

  • si L a moins de trois éléments alors LL et L ont même contenu (mais LL doit être une liste différente de L, autrement dit, avec le vocabulaire vu dans le cours, LL n’est pas un alias de L) ;

  • sinon, LL a au moins trois éléments :

    • le premier et le dernier élément de LL sont identiques à ceux de L
    • l’élément central de LL est le nombre d’éléments de L strictement compris entre les extrémités de L

Voici quelques exemples de comportements :

[42, 1, 9, 4, 7, 5, 3, 6, 0, 2020] -> [42, 8, 2020]
[31, 81, 12] -> [31, 1, 12]
['X', 'Y'] -> ['X', 'Y']
['X'] -> ['X']
[] -> []

Tester les mois de 31 jours avec une liste

Construire une liste littérale jours telle que pour chaque numéro i entre 1 et 12 d’un mois d’une année non bissextile, jours[i] désigne le nombre de jours du mois de numéro i. Par exemple, jours[10] = 31.

En déduire une variable booléenne mois31 qui, étant donné un numéro de mois i, vaut True si le mois de numéro i possède 31 jours et False sinon.

Test de présence

On donne

  • une liste d’entiers L
  • un entier x

On rappelle que x in L est un booléen qui teste la présence de x dans la liste L.

Construire un booléen ok qui vaut True si l’un des trois entiers x, x - 1 et x + 1 est dans la liste et False sinon. Exemple avec x = 42 :

  • si L =[31, 12, 42, 9, 65] alors ok = True
  • si L =[31, 12, 81, 43] alors ok = True
  • si L =[41] alors ok = True
  • si L =[31, 12, 81, 9, 65] alors ok = False

Mois de 31 jours et liste

Cet exercice est corrigé en vidéo

On donne un numéro de mois entre 1 et 12. Créer une variable booléenne est_mois_31 (prenant comme valeur True ou False) qui teste si m est le numéro d’un mois ayant 31 jours comme janvier (numéro 1) ou juillet (numéro 7) mais pas février (numéro 2). On utilisera impérativement l’appartenance à une liste

Afficher toutes les dates de l’année

On vous demande d’afficher sur 365 lignes successives les dates de tous les jours de l’année. Pour simplifier on suppose que février est formé de 28 jours. Par exemple, si le jour de l’An est un vendredi, le code devra afficher :

vendredi 1 janvier
samedi 2 janvier
dimanche 3 janvier
lundi 4 janvier
mardi 5 janvier
mercredi 6 janvier
....
....
vendredi 24 décembre
samedi 25 décembre
dimanche 26 décembre
lundi 27 décembre
mardi 28 décembre
mercredi 29 décembre
jeudi 30 décembre
vendredi 31 décembre

On utilisera une liste pour les noms des jours de la semaine et une liste pour le nom des mois.

Lister les non multiples

Utiliser une liste en compréhension pour construire la liste des entiers entre 1 et 20000 qui ne sont ni pairs ni multiples de 5. Par exemple, 421 ou 2023 feront partie de la liste des entiers concernés mais pas 42 ni 2025. La longueur de la liste à trouver est de 8000.

Liste d’entiers consécutifs décroissants et alternant de signe

On donne deux entiers a et b avec \(a\geq b\geq 0\), par exemple a=2035 et b=2025. En utilisant une liste en compréhension, on demande de créer la liste des entiers consécutifs depuis a jusqu’à b en ordre décroissant et en alternant les signes, a devant apparaître positivement. Par exemple, si a=2035 et b=2025, la liste à contruire est

[2035, -2034, 2033, -2032, 2031, -2030, 2029, -2028, 2027, -2026, 2025]

Liste des entiers impairs suivi des pairs

On pourra utiliser que si L et M sont des listes alors L+M est une nouvelle liste qui donne accès au contenu de L suivi du contenu de M.

On donne un entier \(\mathtt{n\geq 1}\) on demande d’écrire une liste \(\mathtt{L}\) formée d’abord de tous les entiers impairs entre 1 et \(\mathtt{n}\) puis de tous les entiers pairs. Par exemple, si \(\mathtt{n=6}\), on obtiendra la liste \(\mathtt{L=[1, 3, 5, 2, 4, 6]}\) et si \(\mathtt{n=5}\), on obtiendra la liste \(\mathtt{L=[1, 3, 5, 2, 4]}\).

Le code de construction de la liste doit tenir en une seule ligne et utilisera des listes en compréhension.

Liste des carrés entre deux nombres

On donne deux entiers positifs a et b avec \(\mathtt{a\leq b}\). Créer, en utilisant une liste en compréhension, la liste croissante des carrés parfaits entre a et b.

Si a=2000 et b=3000, on trouvera la liste suivante

[2025, 2116, 2209, 2304, 2401, 2500, 2601, 2704, 2809]

Si a=2025 et b=2500, on trouvera la liste suivante

[2025, 2116, 2209, 2304, 2401, 2500]

La liste obtenue peut très bien être vide.

Séparer en indices pairs et indices impairs

On pourra utiliser que si L1 et L2 sont des listes alors L1 + L2 est une nouvelle liste qui donne accès au contenu de L1 suivi du contenu de L2.

Soit une liste L. En utilisant une liste en compréhension, séparer cette liste en deux listes :

  • la liste IP des éléments de L d’indices pairs
  • la liste II des éléments de L d’indices impairs

Par exemple, si L = [31, 12, 9, 65, 81, 42] alors IP = [31, 9, 81] et II = [12, 65, 42].

Tables de multiplication via des listes en compréhension

On représente la table de multiplication d’un entier \(m\) par une liste de ces 10 premiers multiples \(m\), \(2m\), \(3m\), etc ; par exemple la table de 7 est la liste

[7, 14, 21, 28, 35, 42, 49, 56, 63, 70]

Des tables de multiplication sont représentées par une liste de tables de multiplication et donc par une liste de listes.

Ecrire un code qui à partir d’une liste L d’entiers strictement positifs renvoie les tables de multiplication des éléments de L. Par exemple, si L=[5, 0, 7, 42] alors la table sera la liste suivante :

[[5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70],
[42, 84, 126, 168, 210, 252, 294, 336, 378, 420]]

Décompression de liste via des listes en compréhension

On donne une liste L de listes de deux éléments, par exemple

L=[["Pomme", 2], ["Kiwi", 3], ["Orange", 0], ["Poire", 2]]

Chaque liste de deux éléments dans L est formée d’une chaîne de caractères s et d’un entier positif ou nul n. On demande de contruire une liste M de chaînes contenant pour chaque liste [s, n] figurant dans L, la chaîne s mais répétée n fois.

Avec l’exemple ci-dessus, la liste M attendue est :

['Pomme', 'Pomme', 'Kiwi', 'Kiwi', 'Kiwi', 'Poire', 'Poire']

On utilisera une liste en compréhension ainsi que le décompactage de la variable de contrôle d’une boucle for.

Liste des diviseurs via des listes en compréhension

On donne une liste L d’entiers strictement positifs et on demande d’écrire un code qui génère une liste D des listes de diviseurs de chaque entier de L. On rappelle que d est un diviseur de N signifie juste que N est un multiple de d autrement dit que \(\mathtt{N=d\times q}\) pour un certain entier q. Par exemple, si L=[2049,2027, 43, 25] alors D=[[1, 3, 683, 2049], [1, 2027], [1, 43], [1, 5, 25]].

Les diviseurs apparaîtront dans l’ordre croissant.

Voisins d’un coefficient d’une matrice

Etant donné une matrice \(\mathtt{A}\), on appelle voisin d’un élément \(\mathtt{a}\) de cette matrice, tout élément de la matrice situé soit juste à droite, soit juste à gauche soit juste au-dessus soit juste en-dessous de \(\mathtt{a}\). Par exemple si

\(\mathtt{A=\begin{pmatrix}23 & 5 & 7 & 14 & 16 \\4 & 6 & 13 & 20 & 22 \\10 & 12 & 19 & 21 & 3 \\11 & 18 & 25 & 2 & 9\end{pmatrix}}\)

alors :

  • 9 a deux voisins (2 et 3)
  • 22 a trois voisins (20, 16 et 3)
  • 20 a quatre voisins (14, 13, 21 et 22)

On rappelle que les indices de lignes ou de colonnes sont comptés à partir de 0.

  1. Ecrire un code qui, partant de la matrice \(\mathtt{A}\) et de deux indices valides i et j de la matrice, construit une liste de tous les voisins de l’élément \(\mathtt{A[i][j]}\) de la matrice \(\mathtt{A}\). On pourra supposer que \(\mathtt{i}\) et \(\mathtt{j}\) représentent des indices valides de \(\mathtt{A}\). L’ordre des éléments dans la liste renvoyée sera sans importance.

    Avec la matrice \(\mathtt{A}\) ci-dessus, on obtiendra par exemple que

    • pour (i, j) = (3,4) la liste cherchée est [2, 3]
    • pour (i, j) = (1,3) la liste cherchée est [14, 13, 21, 22]

    De même, si \(\mathtt{A}\) est la matrice représentée ci-dessous :

    \(\mathtt{A=\begin{pmatrix}23\\4 \\10\\11\end{pmatrix}}\)

    alors

    • pour (i, j) = (1,0) la liste cherchée est [23, 10]
    • pour (i, j) = (0,0) la liste cherchée est [4]

    Votre code doit être capable de traiter n’importe quelle matrice, y compris une matrice de taille \(\mathtt{1\times 1}\).

  2. Ecrire un code qui, à partir d’une matrice \(\mathtt{A}\) d’entiers, renvoie V, une liste de listes de listes et telle que si i et j sont des indices valides de \(\mathtt{A}\) alors \(\mathtt{V[i][j]}\) est la liste des voisins de \(\mathtt{A[i][j]}\) dans la matrice \(\mathtt{A}\).

Extraction d’une colonne d’une matrice

Soit une matrice \(\mathtt{A}\). Écrire un code qui à partir d’un entier positif \(\mathtt{j}\) renvoie, sous forme de liste, la colonne d’indice \(\mathtt{j}\) de la matrice \(\mathtt{A}\). On suppose que l’indice de colonne est valide.

Par exemple si

\(A=\left(\begin{array}{rrrrr}71 & 21 & 96 & 90 & 42 \\10 & 18 & 51 & 98 & 15 \\93 & 93 & 84 & 13 & 95 \\39 & 50 & 47 & 65 & 90\end{array}\right)\)

et si \(\mathtt{j=3}\) alors la colonne demandée est

[90, 98, 13, 65]

Autant que possible, on utilisera une liste en compréhension. On testera avec une matrice aléatoire.

Trace d’une matrice

Si une matrice \(M\) est carrée, on appelle trace de \(M\) la somme des coefficients de la diagonale descendante de \(M\) issue du coefficient en position (0,0).

Ecrire un code qui partant d’une matrice carrée calcule sa trace. Par exemple, si

\(M=\left(\begin{array}{rrr}17 & 14 & 11 \\10 & 10 & 79 \\88 & 52 & 49\end{array}\right)\)

alors sa trace vaut 76.

Vérifier en appelant la méthode M.trace.

Transposée d’une matrice

On donne une matrice \(A\) ayant \(\mathtt{n}\) lignes et \(\mathtt{p}\) colonnes. On demande de construire la matrice \(\mathtt{B}\) dite transposée de \(\mathtt{A}\) et qui s’obtient ainsi : chaque ligne de \(\mathtt{B}\) est la colonne de \(\mathtt{A}\) ayant même indice que la ligne. Par exemple, si \(A=\left(\begin{array}{rrrr}4 & 4 & 8 & 2 \\4 & 0 & 0 & 4 \\9 & 3 & 2 & 0\end{array}\right)\)

alors \(B=\left(\begin{array}{rrr}4 & 4 & 9 \\4 & 0 & 3 \\8 & 0 & 2 \\2 & 4 & 0\end{array}\right)\)

Etre une matrice diagonale

Une matrice \(\mathtt{D}\) est dite diagonale si elle est carrée et si les éléments hors de la diagonale dont nuls. Ecrire une code qui teste sir une matrice à coefficients entiers donnée est diagonale ou non.

Etre une matrice triangulaire supérieure

Soit \(\mathtt{M}\) une matrice carrée d’ordre \(\mathtt{n}\). On dit que \(\mathtt{M}\) est une matrice triangulaire supérieure si tous les coefficients de \(\mathtt{M}\) situés strictement sous la diagonale sont nuls.

  1. Ecrire un code qui définisse un booléen estTriangulaireSup qui vaut True si \(\mathtt{M}\) est une matrice triangulaire supérieure et False sinon.
  2. Ecrire un code qui génère une matrice triangulaire supérieure d’ordre n donné.

Etre une matrice scalaire

Une matrice \(\mathtt{M}\) est dite scalaire si les trois conditions suivantes sont satisfaites :

  • elle est carrée
  • les éléments hors de la diagonale (principale) sont nuls
  • les éléments de la diagonale ont tous la même valeur.

Une matrice M étant donnée, écrire un booléen estScalaire qui vaut True si M est une matrice scalaire et False sinon.

Les trois matrices suivantes ne sont pas des matrices scalaires :

\(\left(\begin{array}{rrr} 5 & 0 & 2 \\ 0 & 5 & 0 \\ 0 & 0 & 5 \end{array}\right), \quad\left(\begin{array}{rrr} 5 & 0 & 0 \\ 0 & 4 & 0 \\ 0 & 0 & 5 \end{array}\right), \quad\left(\begin{array}{rrr} 5 & 0 & 0 \\ 0 & 5 & 0 \end{array}\right).\)

La matrice suivante est scalaire :

\(\left(\begin{array}{rrr} 5 & 0 & 0 \\ 0 & 5 & 0 \\ 0 & 0 & 5 \end{array}\right).\)

Générer une matrice diagonale

On donne une liste \(\mathtt{L}\) de \(\mathtt{n}\) entiers. Ecrire la matrice \(\mathtt{D}\) carrée d’ordre \(\mathtt{n}\) telle que

  • la diagonale (principale) de \(\mathtt{D}\) est identique à la liste \(\mathtt{L}\)
  • les termes non diagonaux de \(\mathtt{D}\) sont nuls.

Anti-transposée d’une matrice

Soit une matrice carrée \(\mathtt{M}\). On appelle diagonale secondaire de \(\mathtt{M}\) la suite de coefficients de \(\mathtt{M}\) allant du coin inférieur gauche de \(\mathtt{M}\) vers le coin supérieur droit de \(\mathtt{M}\). Par exemple, si \(\mathtt{M}\) est la matrice

\(\left(\begin{array}{rrr}5&3&2\\6&4&0\\7&1&9 \end{array}\right)\)

la diagonale secondaire de \(\mathtt{M}\) est \(\mathtt{7, 4, 2}\).

On appelle anti-transposée d’une matrice carrée \(\mathtt{M}\) la matrice \(\mathtt{N}\) dont les coefficients sont les symétriques des coefficients de \(\mathtt{M}\) par rapport à la diagonale secondaire. Par exemple, si \(\mathtt{M}\) est la matrice

\(\left(\begin{array}{rrr}5&3&2\\6&4&0\\7&1&9 \end{array}\right)\) alors son anti-transposée est \(\left(\begin{array}{rrr}9&0&2\\1&4&3\\7&6&5 \end{array}\right)\)

Ecrire un code qui à partir d’une matrice \(\mathtt{M}\) construise l’anti-transposée \(\mathtt{N}\) de la matrice carrée \(\mathtt{M}\). Pour cela, on procédera comme suit :

  • on construira une matrice nulle \(\mathtt{N}\) de même taille que \(\mathtt{M}\) ;
  • soit une position (ligne, colonne) dans \(\mathtt{M}\) d’indices \(\mathtt{(a, b)}\) et soient \(\mathtt{(c,d)}\) les indices de la position symétrique de \(\mathtt{(a,b)}\) par rapport à la diagonale secondaire de \(\mathtt{M}\). Que valent alors \(\mathtt{a+d}\) et \(\mathtt{ b+c}\) en fonction de la taille \(\mathtt{n}\) de la matrice \(\mathtt{M}\) ?

Bords d’une matrice

Étant donné une matrice \(\mathtt{M}\), on appelle 1er bord de \(\mathtt{M}\) les coefficients de cette matrice qui sont soit sur la première ligne, soit sur la dernière ligne, soit sur la première colonne, soit sur la dernière colonne.

De même, on appelle 2ème bord de \(\mathtt{M}\) les coefficients de \(\mathtt{M}\) qui ne sont pas sur le 1er bord et qui sont soit sur la 2ème ligne, soit sur la 2ème colonne soit sur l’avant-dernière ligne soit sur l’avant dernière colonne.

De manière analogue, on définit le 3ème, 4ème, etc bord de \(\mathtt{M}\).

Ecrire un code qui construit la matrice de taille \(\mathtt{n\times n}\) dont

  • le 1er bord est formé uniquement de coefficients valant 1,
  • le 2ème bord uniquement de coefficients valant 2

et ainsi de suite jusqu’à ce que tous les coefficients de la matrice soient énumérés.

Par exemple, si \(\mathtt{n=5}\) le code doit produire la matrice suivante :

\(\mathtt{A=\begin{pmatrix}1 & 1 & 1 & 1 & 1 \\1 & 2 & 2 & 2 & 1 \\1 & 2 & 3 & 2 & 1 \\1 & 2 & 2 & 2 & 1 \\1 & 1 & 1 & 1 & 1\end{pmatrix}}\)

ou encore si \(\mathtt{n=15}\) la matrice recherchée doit être la matrice suivante :

\(\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrrrrrrrrrrrrrr}1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\1 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 1 \\1 & 2 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 4 & 4 & 4 & 4 & 4 & 4 & 4 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 5 & 5 & 5 & 5 & 5 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 6 & 6 & 6 & 6 & 6 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 6 & 7 & 7 & 7 & 6 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 7 & 6 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 6 & 7 & 7 & 7 & 6 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 6 & 6 & 6 & 6 & 6 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 5 & 5 & 5 & 5 & 5 & 5 & 5 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 4 & 4 & 4 & 4 & 4 & 4 & 4 & 4 & 4 & 3 & 2 & 1 \\1 & 2 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 3 & 2 & 1 \\1 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 1 \\1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1\end{array}\right)\)

Cet exercice est très formateur et vaut la peine de chercher. Y arriver est surtout une affaire d’organisation et de méticulosité. Utilisez une feuille quadrillée. Cet exercice peut nécessiter pas loin d’une heure de recherche (mais il peut être fait en 10 minutes si vous êtes algorithmicien).

Quelques indications :

  • On commencera par créer une matrice d’ordre n remplie de 0
  • Regardez quelques exemples et vérifiez que si n est l’ordre de M alors le nombre de bords est m = (n+1)//2.
  • Cela veut dire que si vous bouclez m fois avec une boucle for, vous pouvez remplir la matrice, bord après bord
  • Pour remplir chaque bord avec la valeur disons k, utiliser la position ligne/colonne (i, j) du coin supérieur gauche du bord ; vous remarquerez que i = j. Vous noterez qu’il y a aussi un lien entre i et k.
  • En connaissant la longueur du bord, vous pouvez calculer la position des quatre coins. Ensuite, avec 4 boucles for successives, vous placerez la valeur k dans les quatre côtés du bord (en fait, une seule boucle remplissant les 4 côtés du bord suffit).

Implémenter le produit de deux matrices

On va définir le produit de deux matrices \(M\) et \(N\). Attention, cette opération n’est pas complètement intuitive.

Avant d’expliquer comment se calcule le produit \(M\times N\) de deux matrices, il faut préciser que les entiers suivants doivent être égaux :

  • nombre de colonnes de la première matrice \(M\)
  • nombre de lignes de la deuxième matrice \(N\).

Ainsi, le produit d’une matrice de taille \(4\times 2\) et d’une matrice de taille \(2\times 3\) aura bien un sens mais pas le produit d’une matrice \(2\times 3\) et d’une matrice \(2\times 3\).

Soient donc \(A\) une matrice de taille \(n\times m\) et \(B\) une matrice de taille \(m\times p\). On définit le produit \(C:=A\times B\) comme étant la matrice \(C\) de taille \(n\times p\) telle que

\(c_{i,j}=a_{i,0}\times b_{0,j}+ a_{i,1}\times b_{1,j}+\cdots +a_{i,m-1}\times b_{m-1,j}\)

Ainsi

\(\begin{pmatrix} 10 &40& 20& 30 \\20& 70& 50 &90 \end{pmatrix} \begin{pmatrix}3 &1 &5 \\2& 4& 7 \\8 &0 &6 \\1& 5& 4 \end{pmatrix}=\begin{pmatrix}300& 320& 570\\690& 750& 1250\end{pmatrix}\)

Comment est obtenu le coefficient 570 du produit \(AB\) à la ligne d’indice \(0\) et la colonne d’indice \(2\) ? On examine la ligne \(\begin{pmatrix} 10 &40& 20& 30 \end{pmatrix}\) d’indice \(0\) de \(A\) et la colonne \(\begin{pmatrix}5 \\7 \\6 \\ 4\end{pmatrix}\) d’indice 2 de \(B\) et on fait le calcul suivant :

\(\begin{pmatrix} 10 &40& 20& 30 \end{pmatrix}\times \begin{pmatrix}5 \\7 \\6 \\ 4\end{pmatrix} =10\times 5 + 40\times 7 + 20\times 6 + 30\times 4=570\)

Voilà pourquoi on dit que le produit matriciel s’effectue ligne par colonne.

Ecrire le code d’une fonction produit(A,B) qui renvoie la matrice produit \(\mathtt{AB}\). On supposera que la fonction produit(A,B) ne reçoit que des matrices \(\mathtt{A}\) et \(\mathtt{B}\) de tailles permettant d’effectuer leur produit.

On testera les deux matrices montrées ci-dessus dont les coefficients sont extractibles ci-dessous :

[[10, 40, 20, 30 ],[20, 70, 50, 90 ]]
[[3, 1, 5 ],[2, 4, 7 ],[8, 0, 6 ],[1, 5, 4 ]]

On vérifiera la justesse du calcul réalisé en comparant avec le résultat donné par la fonction np.matmul.

Générer un motif de matrice : équerres

Etant donné un entier \(\mathtt{n>0}\), on demande de construire la matrice \(\mathtt{M}\) suivante :

\(\left(\begin{array}{rrrrrr}0 & 1 & 2 & \dots & n-2 & n-1 \\1 & 1 & 2 & \dots & n-2 & n-1 \\2 & 2 & 2 & \dots & n-2 & n-1 \\\vdots & \vdots & \vdots & \dots & \vdots & \vdots \\n-2 & n-2 & n-2 & \dots & n-2 & n-1 \\n-1 & n-1 & n-1 & \dots & n-1 & n-1 \end{array}\right)\)

Explication : pour tout tout indice \(\mathtt{k\in\ens{0,\dots, n-1}}\), les \(\mathtt{k+1}\) premiers éléments de la ligne d’indice \(\mathtt{k}\) de \(\mathtt{M}\) ainsi que les \(\mathtt{k+1}\) premiers éléments de la colonne d’indice \(\mathtt{k}\) de \(\mathtt{M}\) valent tous \(\mathtt{k}\).

Générer une matrice tridiagonale

On donne trois entiers \(\mathtt{a}\), \(\mathtt{b}\) et \(\mathtt{c}\). On se donne aussi un entier \(\mathtt{n>0}\). Construire une matrice carrée \(\mathtt{M}\) d’ordre \(\mathtt{n}\) (dite « tridiagonale fg{}) dont

  • la diagonale juste au-dessus de la diagonale principale est formée uniquement du coefficient \(\mathtt{a}\),
  • la diagonale principale est formée uniquement du coefficient \(\mathtt{b}\),
  • la diagonale juste en-dessous de la diagonale principale est formée uniquement du coefficient \(\mathtt{c}\),
  • les autres coefficients{} sont nuls

Par exemple, si \(\mathtt{a=4}\), \(\mathtt{b=2}\), \(\mathtt{c=1}\) et \(\mathtt{n=4}\) alors \(\mathtt{M}\) est la matrice suivante

\(\left(\begin{array}{rrrr}2&4&0&0\\1&2&4&0\\0&1&2&4\\0&0&1&2 \end{array}\right)\)

On commencera par créer une matrice nulle \(\mathtt{M}\) de taille \(\mathtt{n\times n}\) et on y placera ensuite les coefficients de la matrice à construire.

Insérer une ligne dans une matrice

On donne une matrice \(\mathtt{M}\) de taille \(\mathtt{n}\) lignes et \(\mathtt{p}\) colonnes. On donne une liste \(\mathtt{L}\) de \(\mathtt{p}\) nombres et on donne aussi un indice \(\mathtt{k\in\ens{0,\dots, n}}\). On demande de construire la matrice \(\mathtt{N}\) obtenue en insérant, dans la matrice \(\mathtt{M}\) une ligne dont les \(\mathtt{p}\) coefficients sont formés des éléments de la liste \(\mathtt{L}\) en sorte que l’indice de la nouvelle ligne dans \(\mathtt{N}\) soit \(\mathtt{k}\).

Exemple : pour \(\mathtt{k=2}\), on lit successivement \(\mathtt{M}\) et \(\mathtt{L}\) et la nouvelle matrice \(\mathtt{N}\) :

[[70 88 65]
 [22 67 58]
 [55 44 36]
 [12 60 53]]

[[10 80 26]]

[[70 88 65]
 [22 67 58]
 [10 80 26]
 [55 44 36]
 [12 60 53]]

On procédera comme suit :

  • construire une matrice nulle \(\mathtt{N}\) ayant la bonne taille
  • recopier là où il faut dans \(\mathtt{N}\), ligne par ligne, les coefficients demandés.

Insérer une colonne dans une matrice

On donne une matrice \(\mathtt{M}\) de taille \(\mathtt{n}\) lignes et \(\mathtt{p}\) colonnes. On donne une liste \(\mathtt{L}\) de \(\mathtt{n}\) nombres et on donne aussi un indice de colonne \(\mathtt{j\in\ens{1,\dots, p}}\). On demande de construire la matrice \(\mathtt{N}\) obtenue en insérant, dans la matrice \(\mathtt{M}\), entre la colonne d’indice \(\mathtt{j-1}\) et la colonne d’indice \(\mathtt{j}\) de \(\mathtt{M}\), une colonne dont les \(\mathtt{n}\) coefficients sont formés de éléments de la liste \(\mathtt{L}\). Il faudra créer une nouvelle matrice Par exemple, si \(\mathtt{M}\) est la matrice

\(\left(\begin{array}{rrrr}4&6&7&3\\5&0&1&2\\4&4&9&3\end{array}\right)\)

et si \(\mathtt{j=2}\) et si L =[2030, 2031, 2032] alors N est la matrice

\(\left(\begin{array}{rrrrr}4&6&2030&7&3\\5&0&2031&1&2\\4&4&2032&9&3\end{array}\right)\)

Inverser l’ordre des colonnes d’une matrice

On donne une matrice M et on demande de construire une nouvelle matrice N obtenue à partir de M en renversant l’ordre des colonnes. Par exemple, si M est la matrice

4 2 7 3
8 5 1 2
9 6 4 2

alors N est la matrice

3 7 2 4
2 1 5 8
2 4 6 9

Transformation de Sylvester

Etant donné une matrice carrée \(M\) de taille \(n\times n\), on appelle transformée de Sylvester de \(M\) la matrice \(S\) suivante :

\(S=\left[\begin{array}{c|c}M & M \\\hline M & -M\end{array}\right]\)

La matrice \(S\) est de de taille \(2n\times 2n\).

Par exemple, si \(M=\left(\begin{array}{rr}42 & 81 \\31 & 75\end{array}\right)\) alors sa transformée de Sylvester est

\(\left(\begin{array}{rr|rr}42 & 81 & 42 & 81 \\31 & 75 & 31 & 75 \\\hline 42 & 81 & -42 & -81 \\31 & 75 & -31 & -75\end{array}\right)\)

Étant donné une matrice M, construire la transformée de Sylvester de M.

Remplissage d’une grille en hélice

On se propose de remplir une grille de dimension \(n\times p\) par tous les entiers consécutifs entre 1 et \(np\) formant un motif en hélice. Voici le remplissage si \(n=6\) et \(p=7\) :

\(\left(\begin{array}{rrrrrrr}6 & 7 & 8 & 9 & 10 & 11 & 12 \\5 & 26 & 27 & 28 & 29 & 30 & 13 \\4 & 25 & 38 & 39 & 40 & 31 & 14 \\3 & 24 & 37 & 42 & 41 & 32 & 15 \\2 & 23 & 36 & 35 & 34 & 33 & 16 \\1 & 22 & 21 & 20 & 19 & 18 & 17\end{array}\right)\)

Le remplissage de la grille commence en bas à gauche et s’effectue vers le haut. On garde la direction courante sauf si la case suivante est hors de la grille ou si elle a déjà été remplie auquel cas on prend la direction à droite.