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 :

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

Et là où il faut faire attention, c'est que 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 tout cela ?

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

Comme vous le voyez, on représente notre tableau de jeu sous la forme d'un tableau à double entrée. Les entrées sont le numéro de la ligne et de la colonne.

Par contre, attention : le premier indice est 0, pas 1 ou A.

Plateau de jeu Colonne
0 1 2 3 4 5
Ligne 0 100110
1 103110
2 100030
3 800011
4 101010
5 101000

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 du joueur 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 le veut.

Par contre, comment peut-on parvenir à stocker un tableau à double entrée ?

2 - Rappel : tableau avec Python

Rien de neuf ici. Si vous maitrisez les tableaux ou que vous venez de faire l'activité sur les index numérotés, 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

>>> tableau = ['a', 'b', 'c'] >>> tableau ['a', 'b', 'c'] >>> type(tableau) <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

>>> tableau = ['a', 'b', 'c'] >>> nbr = len(tableau) >>> nbr 3
>>> tableau = [12.45, 5.0, 17.9, 4.32] >>> nbr = len(tableau) >>> nbr 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 tableau 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'index du numéro voulu.

Exemple

>>> tableau = ['a', 'b', 'c'] >>> element_0 = tableau[0] >>> element_0 'a' >>> element_1 = tableau[1] >>> element_1 'b' >>> element_2 = tableau[2] >>> element_2 'c'

La correspondance index - élément donne ceci sur l'exemple

Index 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 la valeur limite à placer pour l'indice d'incrémentation de la boucle.

Ainsi avec

 t_notes_A = [15, 18, 8, 10, 12, 15, 20, 5, 12, 17, 12, 10, 18, 4] 

Nous avons 14 éléments avec un index 0-1-2-3-4-5-6-7-8-9-10-11-12-13.

Index 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 index in range(14) :

Exemple

1 2 3 4 5
notes = [15, 18, 8, 10, 12, 15, 20, 5, 12, 17, 12, 10, 18, 4] nbr = len(notes) for index in range(nbr) : print(notes[index])

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

Alors, comment parvenir à encoder une matrice (notre tableau à double entrée) 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.

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

Bien et comment peut-on parvenir à encoder la matrice dans sa totalité ?

Il suffit de créer un tableau dont les éléments sont les tableaux de 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]

Du coup :

matrice[2][4]

ligne2[4]

3

Explications longues

Chercher matrice[2] revient à chercher l'élément d'index 2 de ce tableau.

Voici le contenu de matrice :

8
matrice = [ligne0, ligne1, ligne2, ligne3, ligne4, ligne5]

On voit que matrice[2] fait donc référence à ligne2.


Chercher matrice[2][4] veut dire de chercher d'abord l'élément d'index 2 de matrice puis l'index 4 de l'élément qu'on a trouvé.

On peut donc dire que matrice[2][4] revient ici à chercher ligne2[4].

Le contenu de ligne2 est :

3
ligne2 = [1, 0, 0, 0, 3, 0]

On peut donc dire que matrice[2][4] renvoie à 3.

✎ 04° Que contient matrice[1] ? Que contient matrice[1][0] ?

Tel que nous avons défini notre tableau, on obtient donc le contenu codé de la case de la ligne ligne et de la colonne colonne en tapant simplement
matrice[ligne][colonne]

Avec le système de coordonnées ci-dessous, cela revient à demander ceci :
matrice[y][x].

Du coup, demander matrice[2][4] revient à demander le contenu de la case de ligne 2 et de colonne 4.

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

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.

D'ailleurs, il est temps de nous passer des variables lignes, qui ne servent pas vraiment à grand chose :

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 de ce tableau-matrice sont bien des tableaux 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. Le caractère séparateur est bien la virgule.

On voit bien que les données des lignes sont bien intégrées au tableau, sans avoir besoin de leur attribuer une variable. En plus, si on veut rajouter des lignes, il suffit de la placer dans la matrice.

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 la ligne d'index 0 à la ligne d'index max 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 ce contenu des cases, il faut, pour chaque ligne, lire une à une tous les éléments 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 la ligne d'index 0 à la ligne d'index max for x in range(nC) : # Pour la colonne 0 jusqu'à la max print(matrice[y][x], end=" ")

On indique à la fonction au print de conclure chaque affichage avec un simple 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

Ah... Cette fois, on a les cases mais tout est en ligne.

Il reste donc simplement à rajouter une petite instruction : 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 la ligne d'index 0 à la ligne d'index max for x in range(nC) : # Pour la colonne 0 jusqu'à la max 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.

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

Nous allons réaliser en cours d'année un jeu graphique basé sur ce genre de plateau de jeu. Reste qu'afficher des numéros sur la console, c'est pas top.

UNICODE

L'ordinateur ne sait pas stocker les caractères. Il stocke simplement un nombre qui correspond à un caractére.

Les 127 premières valeurs correspondent à ce qu'on nomme les caractères ASCII. Avant l'invention d'UNICODE, ils étaient les seules associations Caractère-Numéro normalisées. Il existait plusieurs possibilités d'associations pour les caractères de valeur 128 et plus.

Par exemple, le A a pour valeur 65.

En Python, pour obtenir la valeur UNICODE d'un caractère, il faut utiilser la fonction native ord.

>>> ord('A') 65 >>> ord('B') 66 >>> ord('a') 97 >>> ord('z') 122 >>> ord('ç') 231

On peut également faire l'inverse : obtenir le caractère à partir de son numéro UNICODE. Pour cela, on utilise la fonction native chr.

>>> chr(65) 'A' >>> chr(66) 'B' >>> chr(97) 'a' >>> chr(122) 'z' >>> chr(231) 'ç'

08° A l'aide la fonction native chr, chercher les caractères associées aux valeurs

  • 6710
  • 262010, soit 0x2620 avec Python
  • 263A16, soit 0x263A avec Python
  • 2B1B16, soit 0x25A0 avec Python

...CORRECTION...

>>> chr(67) 'C' >>> chr(0x2620) '☠' >>> chr(0x263A) '☺' >>> chr(0x25A0) '■'
UNICODE et affichage 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 qu'entre 000016 et FFFF16. Ces caractères sont les caractères PMB, pour Plan Multilingue de Base.

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

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 le code précédent. 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 la ligne d'index 0 à la ligne d'index max for x in range(nC) : # Pour la colonne 0 jusqu'à la max 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'instruction afficher_plateau dans le Shell 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 la ligne d'index 0 à la ligne d'index max for x in range(nC) : # Pour la colonne 0 jusqu'à la max 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(0x263A) elif code == 3 : return chr(0x2620) elif code == 1 : return chr(0x25A0) 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 noeuds 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 tableau, 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.

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

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 en expliquant clairement la démarche à partir de la lecture de la matrice le nombre de contacts du noeud D.

✎ 11° Donner en expliquant clairement la démarche à partir de la lecture de la matrice si D et G se connaissent.

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_index qui permet de trouver le numéro d'index 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_index(nom) : '''Fonction qui renvoie le numéro d'index 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_index('B') 1 >>> trouve_index('Ada')

Voici maintenant la suite du code : on insère deux fonctions (qui utilisent à l'interne trouve_index).

  • La fonction nbr_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_index(nom) : '''Fonction qui renvoie le numéro d'index 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 def nbr_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 à + ''' nombre = 0 num = trouve_index(nom) for index in range(len(contacts[num])) : if contacts[num][index] != 0 : nombre = nombre + 1 return nombre - 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_index(nom1) num2 = trouve_index(nom2) return contacts[num1][num2] != 0

13° Tester le nouveau programme avec les instructions Shell ci-dessous après les avoir placées en mémoire.

>>> nbr_connaissances(matrice, 'A') 3 >>> nbr_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 le code de la fonction nbr_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 ne compte que le nombre total d'amis et d'amis proches.

✎ 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 Shell pour voir s'il fonctionne.

Voilà. Beaucoup plus de contenu et d'implémentation efficace 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 le Shell 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
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) : '''Fonction qui renvoie un objet-image après l'avoir filtrer pixel par pixel''' # Création de l'objet-image propre à Python ref_image = Img.open(nom) # Lecture et action sur les pixels, un par un largeur, hauteur = ref_image.size 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) ) return ref_image def filtrage(r, g, b) : '''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, g, b) : '''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.

✎ 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, g, b) : '''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 -Filtrage.

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.