Données Matrice

Identification

Infoforall

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 : open document ou pdf

Evaluation ✎ : questions 02-04-10-11-12-14-15-16-17-21-22-23

1 - Matrice

En mathématiques, les matrices les plus siples sont des tableaux à deux dimensions et contenant des nombres.

En informatique, matrices sont des tableaux dont les éléments sont eux-mêmes des tableaux. On peut remplir les tableaux avec autre chose que des nombres, mais au final on y stocke une information binaire, donc... un nombre.

Voici 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  00 
B 0 🐲00 
C 0   🐲 
D 🧙   00
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 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 ?

1.1 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 100110
1 103110
2 100030
3 800011
4 101010
5 101000

Pour rappel :

1.2 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''.''.''.'

Pour rappel :

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 maîtrisez les tableaux ou que vous venez de faire l'activité sur les tableaux, 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 100110
1 103110
2 100030
3 800011
4 101010
5 101000

La réponse est évidente : nous allons faire un tableau contenant... des tableaux.

Un tableau par ligne

Commençons par créer 6 tableaux, un tableau pour chaque ligne.

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 programme ?

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])

...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é.

Un tableau contenant les 6 tableaux

On crée un tableau matrice dont les éléments sont les tableaux-lignes précédents.

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] ?

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])

...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

matrice[LIGNE][COLONNE]

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 (O, x, y) ci-dessous, cela revient à demander ceci :

matrice[y][x].

Système de coordonnées
Système de coordonnées Oxy habituel en informatique

Demander matrice[2][4] revient à récupérer le contenu de la case de ligne 2 et de colonne 4.

Encodage final habituel en Python

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] ?

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] ]

...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.

3 Les matrices avec Python

Définition

Une matrice est un tableau de tableaux dont tous les sous-tableaux ont le même nombre de cases.

Exemple et syntaxe
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] ]
Accès aux cases : matrice[LIGNE][COLONNE]

Lorsqu'on évalue matrice[2], on récupère l'élément d'indice 2 contenu dans le tableau matrice. Il s'agit donc d'une ligne (celle surlignée).

Lorsqu'on évalue matrice[2][4], on récupère l'élément d'indice 4 contenu dans le sous-tableau 2. Il s'agit donc du contenu dans cette case (celle en rouge).

Pour accéder à une case précise dans une matrice, il suffit donc de fournir d'abord son numéro de ligne puis son numéro de colonne.

4 - Lecture et affichage

Il nous reste à voir comment lire les cases une par une.

Ici, nous allons devoir utiliser une boucle POUR imbriquée dans une autre boucle POUR : pour chaque ligne de la matrice, lis chaque colonne de cette ligne

Commençons par voir ce que cela donne si on demande juste à afficher une "ligne" à la fois.

06° Utiliser ce programme pour vous convaincre qu'il affiche les tableaux qui encodent le contenu des lignes.

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])
[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]
Affichage des cases une par une

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.

4.1 Affichage propre d'une matrice à l'écran

Principe

A l'aide d'une boucle imbriquée, on affiche une à une les cases mais on rajouter une instruction ligne 16 : à chaque fois qu'on a totalement fini l'une des boucles internes, on passe à la ligne puisqu'on vient d'afficher toutes les colonnes d'une ligne.

On a besoin du nombre de lignes : len(matrice) renvoie le nombre de sous-tableaux et donc le nombre de lignes dans la matrice.

On a besoin du nombre de colonnes : len(matrice[0]) renvoie le nombre de cases dans le premier sous-tableau et donc le nombre de colonnes dans la matrice (tous les sous-tableaux ont la même taille).

Exemple avec accès par indice
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()
Exemple avec accès direct
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] ] for ligne in matrice: # Pour chaque tableau-ligne possible for valeur in ligne: # Pour chaque valeur sur cette ligne print(valeur, end=" ") print()

07° Utiliser la version finale de l'affichage contenu dans la partie du cours précédente 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

Effacer ensuite les lignes 10 à 16 et réaliser vous-même cette partie à l'aide de vos connaissances et souvenirs.

Reste qu'afficher des numéros sur la console, c'est pas top.

4.2 STRING : fonctions chr() ord()

ASCII et UNICODE

ASCII est la table de conversion standard entre caractère-valeur. Le nombre est encodé dans UN OCTET. Néanmoins, la norme ASCII n'impose que les associations caractère-valeur de 0 à 127.

Les valeurs de 128 à 255 ne sont donc pas fixées et les caractères représentés varient d'une tableau à une autre.

UNICODE est une association caractère-valeur qui comporte tous les glyphes et caractères que l'humanité utilise. UNICODE ne préoccupe pas de savoir comment on encode concrètement ce nombre en mémoire. L'encodage concrèt le plus courant d'UNICODE est l'encodage UTF-8.

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.

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. N'oubliez pas de mettre Ox devant.

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 ' '

Vous savez maintenant afficher un plateau de jeu. Nous verrons comment gérer le jeu en lui-même dans le cadre d'un projet.

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.

graphe représentant les relations

Comment représenter cela en informatique ?

L'une des solutions est d'utilisation un tableau de tableaux, une matrice.

On pourrait dire :

  • 1 s'ils se connaissent,
  • 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 1111000
B connaît 1110010
C connaît 1110000
D connaît 1001110
E connaît 0001101
F connaît 0101111
G connaît 0000111

De cette façon, 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. Donc, 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 encore adapté, ou pas adapté.

✎ 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.

Rappel de l'encodage des relations :

  • 1 pour un simple ami
  • 2 pour un ami proche
  • -1 pour quelqu'un qu'on déteste

✎ 17° Modéliser dans une seconde matrice, matrice2 la situation ci-dessous.

graphe de l'exercice

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.

Voilà. Beaucoup plus de contenu et d'implémentations efficaces en classe terminale.

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.

La grille

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

systeme de coordonnees

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.

et
et
donne l'impression de

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.
Vue Manage Packages

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.

Lorsque nous aurez vu un peu l'interface graphique que vous avez manipulé aujourd'hui, vous pourrez éventuellement aller un peu plus loin sur ce thème.

Si vous voulez voir la détection de contour, allez voir l'activité SNT -Contour d'une image.

Conclusion d'activité

Nous avons vu qu'on encode les matrices comme des tableaux de tableaux.

On les retrouve dans l'encodage des plateaux de jeux, des graphes ou des images.

Chacun des éléments de la matrice peut être un simple nombre, un simple caractère ou une structure de données plus complexe, comme un tuple.

On retiendra enfin que pour atteindre la case voulue, il faut alors fournir deux indices plutôt qu'un seul.

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.