7 - Encodage des matrices
Nous allons utiliser les tableaux de Python pour encoder et mettre en mémoire des choses comme des tableaux de jeu ou des relations sociales.
Prérequis : Python - Introduction aux index numériques et exercices sur les tableaux.
Logiciel nécessaire pour l'activité : Python 3 : Thonny, IDLE ...
Documents de cours :
Evaluation ✎ : questions 02-04-10-11-12-14-15-16-17-21-22-23
1 - Matrice
En mathématiques, les matrices sont des tableaux de nombres.
En informatique, on peut remplir les tableaux avec autre chose que des nombres, mais au final on y stocke une information.
Regardons par exemple un plateau de jeu de 6 cases sur 6 cases comportant :
- Des couloirs (en blanc)
- Des murs (en noir)
- Des monstres (🐲)
- Le personnage du joueur (🧙 ou 🧚)
Plateau de jeu | Colonne | ||||||
1 | 2 | 3 | 4 | 5 | 6 | ||
---|---|---|---|---|---|---|---|
Ligne | A | 0 | 0 | 0 | |||
B | 0 | 🐲 | 0 | 0 | |||
C | 0 | 🐲 | |||||
D | 🧙 | 0 | 0 | ||||
E | 0 | 0 | 0 | ||||
F | 0 | 0 |
Les petits dessins de cette page sont issus de l'UNICODE. Pour les utiliser sur une page HTML, il suffit de taper  où FFFFF correspond au code hexadécimal que vous aurez trouvé sur une page décrivant ces caractères.
On obtient 🧙 en tapant 🧹 dans le code HTML de la page.
On obtient 🧚 en tapant 🧚 dans le code HTML de la page.
On obtient 🐲 en tapant 🐲 dans le code HTML de la page.
Néanmoins, il faut faire attention : les caractères UNICODE ne sont pas toujours représentés exactement de la même façon sur les différents systèmes. A titre d'exemple, voici comment le plateau de jeu apparaît sur mon système qui lit exactement le même code HTML que le votre :

Comment faire pour encoder le contenu des cases ?
Méthode 1 : on utilise des nombres
On pourrait choisir d'encoder la présence :
- d'une case vide correspondant à un couloir par 0
- d'une case contenant un mur par 1
- d'une case contenant un monstre par 3
- de la case contenant le personnage du joueur par 8
Exemple visuel :
1 0 0 1 1 0
1 0 3 1 1 0
1 0 0 0 3 0
8 0 0 0 1 1
1 0 1 0 1 0
1 0 1 0 0 0
On représente notre plateau de jeu sous la forme d'un tableau à double entrée. Attention : le premier indice est 0, pas 1 !
Plateau de jeu | Colonne | ||||||
0 | 1 | 2 | 3 | 4 | 5 | ||
---|---|---|---|---|---|---|---|
Ligne | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
1 | 1 | 0 | 3 | 1 | 1 | 0 | |
2 | 1 | 0 | 0 | 0 | 3 | 0 | |
3 | 8 | 0 | 0 | 0 | 1 | 1 | |
4 | 1 | 0 | 1 | 0 | 1 | 0 | |
5 | 1 | 0 | 1 | 0 | 0 | 0 |
Méthode 2 : on utilise des caractères
On pourrait choisir d'encoder la présence :
- d'une case vide correspondant à un couloir par .
- d'une case contenant un mur par X
- d'une case contenant un monstre par m
- de la case contenant le personnage par o
Exemple visuel :
X . . X X .
X . m X X .
X . . . m .
o . . . X X
X . X . X .
X . X . . .
Plateau de jeu | Colonne | ||||||
0 | 1 | 2 | 3 | 4 | 5 | ||
---|---|---|---|---|---|---|---|
Ligne | 0 | 'X' | '.' | '.' | 'X' | 'X' | '.' |
1 | 'X' | '.' | 'm' | 'X' | 'X' | '.' | |
2 | 'X' | '.' | '.' | '.' | 'm' | '.' | |
3 | 'o' | '.' | '.' | '.' | 'X' | 'X' | |
4 | 'X' | '.' | 'X' | '.' | 'X' | '.' | |
5 | 'X' | '.' | 'X' | '.' | '.' | '.' |
Bref, il y autant d'encodages possibles du contenu des cases qu'on veut.
Question : comment peut-on parvenir à stocker un tableau à double entrée ?
2 - Rappel : tableau avec Python
A savoir par coeur
t désigne l'adresse du tableau (et pas directement son contenu).
i désigne l'indice d'une case, son numéro (et surtout pas son contenu).
t[i] désigne le contenu de la case d'indice i.
Si vous maitrisez les tableaux ou que vous venez de faire l'activité sur les tableaux et leurs indices, vous pouvez passer directement à la partie 3.
1 - Déclaration d'un tableau en Python
Les éléments délimitateurs sont les crochets. Chaque élément du tableau est séparé des autres par une virgule.
Exemple
>>> t = ['a', 'b', 'c']
>>> t
['a', 'b', 'c']
>>> type(t)
<class 'list'>
On voit que les "tableaux" sont en python des objets de classe list
.
2 - Nombre d'éléments stockés
On peut utiliser la fonction native len() pour obtenir le nombre d'éléments (ici des caractères pour le premier cas et des floats pour le deuxième cas) stockés dans le tableau.
Exemple
>>> t = ['a', 'b', 'c']
>>> len(t)
3
>>> t = [12.45, 5.0, 17.9, 4.32]
>>> len(t)
4
On remarquera bien que dans les deux cas, nous avons des types list : on ne trouve pas d'indication directe sur le type des données contenues dans le tableau.
La variable t désigne ici le conteneur, pas le contenu.
3 - Accès à l'un des éléments
Pour accéder à l'un des éléments en particuliers, on peut noter le nom de la variable suivi de crochets et y placer l'indice du numéro voulu.
Exemple
>>> t = ['a', 'b', 'c']
>>> t[0]
'a'
>>> t[1]
'b'
>>> t[2]
'c'
La correspondance indice-élément donne ceci sur l'exemple
Indice | 0 | 1 | 2 |
Elément | 'a' | 'b' | 'c' |
4 - Lecture des éléments un à un (méthode classique)
On peut aussi lire les éléments un par un en utilisant une boucle FOR couplée à la fonction len() pour connaitre le nombre de cases et donc savoir le numéro de la dernière.
Ainsi avec
notes = [15, 18, 8, 10, 12, 15, 20, 5, 12, 17, 12, 10, 18, 4]
Nous avons 14 éléments avec un indice 0-1-2-3-4-5-6-7-8-9-10-11-12-13.
Indice | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
Elément | 15 | 18 | 8 | 10 | 12 | 15 | 20 | 5 | 12 | 17 | 12 | 10 | 18 | 4 |
Il faudrait donc utiliser for i in range(14):
Exemple
1
2
3
4
5 | notes = [15, 18, 8, 10, 12, 15, 20, 5, 12, 17, 12, 10, 18, 4]
nb = len(notes)
for i in range(nb):
print(notes[i])
|
Ce programme va afficher ceci dans la console :
15
18
8
10
12
15
20
5
12
17
12
10
18
4
3 - Création d'une matrice
Comment parvenir à encoder concrétement une matrice alors que nous avons rencontré uniquement des tableaux à une dimension pour l'instant ?
Plateau de jeu | Colonne | ||||||
0 | 1 | 2 | 3 | 4 | 5 | ||
---|---|---|---|---|---|---|---|
Ligne | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
1 | 1 | 0 | 3 | 1 | 1 | 0 | |
2 | 1 | 0 | 0 | 0 | 3 | 0 | |
3 | 8 | 0 | 0 | 0 | 1 | 1 | |
4 | 1 | 0 | 1 | 0 | 1 | 0 | |
5 | 1 | 0 | 1 | 0 | 0 | 0 |
La réponse est évidente : nous allons faire un tableau contenant... des tableaux.
1er solution : tableau de lignes
Commençons par créer 6 tableaux qui vont contenir le contenu des lignes 0 à 5.
1
2
3
4
5
6
7
8 | ligne0 = [1, 0, 0, 1, 1, 0]
ligne1 = [1, 0, 3, 1, 1, 0]
ligne2 = [1, 0, 0, 0, 3, 0]
ligne3 = [8, 0, 0, 0, 1, 1]
ligne4 = [1, 0, 1, 0, 1, 0]
ligne5 = [1, 0, 1, 0, 0, 0]
print(ligne2[4])
|
01° Que va afficher ce court programme ?
...CORRECTION...
La ligne de code 8 indique d'aller chercher dans le tableau ligne2 l'élément d'indice 4.
3 | ligne2 = [1, 0, 0, 0, 3, 0]
|
La console va donc afficher 3.
02° Que va afficher le programme si on remplace la ligne 8 par celle-ci ?
8 | print(ligne4[1])
|
...CORRECTION...
La ligne de code 8 indique d'aller chercher dans le tableau ligne4 l'élément d'indice 1.
5 | ligne4 = [1, 0, 1, 0, 1, 0]
|
La console va donc afficher 0.
Voyons maintenant comment parvenir à encoder la matrice dans sa totalité.
Il suffit de créer le tableau matrice dont les éléments sont les tableaux-lignes.
1
2
3
4
5
6
7
8
9
10 | ligne0 = [1, 0, 0, 1, 1, 0]
ligne1 = [1, 0, 3, 1, 1, 0]
ligne2 = [1, 0, 0, 0, 3, 0]
ligne3 = [8, 0, 0, 0, 1, 1]
ligne4 = [1, 0, 1, 0, 1, 0]
ligne5 = [1, 0, 1, 0, 0, 0]
matrice = [ligne0, ligne1, ligne2, ligne3, ligne4, ligne5]
print(matrice[2][4])
|
03° Que contient matrice[2] ? Que contient matrice[2][4] ?
...CORRECTION...
Explications courtes
Le contenu de matrice :
8 | matrice = [ligne0, ligne1, ligne2, ligne3, ligne4, ligne5]
|
Le contenu de ligne2 :
3 | ligne2 = [1, 0, 0, 0, 3, 0]
|
On a donc :
matrice[2][4] qui est évaluée en
ligne2[4] qui est évaluée à
3
04° Que contient matrice[1] ? Que contient matrice[1][0] ?
...CORRECTION...
Explications courtes
Le contenu de matrice :
8 | matrice = [ligne0, ligne1, ligne2, ligne3, ligne4, ligne5]
|
Le contenu de ligne1 :
2 | ligne1 = [1, 0, 3, 1, 1, 0]
|
On a donc :
matrice[1][0] qui est évaluée en
ligne1[0] qui est évaluée à
1
On obtient donc le contenu de la case d'une ligne et d'une colonne en tapant simplement
matrice[ligne][colonne]
Avec le système de coordonnées xy ci-dessous, cela revient à demander ceci :
matrice[y][x].

Demander matrice[2][4] revient à récupérer le contenu de la case de ligne 2 et de colonne 4.
Le problème, c'est qu'on prefère habituellement donner les coordonnées sous la forme (x,y), soit (colonne, ligne). Si vous voulez créer un tableau dans le "bon sens" (x puis y), allez voir la FAQ.
Nous allons maintenant directement définir les tableaux-lignes à l'intérieur du tableau-matrice en nous passant des variables intermédiaires.
1
2
3
4
5
6
7
8 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
- Les éléments du tableau matrice sont bien des tableaux-lignes encodant le contenu des lignes.
- Notez la présence de la virgule pour séparer les tableaux-lignes. Le passage à la ligne n'est que cosmétique.
05° Que va-t-on récupérer si on tape matrice[2] ?
...CORRECTION...
Voici l'élément qui correspond à l'index 2 :
1
2
3
4
5
6
7
8 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
|
On récupère donc le contenu de ce que nous avions nommé ligne2 auparavant. La première ligne avait bien été nommée ligne0 pour ne pas avoir de décalage entre le numéro de la ligne et l'index de l'élément.
4 - Lecture et affichage
Il nous reste à voir comment lire les cases une par une.
Ici, petite nouveauté : une boucle POUR imbriquée dans une autre boucle POUR.
Commençons par voir ce que cela donne si on demande juste à afficher une "ligne" à la fois.
1
2
3
4
5
6
7
8
9
10
11
12
13 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
nL = len(matrice) # Nbr de lignes dans la matrice
for y in range(nL): # Pour chaque indice y de ligne possible
print(matrice[y])
|
06° Utiliser ce programme pour vous convaincre qu'il affiche les tableaux qui encodent le contenu des lignes.
[1, 0, 0, 1, 1, 0]
[1, 0, 3, 1, 1, 0]
[1, 0, 0, 0, 3, 0]
[8, 0, 0, 0, 1, 1]
[1, 0, 1, 0, 1, 0]
[1, 0, 1, 0, 0, 0]
Si on veut vraiment afficher le contenu des cases, il faut, pour chaque ligne, lire une à une toutes les cases de la ligne.
Cela revient à imbriquer une boucle POUR dans la précédente.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
nL = len(matrice) # Nbr de lignes dans la matrice
nC = len(matrice[0]) # Nbr de colonnes dans la première ligne
for y in range(nL): # Pour chaque indice y de ligne possible
for x in range(nC): # Pour chaque indice de colonne x possible
print(matrice[y][x], end=" ")
|
On indique à la fonction native print() de conclure chaque affichage avec un espace plutôt qu'avec le passage à la ligne par défaut. Résultat :
1 0 0 1 1 0 1 0 3 1 1 0 1 0 0 0 3 0 8 0 0 0 1 1 1 0 1 0 1 0 1 0 1 0 0 0
Cette fois, on a les cases mais tout est en ligne.
Il reste donc simplement à rajouter une petite instruction ligne 16 : une fois sorti de la deuxième boucle, on veut passer à la ligne : on vient d'afficher toutes les cases d'une ligne.
Version finale de l'affichage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
nL = len(matrice) # Nbr de lignes dans la matrice
nC = len(matrice[0]) # Nbr de colonnes dans la première ligne
for y in range(nL): # Pour chaque indice y de ligne possible
for x in range(nC): # Pour chaque indice de colonne x possible
print(matrice[y][x], end=" ")
print()
|
07° Utiliser la version finale de l'affichage pour vous convaincre qu'il affiche les cases en passant à la ligne à la fin de chaque ligne et plus les tableaux-lignes.
1 0 0 1 1 0
1 0 3 1 1 0
1 0 0 0 3 0
8 0 0 0 1 1
1 0 1 0 1 0
1 0 1 0 0 0
Reste qu'afficher des numéros sur la console, c'est pas top.
(RAPPEL) UNICODE - Fonctions natives ord() et chr()
ASCII est une table de conversion qu'on peut considérer comme un standard. On travaille sur un octet et on dispose donc de valeurs allant de 0 à 255. La norme ASCII n'impose que les associations caractère-valeur de 0 à 127.
UNICODE est lui une simple association entre un nombre et un caractère. Il comporte quasiment tous les glyphes et caractères que l'humanité utilise. On ne préoccupe pas de savoir comment on encode concrétement ce nombre en mémoire. UNICODE est une simple association caractère-valeur. Nous verrons en fin d'année comment UTF-8 permet d'encoder la valeur UNICODE.
Fonction native ord()
En Python, on peut trouver la valeur UNICODE d'un caractère en utilisant la fonction native ord.
>>> ord('A')
65
>>> ord('B')
66
>>> ord('a')
97
>>> ord('z')
122
>>> ord('ç')
231
Ainsi, lorsque Python doit classer des mots, il place les mots commençant par ç après les mots en z puisque la valeur unicode du ç est supérieure à celle du z !
Fonction native chr()
C'est l'inverse : on lui donne un caractère et elle renvoie la valeur UNICODE correspondante.
>>> chr(65)
'A'
>>> chr(231)
'ç'
>>> chr(9821)
'♝'
>>> chr(9822)
'♞'
>>> chr(9823)
'♟'
08° A l'aide la fonction native chr, chercher les caractères associées aux valeurs
67
9760
9786
11035
...CORRECTION...
>>> chr(67)
'C'
>>> chr(9760)
'☠'
>>> chr(9786)
'☺'
>>> chr(11035)
'■'
UNICODE et affichage dans une console Python
Ce serait super de pouvoir afficher notre magicien et nos dragons dans la console Python pour notre plateau, non ?
Ben oui. Mais ce n'est pas possible avec la plupart des implémentations pour l'instant.
L'affichage des caractères UNICODE ne fonctionne souvent pas au delà de 65535
dans les implémentations actuelles de Thonny.
Si vous demandez l'affichage d'un mage, ça ne fonctionnera pas malheureusement. C'est pour cela que je suis parti sur les caractères de substitution précédents.
Pour voir les caractères disponibles, allez sur cette très bonne page de Wikipedia : les UNICODE - les caractères.
Vous devriez trouver votre bonheur dans ces caractères, même si le choix est un peu plus limité. Attention néanmoins, les valeurs sont données en hexadécimal. Il va falloir attendre cette leçon pour savoir décoder facilement les valeurs UNICODE fournies.
09° Vous avez ci-dessous le code comportant la matrice encodant le plateau sous forme de nombres. On vous donnne la procédure afficher_plateau() qui contient les lignes précédentes. Seule nouveauté : on veut qu'une fonction carac_case() se charge de renvoyer le caractère qu'on veut voir afficher. Par exemple :
- Le personne (☺) plutôt que le code 8
- Le monstre (☠) plutôt que le code 3
- Un mur(■) plutôt que le code 1
- Pour le reste, un espace.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | # Déclaration des variables globales
matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
# Déclaration des fonctions
def afficher_plateau(plateau):
'''Procédure qui affiche le plateau dans la console, plateau étant une matrice contenant des entiers'''
nL = len(matrice) # Nbr de lignes dans la matrice
nC = len(matrice[0]) # Nbr de colonnes dans la première ligne
for y in range(nL): # Pour chaque indice y de ligne possible
for x in range(nC): # Pour chaque indice x de colonne
caractere = carac_case(matrice[y][x])
print(caractere, end=" ")
print()
def carac_case(code):
'''Fonction qui renvoie un caractère à afficher en fonction du code entier fourni
:: param code(int) :: un entier correspondant à l'affichage voulu
:: return (str) :: le caractère correspondant au code fourni
'''
return ' '
|
A vous de compléter la fonction carac_case() pour qu'elle réponde aux attendus de la documentation fournie.
L'appel ci-dessous dans la console devrait produire ceci :
>>> afficher_plateau(matrice)
■ ■ ■
■ ☠ ■ ■
■ ☠
☺ ■ ■
■ ■ ■
■ ■
...CORRECTION...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 | # Déclaration des variables globales
matrice = [
[1, 0, 0, 1, 1, 0],
[1, 0, 3, 1, 1, 0],
[1, 0, 0, 0, 3, 0],
[8, 0, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 0]
]
# Déclaration des fonctions
def afficher_plateau(plateau):
'''Procédure qui affiche le plateau dans la console, plateau étant une matrice contenant des entiers'''
nL = len(matrice) # Nbr de lignes dans la matrice
nC = len(matrice[0]) # Nbr de colonnes dans la première ligne
for y in range(nL): # Pour chaque indice y de ligne
for x in range(nC): # Pour chaque indice x de colonne
caractere = carac_case(matrice[y][x])
print(caractere, end=" ")
print()
def carac_case(code):
'''Fonction qui renvoie un caractère à afficher en fonction du code entier fourni
:: param code(int) :: un entier correspondant à l'affichage voulu
:: return (str) :: le caractère correspondant au code fourni
'''
if code == 8 :
return chr(9786)
elif code == 3 :
return chr(9760)
elif code == 1 :
return chr(11035)
else :
return ' '
|
5 - Matrice et relations sociales
Mais les matrices ne servent pas qu'à encoder un plateau de jeu. Loin de là même. Les matrices ont un rôle fondamental dans toutes modélisation sérieuses des phénomènes physiques.
Et pas que. Prenons le cas du graphe suivant qui représente des relations sociales entre les personnes. Les sommets sont les personnes et les arêtes représentent le fait qu'elles se connaissent.

Comment représenter cela en informatique ?
L'une des solutions est d'utilisation un tableau de tableaux, une matrice.
On pourrait dire :
- 1 s'il se connaisse,
- 0 sinon.
On pourra même rajouter des choses plus complexes :
- -1 s'ils se détestent,
- 2 s'ils sont vraiment très proches...
Souvenez-vous de votre cours de 2nd : les arêtes peuvent avoir une valeur associée, un poids.
Graphe de relations | ||||||||
A | B | C | D | E | F | G | ||
---|---|---|---|---|---|---|---|---|
A connaît | 1 | 1 | 1 | 1 | 0 | 0 | 0 | |
B connaît | 1 | 1 | 1 | 0 | 0 | 1 | 0 | |
C connaît | 1 | 1 | 1 | 0 | 0 | 0 | 0 | |
D connaît | 1 | 0 | 0 | 1 | 1 | 1 | 0 | |
E connaît | 0 | 0 | 0 | 1 | 1 | 0 | 1 | |
F connaît | 0 | 1 | 0 | 1 | 1 | 1 | 1 | |
G connaît | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
Du coup, on peut facilement récupérer pas mal de choses. Un exemple parmi tant d'autres : combien de contacts possède une personne ? Il suffit de faire la somme de sa ligne (et de retirer un puisqu'on considère qu'elle se connaît elle-même).
Si on regarde G, la somme de sa ligne donne 3. Du coup, on voit qu'il connait 2 autres personnes.
Voici l'encodage de cette situation :
1
2
3
4
5
6
7
8
9
10
11 | # Déclaration des variables globales
matrice = [
[1, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 0, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 1],
[0, 1, 0, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1]
]
|
✎ 10° Donner le nombre de contacts du noeud D (en expliquant clairement la démarche à partir de la lecture de la matrice).
✎ 11° Donner si D et G se connaissent (en expliquant clairement la démarche à partir de la lecture de la matrice).
Il n'en reste pas moins que faire cela à la main, c'est un peu long. Passons à la programmation.
✎ 12° Utiliser le programme ci-dessous qui possède une fonction trouve_indice() qui permet de trouver le numéro d'indice d'une personne dont on connaît le nom (A, B, C...).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | # Déclaration des variables globales
matrice = [
[1, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 0, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 1],
[0, 1, 0, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1]
]
# Déclaration des fonctions
def trouve_indice(nom):
'''Fonction qui renvoie le numéro d'indice d'un nom valide
:: param nom(str) :: un string exploitable en tant que nom
:: return (int) :: le numéro d'index correspondant ou None
'''
if nom == 'A' :
return 0
elif nom == 'B' :
return 1
elif nom == 'C' :
return 2
elif nom == 'D' :
return 3
elif nom == 'E' :
return 4
elif nom == 'F' :
return 5
elif nom == 'G' :
return 6
return None # Inutile en réalité : ce serait fait automatiquement
|
Question : Fournir une explication à destination d'un débutant pour expliquer pourquoi la fonction ne renvoie pas systématiquement None alors qu'il s'agit de la dernière ligne du code.
>>> trouve_indice('B')
1
>>> trouve_indice('Ada')
Voici maintenant la suite du programme : on insère deux fonctions (qui utilisent à l'interne trouve_indice()).
- La fonction nb_connaissances() renvoie le nombre de personnes que connaît la personne dont le nom est fourni.
- La fonction se_connaissent() renvoie True si les deux personnes dont les noms sont fournis se connaissent.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 | # Déclaration des variables globales
matrice = [
[1, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0],
[1, 1, 1, 0, 0, 0, 0],
[1, 0, 0, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 1],
[0, 1, 0, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1]
]
# Déclaration des fonctions
def trouve_indice(nom):
'''Fonction qui renvoie le numéro d'indice d'un nom valide
:: param nom(str) :: un string exploitable en tant que nom
:: return (int) :: le numéro d'indice correspondant ou None
'''
if nom == 'A' :
return 0
elif nom == 'B' :
return 1
elif nom == 'C' :
return 2
elif nom == 'D' :
return 3
elif nom == 'E' :
return 4
elif nom == 'F' :
return 5
elif nom == 'G' :
return 6
return None # Inutile en réalité : ce serait fait automatiquement
def nb_connaissances(contacts, nom):
'''Renvoi le nombre d'amis connus par la personne identifiée par nom
:: parama contacts(list) :: une matrice encodant les relations
:: param nom(str) :: un nom VALIDE
:: return (int) :: le nombre de contact connus, de 0 à +
'''
nb = 0
num = trouve_indice(nom)
for i in range(len(contacts[num])) :
if contacts[num][i] != 0 :
nb = nb + 1
return nb - 1
def se_connaissent(contacts, nom1, nom2):
'''Fonction qui renvoie True si les deux personnes d'index num1 et num2 se connaissent
:: param nom1(str) :: un nom VALIDE
:: param nom2(str) :: un nom VALIDE
:: return (bool) :: True si les deux personnes se connaissent, False sinon
'''
num1 = trouve_indice(nom1)
num2 = trouve_indice(nom2)
return contacts[num1][num2] != 0
|
13° Tester le nouveau programme avec les instructions ci-dessous après les avoir placées en mémoire.
>>> nb_connaissances(matrice, 'A')
3
>>> nb_connaissances(matrice, 'G')
2
>>> se_connaissent(matrice, 'A', 'G')
False
>>> se_connaissent(matrice, 'A', 'B')
True
✎ 14° Fournir les explications qu'on pourrait donner à un débutant sur le fonctionnement de la fonction se_connaissent().
✎ 15° Expliquer (en justifiant vos réponses) si la fonction nb_connaissances() fonctionnerait encore pour compter le nombre de connaissances si l'on rajoutait la variante suivante dans la matrice
- 1 pour un simple ami
- 2 pour un ami proche
- -1 pour quelqu'un qu'on déteste
Conclure en expliquant si le nom de la fonction est bien choisi ou pas du coup.
✎ 16° En vous inspirant de la question précédente, créer une fonction nbr_amis_proches() qui ne renvoie que le nombre d'amis proches. Créer ensuite une fonction nbr_amis() qui compte le nombre total d'amis, proches ou pas proches.
✎ 17° Modéliser dans une seconde matrice, matrice2 la situation ci-dessous.

Attention, cette fois les relations ne sont pas nécessairement dans les deux sens. On dit que le graphe est orienté.
Par exemple, B connait C, D et E car il y a bien une flèche de B vers C et de B vers D et de B vers E.
Par contre, B ne connaît pas A. A connaît B par contre.
Lancer le programme et fournir quelques tests de Console pour voir s'il fonctionne.
6 - Images
Voyons une dernière application concrête d'encodage d'informations utilisant les matrices : les images.
Commençons par trois rappels.
1 - Pixels : les images sont composées de pixels.

2 - Système d'axes : l'axe x va bien à droite mais y va vers le bas, contrairement au repère cartésien habituel.

3 - Couleur d'un pixel : toutes les couleurs sont obtenues en activant les DEL RGB (Red - Green - Blue) en leur fournissant chacune une intensité sur un octet (donc entre 0 et 255).
Les 8 cas typiques (ici, les intensités valent soit 0(noir), soit 255 (couleur au maximum).
Voici un pixel vu de très très près. On a activé à fond les intensités RGB (red-green-blue) ou RVB (rouge-vert-bleu) :
En 4x4 pixels de plus loin
En 10x10 pixels de plus loin
En 30 x 30 pixels
Si on continue encore à réduire la taille affichée du pixel :
Voici donc comment on parvient à obtenir du blanc : lorsqu'on envoie du Rouge, du Vert et du Bleu à grande intensité, les 3 cônes de l'oeil sont activés et le cerveau interprète cela en BLANC.
Nous allons maintenant manipuler des images et changer les valeurs RGB des différents pixels.
Une image est donc une matrice mais chaque case ne contient pas une valeur mais trois valeurs R-G-B. Vous allez voir que chaque case de la matrice contient un tuple (r, g, b)
.
Pour manipuler les images avec Python et Thonny, il faut possèder un module spécifique, qui gère les images : Pillow.
Commençons par voir si vous avez le module.
18° Tapez ceci dans la console pour voir si cela déclenche une erreur.
>>> from PIL import Image
Si c'est le cas :
- Ouvrir le menu Tool
- Sélectionner Manage Packages
- Faire une recherche sur Pillow puis installer la bibliothèque. Ca peut être un peu long.

19° Téléchargez une image de type png ou jpg sur le Web et placez la dans votre dossier de travail. Choisissez une image possèdant pas mal de couleurs différentes si possible.
20° Enregistrer le programme ci-dessous avec Thonny en le plaçant dans le même répertoire que votre image. Il faudra modifier le nom du fichier stocké dans la variable base de façon à ce qu'il corresponde à celui de votre fichier-image.
📁 VotreDossierDeTravail
📄 votre_programme.py
📄 votre_image.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 | from PIL import Image as Img
# Nom du fichier-image original
base = "votre_image.png"
# Nom du fichier-image modifié
sauvegarde = "modification.jpg"
def filtrer_image(nom:str) -> Image:
'''Fonction qui renvoie un objet-image après l'avoir filtré pixel par pixel'''
# Création de l'objet-image propre à Python
ref_image = Img.open(nom)
# Récupération de la taille de l'image
largeur, hauteur = ref_image.size
# Si image avec 3 couches : R G B
if len(ref_image.getbands()) == 3 :
for x in range(largeur):
for y in range(hauteur):
# On récupère les valeurs RGB du pixel de coordonnées (x,y)
rouge, vert, bleu = ref_image.getpixel( (x,y) )
# On transforme les valeurs RGB avec la fonction de filtrage
rouge, vert, bleu = filtrage(rouge, vert, bleu)
# On transforme l'image-Python en mémoire
ref_image.putpixel( (x, y) , (rouge, vert, bleu) )
# Sinon, si image avec 4 couches : R G B + Transparence(alpha)
if len(ref_image.getbands()) == 4 :
for x in range(largeur):
for y in range(hauteur):
# On récupère les valeurs RGB du pixel de coordonnées (x,y)
rouge, vert, bleu, alpha = ref_image.getpixel( (x,y) )
# On transforme les valeurs RGB avec la fonction de filtrage
rouge, vert, bleu = filtrage(rouge, vert, bleu)
# On transforme l'image-Python en mémoire
ref_image.putpixel( (x,y), (rouge, vert, bleu, alpha) )
return ref_image
def filtrage(r:int, g:int, b:int) -> tuple:
'''Fonction qui renvoie des valeurs r,g,b après les avoir modifiées'''
red = g
green = b
blue = r
return (red, green, blue)
# Création d'une nouvelle image à partir de la source originale
nouvelle = filtrer_image(base)
# Affichage de l'objet-image Python à l'écran
nouvelle.show()
# Sauvegarde de l'image dans un nouveau fichier-image
nouvelle.save(sauvegarde)
|
Vous n'aurez qu'à apporter des modifications sur ces parties du programme :
1
2
3
4
5
6
7
8
9
10
11
12
13 | from PIL import Image as Img
# Nom du fichier-image original
base = "maisons.jpg"
# Nom du fichier-image modifié
sauvegarde = "modification.jpg"
def filtrage(r:int, g:int, b:int) -> tuple:
'''Fonction qui renvoie des valeurs r,g,b après les avoir modifiées'''
red = g
green = b
blue = r
return (red, green, blue)
|
Il faudra parfois changer les noms d'ouverture et d'enregistrement.
Que fait la fonction filtrage ? Pour l'instant, elle inverse les valeurs des intensités RGB sur les lignes en jaunes.
✎ 21° Tester le programme sur votre image.
FILTRE ROUGE : Modifier maintenant le programme : on veut maintenant ne garder que l'intensité R identique à l'image originale et mettre les autres à 0.
✎ 22° FILTRE BLEU : Idem mais avec l'intensité bleue uniquement.
✎ 23° IMAGE GRISE : Remplacer les trois intensités R, G et B par la moyenne des trois. On pourra utiliser la division euclidienne en Python en utilisant //
plutôt que simplement /
.
Par exemple red = (r+g+b) // 3
Si les trois intensités ont la même valeur, votre oeil percevra du gris.
Si on veut transformer une image en couleur, il faut utiliser des formules particulières pour avoir un rendu correct car nos yeux ne sont pas sensibles de la même manière à la lumière rouge, verte ou bleue. ils sont beaucoup plus sensibles à la lumière verte qu'aux lumières bleus et rouges.
24° IMAGE GRISE 2 : Remplacer les trois intensités R, G et B par la formule suivante. On pourra utiliser la division euclidienne en Python en utilisant //
plutôt que simplement /
.
Par exemple red = (21*r + 71*g + 8*b) // 100
Si les trois intensités ont la même valeur, votre oeil percevra du gris.
1
2
3
4
5
6
7
8
9
10
11
12
13 | from PIL import Image as Img
# Nom du fichier-image original
base = "maisons.jpg"
# Nom du fichier-image modifié
sauvegarde = "modification.jpg"
def filtrage(r:int, g:int, b:int) -> tuple:
'''Fonction qui renvoie des valeurs r,g,b après les avoir modifiées'''
red = (21*r + 71*g + 8*b) // 100
green = (21*r + 71*g + 8*b) // 100
blue = (21*r + 71*g + 8*b) // 100
return (red, green, blue)
|
Beaucoup de choses à faire sur les images. En SNT, vous avez peut-être fait cette activité en allant jusqu'à la détection de contour.
7 - FAQ
Comment avoir une matrice XY plutôt que YX
Cette partie vous montre comment pouvoir écrire matrice[x][y]
plutôt que matrice[y][x]
.
1e solution : tableau de colonnes pour écrire [x][y]
La première solution serait de créer un tableau de colonnes plutôt qu'un tableau de lignes. Pas top, mais fonctionnelle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | # Anciennes données en lignes : claires pour un humain mais [y][x]
ligne0 = [1, 0, 0, 1, 1, 0]
ligne1 = [1, 0, 3, 1, 1, 0]
ligne2 = [1, 0, 0, 0, 3, 0]
ligne3 = [8, 0, 0, 0, 1, 1]
ligne4 = [1, 0, 1, 0, 1, 0]
ligne5 = [1, 0, 1, 0, 0, 0]
# Nouvelles données en colonnes : permet d'écrire [x][y]
colonne0 = [1, 1, 1, 8, 1, 1]
colonne1 = [0, 0, 0, 0, 0, 0]
colonne2 = [0, 3, 0, 0, 1, 1]
colonne3 = [1, 1, 0, 0, 0, 0]
colonne4 = [1, 1, 3, 1, 1, 0]
colonne5 = [0, 0, 0, 1, 0, 0]
matrice = [colonne0, colonne1, colonne2, colonne3, colonne4, colonne5]
print(matrice[4][2])
|
2e solution : transformer le tableau de lignes en tableau de colonnes
Deux étapes : on crée un tableau pour qu'il soit facile à taper et à lire pour un humain et ensuite on le transforme en tableau de colonnes automatiquement.
Le probmème vient juste de la double place mémoire lors de la phase de création.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | # Etape 1 : on crée les données en les présentant en lignes
ligne0 = [1, 0, 0, 1, 1, 0]
ligne1 = [1, 0, 3, 1, 1, 0]
ligne2 = [1, 0, 0, 0, 3, 0]
ligne3 = [8, 0, 0, 0, 1, 1]
ligne4 = [1, 0, 1, 0, 1, 0]
ligne5 = [1, 0, 1, 0, 0, 0]
matrice = [ligne0, ligne1, ligne2, ligne3, ligne4, ligne5]
# Etape 2 : on crée le tableau de colonnes à partir de la matrice de base
nbrL = len(matrice)
nbrC = len(matrice[0])
mat2 = [ [ matrice[y][x] for y in range(nbrL) ] for x in range(nbrC) ]
# Etape 3 : on vérifie que les deux matrices pointent bien la même case
print(matrice[2][4])
print(mat2[4][2])
|
La ligne centrale est la ligne 17 : on crée un tableau de tableaux en utilisant deux créations par compréhension imbriquées l'une dans l'autre.
.Remarquez bien que cette ligne 17 revient à écrire mat2[x][y] = matrice[y][x]
.
Activité publiée le 10 06 2020
Dernière modification : 10 06 2020
Auteur : ows. h.