Académique Documents
Professionnel Documents
Culture Documents
Tp informatique semestre 1
I L’informatique en cpge
1 Python au concours
• Le langage de programmation utilisé en cpge PTSI est Python. Cela signifie que :
* à l’écrit : à l’épreuve Informatique et Modélisation de systèmes physiques, les réponses à de nombreuses questions
consistent à écrire du code Python 1 (au stylo, sur la copie) ;
* à l’oral : un ordinateur avec Python est mis à la disposition du candidat pour l’épreuve TP Sciences Industrielles,
et un exercice de l’épreuve Mathématiques et Algorithmique se déroule sur un ordinateur avec Python.
2 Ressources
• Pour travailler Python tout seul :
* la documentation officielle : docs.python.org/3/
* les cours sur openclassrooms : fr.openclassrooms.com/informatique/python/cours
* le site de l’association France-ioi : france-ioi.org/algo/chapters.php
* le livre open source de Gérard Swinnen : inforef.be/swi/python.htm
* le cours de Robert Cordeau et Laurent Pointal : perso.limsi.fr/pointal/python:courspython3
* et de nombreux autres. . .
• Il existe aussi les PEP (Python Enhancement Proposals), notamment le numéro 8, qui donne des recommandations
(très fortes) sur la manière d’écrire du code Python (peps.python.org/pep-0008/).
• Le memento disponible sur agora est celui de l’épreuve orale Mathématiques et algorithmique. Remarque : à l’épreuve
écrite, il n’y a pas de memento.
2 Premier programme
Q 1. Exécuter le code suivant :
1 import math
2
3 def hypothenuse(a,b):
4 """Renvoyer la longueur de l'hypothénuse d'un triangle
5 Arguments : les longueurs des 2 autres côtés
6 """
7 c = math.sqrt(a**2 + b**2)
8 return c
9
10 L1 = 42 # Voici un commentaire
11 L2 = 5
12 L3 = hypothenuse(L1, L2)
13 print("Longueur de l'hypothénuse : ", L3)
14
15 x = 6.5
16 y = 7.8
17 print('Résultat de ce deuxième calcul de longueur : ', hypothenuse(x, y))
Tout semble normal ? Bien. Cela n’a l’air de rien, mais il y a beaucoup de choses à dire. . .
4. Par exemple Google Colab, pour ne citer que celui-là. . .
5. Il y en a d’autres installés au lycée : IDLEX (dans le dossier Sciences Industrielles), IDLE (dans le dossier Mathématiques), à votre préférence.
6. Par défaut, avec IDLE, la console seule apparaît, et pour obtenir l’éditeur, il faut faire File, New File.
7. Mais au fait, est-ce que np.arange(0, 3, 1) renvoie [0, 1, 2, 3] ou bien [0, 1, 2] ? → Je teste sur la ligne de commande. (C’est la
deuxième réponse qui est la bonne. Vous comprendrez bientôt.)
Info S1 – page 3/ 43
Remarque : tout ce qui suit un caractère # est un commentaire. C’est ignoré par l’interpréteur, et ne sert qu’à rendre
le code plus compréhensible pour celui qui le lit.
Autre remarque : tout le texte entre les triple guillemets n’est pas du code, mais une chaîne de documentation (voir
plus loin).
Autre remarque : le séparateur décimal est le point, et pas la virgule.
3 Fonction
Q 2. hypothenuse, math.sqrt et print sont des objets Python appelés fonctions. Taper type(hypothenuse) dans la
console : l’interpréteur affiche alors le type de l’objet hypothenuse. Faire de même pour print et math.sqrt.
Remarques :
• Ce que renvoie la fonction type ne parle pas de « type. . . » mais de « class. . . ». Pour faire simple, disons que
type et classe sont synonymes.
• Elle renvoie aussi « function_or_method » et pas « function ». Pour faire simple, disons qu’une méthode est
une catégorie particulière de fonction.
• Une fonction Python est très proche de ce qu’est une fonction en mathématiques : elle admet 0, 1, 2 ou plus paramètres
(ou arguments), fait quelque chose, et retourne (ou renvoie) 0, 1, 2 ou plus objets.
Remarque : en fait, une fonction Python ne renvoie jamais 0 objet. Par défaut, elle renvoie toujours l’objet None, qui
est un objet très spécial, qui veut dire « rien », mais qui n’est pas vraiment rien 8 , puisque c’est un objet.
• Une fonction peut être déjà intégrée (built-in) dans le langage Python de base (exemple : print).
• Une fonction peut être déjà intégrée dans un module (ou bibliothèque) qui ne fait pas partie du langage Python de
base, mais qui peut être importée (exemple : math.sqrt). Les importations de module se font toujours au début du
code : ici, la ligne import math est au début du code. À cause de cette ligne, on a accès aux fonctions du module
math, dont math.sqrt (square root : racine carrée).
• Une fonction peut être définie par l’utilisateur (ici, par exemple : hypothenuse). Retenir la syntaxe :
* On commence par l’instruction (statement en anglais) def ;
* suivie du nom de la fonction ;
* suivi, entre parenthèses, des noms donnés aux paramètres (ou arguments) de la fonction (remarques : s’il n’y a
pas de paramètre, il y a quand même une paire de parenthèses avec rien dedans ; les paramètres sont séparés par
des virgules) ;
* suivis d’un « : » (deux-points) ;
* suivi d’un bloc de code constituant le corps de la fonction (= son contenu = ce qu’elle « fait ») : on sait où ce
bloc de code commence et où il se termine parce qu’il est indenté, c’est-à-dire décalé vers la droite par rapport
au reste du code.
On résume la syntaxe :
1 def nom_de_la_fonction(parametre1, parametre2, parametre3):
2 bloc de code qui n est PAS
3 exécuté maintenant mais qui SERA
4 exécuté quand la fonction sera appelée
5 return objet_renvoyé_1, objet_renvoyé_2
• Remarque sur le vocabulaire. On fait parfois une distinction entre paramètres (les noms donnés dans la définition de
la fonction) et arguments (les valeurs qui sont effectivement passées à la fonction lorsque elle est appelée, c’est-à-dire
que son code est exécuté). Mais cette distinction n’est pas faite partout. On ne la fait pas forcément dans ce cours.
• Une fonction peut contenir (ce n’est pas obligatoire, mais c’est très souvent le cas) une instruction return, suivie d’un
espace, puis de l’objet que la fonction renvoie (ou de plusieurs objets, séparés par des virgules). Lorsque l’interpréteur
rencontre l’instruction return, l’exécution de la fonction est interrompue (c’est-à-dire que s’il y a du code écrit après
dans la fonction, il n’est pas exécuté). Lorsqu’on écrit une fonction, il faut qu’il y ait une instruction return dans
son code.
8. Voir youtube.com/watch ?v=hz5xWgjSUlk, vers 55 s.
Info S1 – page 4/ 43
• Pour appeler une fonction (i.e. pour que son contenu soit exécuté), il faut écrire son nom, suivi des arguments qui
lui sont passés (séparés par des virgules, le tout encadré par des parenthèses). Si la fonction renvoie une valeur et
qu’on souhaite la récupérer, il faut procéder à une affectation en plaçant devant le nom de la fonction un objet suivi
d’un signe =. Par exemple, la ligne L3 = hypothenuse(L1, L2) affecte à la variable L3 ce qu’a retourné la fonction
hypothenuse().
• Une bonne pratique de programmation consiste à documenter son code. Une des choses à faire pour cela est d’insérer,
au début du bloc de code définissant le contenu d’une fonction, une chaîne de documentation (docstring) commençant
et finissant par des « """ » (triple guillemets). Elle décrit sommairement ce que fait la fonction, ce qu’elle renvoie,
les arguments qu’il faut lui fournir.
Q 3. Exécuter help(hypothenuse) dans la console : cela affiche la chaîne de documentation. Essayer avec help(print),
avec help(math.sqrt).
4 L’indentation a un sens
• On insiste : en Python, l’indentation a un sens.
• L’indentation, c’est le fait que les lignes de code sont plus ou moins décalées (= indentées) par rapport au bord gauche
de la fenêtre de l’éditeur.
• Des lignes de code possédant la même indentation forment un bloc de code, qui peut correspondre :
* au contenu d’une fonction,
* au code exécuté plusieurs fois quand on utilise une boucle for ou while,
* au code exécuté ou pas quand on utilise une instruction conditionnelle (if).
• Pour indenter, on utilise 4 espaces (mais l’éditeur le fait tout seul en général). Pas plus, pas moins, pas de tabulation
(voir peps.python.org/pep-0008/).
• Il peut y avoir plusieurs niveaux d’indentation (si des boucles for sont imbriquées les unes dans les autres par exemple).
7 Chaîne de caractère
Q 9. Quel est le type de "l'hypothénuse" (demander à Python dans la console avec type("l'hypothénuse")) ? Et
pour 'Résultat' ?
• Le type str (i.e. string, i.e. chaîne) sert à représenter en mémoire des chaînes de caractères.
• Une chaîne de caractères est entourée de délimiteurs, qui peuvent être :
* soit des apostrophes : 'exemple'
* soit des guillemets : "exemple"
Si la chaîne de caractères contient une apostrophe ou un guillemet, mais pas les deux, on peut ruser. Si elle contient
les deux, on peut toujours se débrouiller. Exécuter le programme suivant pour voir comment on fait :
1 a = "Avec l'apostrophe"
2 b = 'Avec "guillemets"'
3 c = 'Avec l\'apostrophe et les "guillemets"'
4 d = "Avec l'apostrophe et les \"guillemets\""
5 print(type(a), type(b))
6 print(a)
Info S1 – page 6/ 43
7 print(b)
8 print(c)
9 print(d)
• \' et \" sont des caractères spéciaux (comme le retour à la ligne \n).
Q 10. Exécuter print('Un\nDeux') dans la console pour voir.
8 Importation de modules
• Il y a différentes façons d’importer un module. On teste ces différentes façons :
Q 11. Première façon de faire, sur un exemple :
1 import math
2 print(math.cos(math.pi))
Le fait d’utiliser import math donne accès à tous les objets définis dans le module math (sur cet exemple : la fonction
cos, le flottant pi, mais aussi d’autres comme sin, tan, exp, log. . .). Pour appeler ces objets, il faut utiliser le
préfixe math.
Q 12. Deuxième façon de faire :
1 from math import cos, pi
2 print(cos(pi))
Le fait d’utiliser from math import cos, pi donne accès aux objets cos et pi du module math (et pas aux autres
objets). Pour appeler ces objets, il est inutile d’utiliser le préfixe math.
Q 13. Variante de la deuxième façon :
1 from math import *
2 print(cos(pi))
Le fait d’utiliser from math import * donne accès à tous les objets définis dans le module math. Pour appeler ces
objets, il est inutile d’utiliser le préfixe math.
Q 14. Troisième façon de faire :
1 import math as m
2 print(m.cos(m.pi))
Le fait d’utiliser import math as m donne accès à tous les objets définis dans le module math. Pour appeler ces
objets, il faut utiliser le préfixe m (qui est un alias de math).
• C’est la troisième façon de faire qu’il faut en général utiliser :
* ce n’est pas le deuxième parce que sans préfixe, on ne peut plus distinguer, par exemple, la fonction cos du module
math de la fonction cos du module numpy (et ces deux fonctions n’ont peut-être pas le même comportement,
d’où des erreurs qui peuvent être très difficiles à déboguer) ;
* et ce n’est pas la première parce qu’il est pratique d’utiliser des alias, qui sont plus courts que le nom complet.
5 a = 3
6 b = 4
7 print('a =', a, ',', 'b =', b)
8 print(puiss(a, b)) # Premier appel à puiss
9 print()
10 a = 4
11 b = 3
12 print('a =', a, ',', 'b =', b)
13 print(puiss(b, a)) # Deuxième appel à puiss
14 print('a =', a, ',', 'b =', b)
Remarque : la fonction puiss n’est pas très utile en elle-même, elle ne sert qu’à illustrer le propos. L’intérêt de ce
code réside dans le fait d’afficher le contenu des objets a et b juste avant l’appel à la fonction puiss, et ensuite
pendant l’exécution de la fonction puiss.
• Lors du premier appel à puiss : la valeur de a définie ligne 5 est passée en premier argument à puiss, donc le
paramètre a (à l’intérieur de la fonction, i.e. lignes 1 à 3) reçoit la valeur 3. Pour b, c’est la même chose avec la valeur
4 et le deuxième argument.
• Lors du deuxième appel à puiss, la valeur de a définie ligne 10 est passée en deuxième argument à puiss, donc le
paramètre b (à l’intérieur de la fonction, i.e. lignes 1 à 3) reçoit la valeur 4. Et on a le comportement symétrique : la
valeur de b définie ligne 11 est passée en premier argument à puiss, donc le paramètre a (à l’intérieur de la fonction)
reçoit la valeur 3.
Dit autrement : « à l’intérieur » de la fonction, a vaut 4, et « à l’extérieur » de la fonction, a vaut 3.
• Avec le bon vocabulaire maintenant :
* Le a des lignes 5 et 10 est une variable globale. On dit aussi que sa portée est globale : cette variable existe (=
est visible) dans l’ensemble du code (= dans tout le programme).
* Le a des lignes 1 à 3 est une variable locale. On dit aussi que sa portée est locale : elle n’existe que localement,
à l’intérieur du bloc de code constitué par la fonction puiss.
Remarque : ces deux a ne sont pas la même variable.
• On parle de la portée lexicale (ou portée, ou scope en anglais) d’une variable : c’est la portion du programme à
l’intérieur de laquelle cette variable existe.
• Il peut y avoir conflit, par exemple si deux variables a sont visibles en même temps au même endroit du code. C’est
le cas ici à l’intérieur de la fonction puiss. Dans ce cas, l’interpréteur Python donne la priorité à la variable locale.
Mais cela ne veut pas dire que la variable globale est oubliée : on voit bien sur la dernière ligne affichée sur la console
que dès qu’on sort de la fonction puiss, a et b font à nouveau référence aux variables a et b globales.
5 Boucle while
Q 23. Une boucle while, comme son nom l’indique, répète l’exécution d’un bloc de code « tant que » une condition est
True. Cette condition est testée avant l’éventuelle exécution du bloc de code. Exemple d’utilisation d’une boucle
while :
1 i = 0
2 print(i)
3 while i<9:
4 i += 1
5 print(i)
• i += 1 aboutit à un résultat similaire à i = i + 1. Retenir que cela s’appelle une affectation augmentée. Il est
préférable d’utiliser une affectation augmentée plutôt qu’un affectation normale 14 . Parmi les affectations augmentées
on trouve +=, -=, *=, /=, **=. . .
• Remarque : ce code produit un effet strictement identique à :
1 for i in range(10):
2 print(i)
En fait, tout ce qu’on fait avec une boucle for peut être fait avec une boucle while, et réciproquement. C’est
simplement plus ou moins pratique selon les cas.
En général, si on connaît à l’avance le nombre d’itérations, c’est a priori une boucle for qui est la plus utile, et sinon
une boucle while.
• Noter la syntaxe :
while condition:
bloc de code à exécuter
tant que condition est True
14. Car c’est (parfois, quand l’opération peut se faire en place) plus économique en mémoire et temps de calcul.
Info S1 – page 11/ 43
6 Instructions break et continue
• L’instruction break interrompt l’exécution d’une boucle (for ou while). On la place en général dans une instruction
conditionnelle (if). On sort alors de la boucle, et l’exécution continue avec le code qui suit cette boucle.
• Lorsque la boucle concernée est contenue dans une fonction, un effet similaire (mais pas forcément identique) à un
break est obtenu avec un return (mais c’est bien l’exécution de la fonction qui est interrompue, et pas seulement
de la boucle).
• L’instruction continue interrompt également l’exécution d’une boucle, mais seulement de l’itération en cours. On
passe directement à l’itération suivante, sans exécuter la fin de l’itération en cours.
3 Lancé de dés
• Retenir que le module random contient des fonctions fournissant des nombres (pseudo-)aléatoires. En particulier, la
fonction randint(a,b) renvoie un nombre entier tiré aléatoirement entre a et b (ces valeurs étant toutes les deux
inclues), avec une densité de probabilité uniforme.
Q 28. Écrire puis tester une fonction gagne_double() qui :
• ne possède aucun paramètre ;
• réalise un tirage en « lançant deux dés 15 », c’est-à-dire en tirant au sort deux entiers compris entre 1 et 6 ;
• renvoie le booléen True si on a tiré un double (i.e. deux chiffres identiques), ou False sinon.
Indication : pour tester une fonction qui a été écrite dans un script, ne pas l’appeler dans la console. Appelez-là dans
le script.
Autre indication : votre code doit respecter certaines conventions ; vous devez respecter une certain ordre :
• d’abord l’importation des modules ;
• ensuite les définitions des fonctions qui seront appelées dans la suite ;
• et enfin le code proprement dit (qui est ici très court : c’est simplement l’appel de la fonction à tester et le print
qui affiche ce que renvoie cette fonction).
Remarque : pour tester, il peut être utile d’ajouter un print à un endroit précis pour vérifier qu’à cet endroit telle
objet à bien la valeur attendue.
• D’une manière générale, il est indispensable de s’assurer qu’une fonction respecte bien son cahier des charges. Dit
autrement : il faut vérifier que pour chaque entrée possible, la fonction retourne bien la sortie attendue. Tout un jeu
de tests est nécessaire si on veut le faire de manière la plus complète possible 16 . Ces tests peuvent être réalisés « à
la main » (avec un papier et un crayon), ou bien de manière logicielle, en écrivant une ou des fonctions qui vérifient
que la fonction effectue bien le travail attendu.
15. À six faces. . .
16. Même s’il est parfois tout simplement impossible de le faire de manière exhaustive.
Info S1 – page 13/ 43
Remarque : en fait, vous avez déjà l’intuition de ce qu’est un jeu de test. Par exemple, pour tester le code de la
question 21, vous avez eu l’idée de tester une entrée égale à 0, et une autre différente de 0, et de vérifier que le
comportement du programme était bien celui attendu. Il s’agit d’un jeu de test (très sommaire).
Remarque : il peut même arriver que les jeux de tests soit écrits avant le code qu’ils sont censés vérifier. . .
• Ici, vu le caractère aléatoire du processus, il n’est pas envisageable de vérifier de manière exhaustive le comportement
de la fonction gagne_double. Mais cela n’empêche pas de vérifier certaines propriétés. Par exemple, on sait que (si
les dés ne sont pas pipés) la probabilité d’obtenir un double est de 16 . Donc, si on effectue un grand nombre de lancés,
la proportion de double devrait tendre vers 16 .
Q 29. Écrire une fonction test_gagne_double(n) qui :
• prend en paramètre un entier n (le nombre de tirages à effectuer) ;
• effectue n lancés (en appelant n fois la fonction gagne_double(). . .) ;
• renvoie la proportion de lancés gagnés.
Tester. . .
4 Observations météorologiques
Q 30. Télécharger le fichier temp.py depuis l’espace classe vers le dossier où vous rangez vos fichiers Python. Il contient les
listes Tmin et Tmax des températures journalières minimales et maximales relevées à la station météorologique de Le
Mans - Arnage en juillet 2022. Écrire le code permettant de compter le nombre de nuits dites tropicales, c’est-à-dire
pour lesquelles la température nocturne n’est pas descendu sous 20 ◦ C.
Q 31. Faire de même pour le nombre de jours dits de forte chaleur, c’est-à-dire pour lesquels la température a dépassé 30 ◦ C.
Q 32. Les fonctions max(l), min(l) et sum(l) renvoie le maximum, le minimum et la somme des éléments d’une liste l
(de nombres). Écrire une fonction moy(l) qui renvoie la moyenne des valeurs de la liste l (on peut utiliser sum()).
Ne pas utiliser une fonction déjà faite (ne pas utiliser np.mean, par exemple). Tester sur Tmax et Tmin (indication :
on trouve environ 29,6 ◦ C et 16,0 ◦ C).
Q 33. Écrire une fonction maxi(l) qui retourne la valeur du maximum de la liste l, (sans utiliser max, ni sort). Tester sur
Tmax, Tmin, etc.
Remarque sur les tests : Il faut toujours envisager, dans les tests, les éventuels cas particuliers. Ici, les cas particuliers
pourraient être des listes comme [1, 2, 3] ou [3, 2, 1], où le maximum est au début ou à la fin de la liste.
On insiste : il faut que le jeu de tests soit le plus complet possible. En fait, il faut même penser à ce genre de cas
particulier en écrivant la fonction.
Remarque plus générale : évidemment, la fonction maxi ne sert à rien. La fonction max déjà implémentée dans Python
fait plus efficacement le travail. . .
Q 34. Modifier la fonction pour qu’elle retourne la position (= l’indice) et la valeur du maximum de la liste l, dans cet
ordre. Rappel : pour renvoyer le contenu de deux objets a et b, on utilise simplement return a, b.
Q 35. Écrire une fonction max_local(l) qui cherche le premier maximum local de la liste l, et qui renvoie l’indice de ce
maximum et ce maximum (dans cet ordre). Tester sur Tmax, Tmin, [1, 2, 3], [3, 2, 1].
Indication : il y a de multiples façons de faire. . .
Rappel (pas forcément utile vu qu’il y a de multiples façons de faire) : une fois que l’interpréteur Python rencontre
l’instruction return a, l’exécution de la fonction s’arrête (même s’il y a encore du code écrit à la suite dans la
fonction) et le contenu de a est retourné ; par ailleurs, il peut y avoir plusieurs return dans une fonction.
5 Pi
• On peut montrer que le nombre π
4 est égal à la somme (infinie) :
π 1 1 1 1 1
= − + − + + ···
4 1 3 5 7 9
En se limitant à un nombre n + 1 de termes dans la somme, et en multipliant par 4 à gauche et à droite, on obtient
l’expression d’une valeur approchée de π :
Å ã
1 1 1 1 1 n 1
π ≃4× − + − + + · · · + (−1)
1 3 5 7 9 2n + 1
Info S1 – page 14/ 43
Remarque : on peut démontrer que chaque nouveau terme ajouté dans la somme est strictement plus petit (en valeur
absolue) que le précédent, et que l’expression ci-dessus tend vers π lorsque n tend vers l’infini 17 .
Q 36. Écrire une fonction approx_pi(n) qui prend en paramètre un entier n et qui renvoie une valeur approchée de π
calculée comme indiquée ci-dessus, avec n+1 termes dans la somme. Tester.
Q 37. On donne quelques décimales de π : π ≃ 3,141 592 653 589 793 238 462 643. Écrire une fonction decimales_pi(d)
qui prend en paramètre un entier d et renvoie le plus petit nombre de termes n tel que la valeur de π calculée soit
correcte à ±10−d près.
Indication : abs(a) retourne la valeur absolue de a. On peut utiliser la valeur de π donnée par math.pi. On peut
partir de approx_pi(n) et (beaucoup) la modifier.
Commencer à tester avec un d « pas trop grand » (3, 4, 5. . .) car le temps de calcul va rapidement augmenter.
Remarque : pour interrompre le déroulement d’un programme, aller dans la console et faire Ctrl + C.
V Tuples
• Le type tuple (ou n-uplet en français, ou bien tuple prononcé à la française) est très similaire au type list mais il
est immuable (immutable, non modifiable).
• Explication sur cette dénomination : un 2-uplet est aussi appelé un doublet ou un couple, un 3-uplet un triplet, un
4-uplet un quadruplet. . .
• Un tuple est une collection d’éléments (pas forcément de même nature) séparés par des virgules, et éventuellement
encadré par des parenthèses (ce n’est pas obligatoire). Exemples : (1, 2, 3) et (1, 'ab', [5, 6, (10, 20)])
sont des tuples (comportant chacun 3 éléments). On aurait pu noter 1, 2, 3 et 1, 'ab', [5, 6, (10, 20)].
• Les parenthèses ne sont strictement obligatoires que pour le tuple vide : ().
• Dans les autres cas, c’est la présence d’au moins une virgule qui fait que l’interpréteur reconnaît un tuple. Exemple
de tuple à un élément : (1,) ou 1,. Contre-exemple : (1) n’est pas un tuple.
• Un tuple est indiçable (= on fait référence à l’élément d’indice i du tuple t avec t[i]).
• Un tuple est itérable (= on peut utiliser un tuple t dans un for i in t).
Q 38. Demander à Python le type des exemples ci-dessus (dans la console) pour vérifier. . .
Attention, la fonction type() utilisée avec plusieurs arguments ne sert pas à retourner un type mais à définir un
nouveau type. Donc type(1, 2, 3) ne renvoie pas le type de 1, 2, 3 mais une erreur. Pour avoir le type de
1, 2, 3, il faut faire une affectation (a = 1, 2, 3) puis demander le type (print(type(a))).
• À quoi sert un tuple ?
* à la même chose qu’une liste lorsqu’on est certain de ne pas avoir à la modifier (c’est plus économique en
mémoire/temps de calcul) ;
* à faire des affectations multiples (par exemple quand une fonction renvoie plusieurs valeurs, voir ci-dessous) ;
* en particulier, à faire des affectations croisées (pour échanger les valeurs de deux variables, voir ci-dessous) ;
* comme clé dans un dictionnaire 18 , car une liste ne peut pas servir de clé car une clé doit être de type immuable 19 .
Q 39. Exécuter le code suivant pour voir comment fonctionne les affectations multiples, et en particulier croisées :
1 def f():
2 return 7, [10, 20] # return(7, [10, 20]) serait strictement identique
3
4 # Ceci :
5 c, d = f() #Affectation multiple
6 print(c)
7 print(d)
8
9 # Équivaut à :
10 x = f()
Pn
17. La suite de terme général sn = i 4
i=0 (−1) 2i+1 converge strictement vers π. . .
18. Voir plus tard.
19. Voir plus tard, on a dit. . .
Info S1 – page 15/ 43
11 c = x[0]
12 d = x[1]
13 print(c)
14 print(d)
15
16 print()
17
18 a = (1, 2, 3)
19 x, y, z = a
20 print(x)
21 print(y)
22 print(z)
23
24 print()
25
26 # Affectations croisées
27 a = 1
28 b = 2
29 a, b = b, a
30 print(a)
31 print(b)
7 a = [1, 2, 3] # list
8 b = [10, 11, 12]
9 c = a + b
10 print(c)
11
12 a = (1, 2, 3) # tuple
13 b = (10, 11, 12)
14 c = a + b
15 print(c)
• Retenir que, de même, le signe * ne réalise pas une multiplication mais sert à concaténer plusieurs fois la même
séquence.
Q 41. Exemple :
1 a = 'Hop ! '
2 b = 3*a
3 print(b)
4
5 a = [1, 2, 3]
6 b = 3*a
Info S1 – page 16/ 43
7 print(b)
8
9 a = (1, 2, 3)
10 b = 3*a
11 print(b)
1 x = [] 1 x = [] 1 x = []
2 for i in range(50, 125, 5): 2 for i in range(0, 75, 5): 2 for i in range(0, 75):
3 x.append(i) 3 x.append(i+50) 3 x.append(i*5 + 50)
4 print(x) 4 print(x) 4 print(x)
Q 43. Écrire le programme permettant de construire la liste [1, 1.001, 1.002, 1.003 ... 2.998, 2.999, 3] (com-
prenant 2001 éléments) à l’aide d’une boucle for ; on appellera cette liste x.
• Remarque : on ne peut pas utiliser la méthode append sur une chaîne ou un tuple, car ce sont des objets immuables.
Le faire renverrait une erreur.
• Autre remarque : on peut remplacer les x.append(a) du code précédent par des x = x + [a] (= opération de
concaténation). Cela aboutit au même résultat mais est beaucoup plus gourmand en mémoire/temps de calcul. En
effet, dans ce cas, l’interpréteur crée une nouvelle liste (ailleurs dans la mémoire), un peu plus grande que x, et y place
le résultat de la concaténation, puis détruit l’ancienne liste. Alors que dans le cas du append, la liste x est modifiée
« en place », simplement en agrandissant un peu l’espace mémoire utilisé par x.
• Remarque sur ces deux remarques : on pourrait donc utiliser cette dernière façon de faire (avec x = x + [a]) avec
des chaînes ou des tuples, même si ce sont des objets non mutables (puisque on recrée un nouvel objet à chaque
opération).
4 x = np.linspace(0.01, 5, 100)
5 y1 = np.log(x)
6 y2 = np.sin(x)
7 y3 = np.cos(x)
8 y4 = np.arctan(x)
9 y5 = np.exp(x)
10 y6 = 1 / (1 + x)
11 plt.figure('Évolution des courbes jaunes et vertes')
12 plt.plot(x, y2, '-g', label='La verte')
13 plt.xlabel('Altitude (m)')
14 plt.ylabel('Température (K)')
15 plt.title('Titre')
16 plt.suptitle('suptitle')
17 plt.grid()
18
19 plt.figure('Utiliser subplot')
20 plt.subplot(3, 2, 1)
21 plt.plot(x, y1)
22 plt.subplot(3, 2, 2)
23 plt.plot(x, y2)
24 plt.title('titre 2')
25 plt.xlim(0, 3)
26 plt.ylim(-2, 2)
27 plt.subplot(3, 2, 3)
28 plt.plot(x, y3)
29 plt.subplot(3, 2, 4)
30 plt.plot(x, y4)
31 plt.subplot(3, 2, 5)
32 plt.plot(x, y5)
33 plt.subplot(3, 2, 6)
34 plt.plot(x, y6)
35 plt.title('titre 6')
36 plt.suptitle('suptitle')
37
43 plt.show()
• Si on trace une courbe en plaçant p′ en ordonnée et p en abscisse (ou l’inverse), la relation théorique nous indique
que la courbe obtenue n’est pas une droite. En revanche, si on place y = p1′ en ordonnée et x = p1 en abscisse, alors
la relation théorique indique qu’on obtient une droite (puisqu’on peut l’écrire y = x + f1′ ).
• On va donc calculer x_exp et y_exp à partir des données expérimentales p_exp et pp_exp, puis tracer y_exp en
fonction de x_exp. Si ces points forment une droite, alors la relation théorique est vérifiée. La fonction polyfit
permet alors de déterminer le coefficient directeur et l’ordonnée à l’origine de la droite « modèle » passant au plus
près des points expérimentaux.
Remarque : en fait, on utilise d’abord polyfit et ensuite seulement on fait le tracé, sur le même graphique, des points
expérimentaux et de la droite modèle. Et alors on peut conclure « à lœil » : a-t-on une droite ou pas ? = la relation
théorique est-elle vérifiée ou pas ?
Q 72. Faire le programme qui permet de conclure si la relation théorique entre p et p′ est vérifiée, et qui détermine la valeur
de f ′ (dans l’affirmative).
• Maintenant vous savez aussi faire cela en tp de physique.
1 Trois algorithmes
a Recherche d’un élément dans une liste
• Pour effectuer une recherche séquentielle d’un élément x dans une liste ou une chaîne de caractère L on propose de
procéder de la manière suivante :
* On parcourt les éléments de L un par un ;
* Si l’élément en cours correspond à x on renvoie True ;
* Une fois tous les éléments testés, si aucun ne correspond à x, on renvoie False.
Q 73. Écrire une fonction appartient(x, L) qui renvoie True si x (nombre ou chaîne de caractères d’une seule lettre)
se trouve dans L (liste de nombres ou chaîne de caractères) et False sinon, en faisant une recherche séquentielle.
Remarque : ne pas utiliser in.
Tester sur des listes de nombres, des chaînes.
Q 74. Écrire une fonction liste_position(x, L) qui renvoie la liste (éventuellement vide) de toutes les positions de
l’élément x dans L.
b Recherche du maximum
• On l’a déjà fait à la question 33. Retrouver le code correspondant.
b Ordre de grandeur
• En général, le nombre d’opérations dépend de la taille de l’entrée. La taille de l’entrée est souvent le nombre de
valeurs d’une liste ou d’un tableau (exemples : cas d’une image, d’une liste de mesures, d’une acquisition, d’un relevé
de compte. . .). En effet, pour traiter tous les éléments de l’entrée, on répète généralement des opérations avec une
boucle.
• Pour comparer des algorithmes, on s’intéresse seulement à l’ordre de grandeur asymptotique de f (n) (n est la taille
de l’entrée) pour n « grand ».
La notation utilisée pour donner cet ordre de grandeur est de dire qu’une fonction f (n) est en O(g(n)) (« en grand
O de g(n) »). Cela ce définit rigoureusement par :
b Recherche du maximum
Q 81. Expliquer pourquoi la complexité de l’algorithme de la fonction maxi de la question 33 est linéaire.
X Dictionnaire
1 Le type dict
• En python, un objet de type dict (dictionnary, i.e. dictionnaire) est un ensemble de paires « clé : valeur ». L’appellation
dictionnaire est bien sûr tirée de la ressemblance avec le dictionnaire que tout un chacun connaît, qui est un ensemble
de paires « mot : définition ». La comparaison s’arrête cependant là ; le dictionnaire de la vie courante est trié (dans
l’ordre alphabétique), alors que le dict de python ne l’est pas. De plus, en python, une clé peut être presque n’importe
quel objet (sauf une liste) 26 . La valeur associée à une clé peut être n’importe quel objet.
• {1 : 'a', 'frite' : [1, 2, 3], 'tortue' : (7.5, 'carapace')} est un exemple de dictionnaire en python.
On voit que :
* Un dictionnaire est encadré avec des accolades.
* Entre les accolades, on trouve des paires clé :valeur, séparées par des virgules.
* Une clé et la valeur associée sont séparées par « : ».
* Cet objet ressemble à une liste de listes de deux éléments (par exemple, l’exemple ci-dessus ressemble à
[[1, 'a'], ['frite',[1, 2, 3]], ['tortue', (7.5, 'carapace')]). Mais attention, il n’est pas une
liste de listes de deux éléments. En particulier, il n’est pas indiçable.
Q 83. Créer et visualiser le dictionnaire d avec le code :
1 d = {1 : 'a', 'frite' : [1, 2, 3], 'tortue' : (7.5, 'carapace')}
2 print(d)
Q 84. Ajouter ensuite le code suivant pour voir comment accéder à un élément du dictionnaire, modifier un élément, ajouter
un élément, supprimer un élément, itérer sur les clés, itérer sur les valeurs, itérer sur les paires clé :valeur, tester
l’existence d’une clé / d’une valeur / d’une paire clé : valeur, constater que l’ordre des éléments n’a pas d’importance,
créer un dictionnaire vide :
3 print(d['frite']) # d['frite'] renvoie la valeur associée à la clé 'frite'
4 # dans le dictionnaire d.
5 d['frite'] = 'pomme de terre' # Modifie la valeur associée à la clé 'frite'
6 print(d) # dans le dictionnaire d.
7 d['physique'] = 999 # Crée dans le dictionnaire d une nouvelle entrée avec
8 print(d) # la clé 'physique' et la valeur 999
9 for i in d.keys(): # Itération sur les clés
10 print(i)
11 for i in d: # Également itération sur les clés (uniquement)
12 print(i)
26. En fait, une clé doit être un objet non muable, de manière à pouvoir être hachable (voir un peu plus loin). En particulier, une clé ne peut
pas être une liste ou un dictionnaire.
Info S1 – page 28/ 43
13 for i in d.values(): # Itération sur les valeurs
14 print(i)
15 for i in d.items(): # Itération sur les paires clé : valeur
16 print(i)
17 for i, j in d.items(): # Même chose avec une affectation conjointe
18 print('À la clé', i, 'correspond la valeur', j)
19 print(len(d)) # len(d) renvoie le nombre d'entrées de d
20 del(d['frite']) # Supprime la paire clé:valeur dont la clé est 'frite'
21 print(d)
22 print(1 in d, 2 in d) # Teste si 1, puis 2, est une clé de d
23 print(1 in d.keys(), 2 in d.keys()) # Même chose
24 print('a' in d.values(), 'b' in d.values()) # Teste si 'a', puis 'b' est une valeur de d
25 print((1, 'a') in d.items(), (1, 'b') in d.items()) # Teste si une paire clé:valeur est dans d
26 # (remarquer l'utilisation d'un tuple)
27 d1 = {1:'un', 2:'deux'}
28 d2 = {2:'deux', 1:'un'}
29 print(d1 == d2) # L'ordre n'a pas d'importance
30 d3 = {} # Crée un dictionnaire vide
2 Sommes doubles
Q 93. Écrire la fonction somme_double_1(n) qui prend un entier n > 1 en argument et qui renvoie la valeur de cette
somme :
Xn X n
ij
i=1 j=1
Indication : utiliser somme_double_2(), définir une autre fonction pour le membre de droite. Comparer les résultats
pour toutes les valeurs de n entre 1 et N (choisir N « pas trop grand » au départ pour ne pas que le temps de calcul
soit trop long).
• Remarquer la méthode .split(), qui permet de découper une chaîne de caractères en plusieurs chaînes de
caractères (le séparateur par défaut est le caractère espace).
• Remarquer aussi qu’on force un changement de type. En effet, la fonction float() renvoie son argument après
l’avoir converti en un flottant. Il existe aussi int() pour convertir en entier, str() pour convertir en chaîne de
caractères, bool() pour convertir en booléen. . .
2 Répertoire courant
• Par défaut, open() cherche le fichier à ouvrir dans le répertoire (= le dossier) courant, c’est-à-dire celui où est
enregistré le fichier python en train d’être exécuté. Si besoin, on peut spécifier le chemin complet du fichier. Exemple :
open('U:/tppython/sousdossier/fichier.txt'). Ou bien on peut utiliser un chemin relatif (= un chemin donné
« à partir » du répertoire courant) : open('sousdossier/fichier.txt').
Remarque : il est préférable d’utiliser des slashs / plutôt que des antislashs \ 31 . Attention, si malgré tout on veut
utiliser les \, il faut écrire U:\\Dossier\\SousDossier et pas U:\Dossier\SousDossier.
• La fonction os.getcwd() (du module os = operating system = système d’exploitation) permet d’obtenir le répertoire
courant (getcwd = get current working directory = obtenir le répertoire de travail courant).
• La fonction os.chdir() (= change directory = changer de répertoire) prend comme argument une chaîne de caractères
correspondant à un chemin de répertoire et définit ce répertoire comme le répertoire de travail courant.
3 Premiers exercices
Q 104. Le fichier premiers_1000.txt est fourni. Il contient les 1000 premiers nombres premiers. Écrire un programme qui
récupére le 684ème nombre premier et le stocke dans une variable de type int. Indication : c’est 5113.
Q 105. Écrire un programme qui détermine la somme des 1000 premiers nombres premiers. Indication : c’est 3 682 913.
Q 106. Le fichier tennis_femmes_2022.csv contient le classement mondial des joueuses de tennis courant 2022, avec les
informations suivantes : rang, nom, prénom, nationalité, nombre de points. Écrire un programme déterminant :
• le nombre de joueuses françaises de ce classement (indication : 75) ;
• la moyenne des scores de toutes les joueuses (indication : environ 178) ;
• la moyenne des scores des 100 premières joueuses (indication : environ 1420) ;
• la moyenne des scores des joueuses françaises (indication : environ 169) ;
• la moyenne des rangs des joueuses dont le prénom commence par un N (indication : environ 761).
Indication : utiliser la méthode .split(), avec en argument le bon caractère. Utiliser aussi l’argument encoding = 'utf8'
pour open.
31. Car cela produit du code (un peu) plus portable d’un système à l’autre.
Info S1 – page 33/ 43
Q 107. Le fichier admissibles.txt contient la liste des élèves de PT qui se sont présentés au concours. Il contient des lignes
de la forme :
1234 TOURNESOL Tryphon Admissible 2
56789 HADDOCK Archibald Eliminé -
La première valeur est le numéro du candidat et la dernière valeur est le numéro de la série d’oral du candidat (il y
a 4 séries d’oral) quand il est admissible, et - sinon. Le caractère de séparation des quatre champs d’une ligne est le
caractère tabulation \t.
Écrire un code produisant une liste de 6 entiers : le nombre de total de candidats, le nombre d’admissibles et le nombre
de candidats dans chaque série. Contrainte : parcourir la liste une seule fois.
Indication : avant d’utiliser la méthode .split(), utiliser la méthode .strip() sur chaque ligne : elle « nettoie »
(= élimine les espaces, \n. . .) le début et la fin de la chaîne de caractères.
2 Allocation de ressources
• On souhaite trouver une solution, grâce à un algorithme glouton, à un problème d’allocation d’une ressource en réponse
à plusieurs demandes. On prend l’exemple de l’attribution de créneaux horaires pour l’utilisation d’une salle sur une
journée. Plusieurs demandes de réservation sont faites, chacune avec une heure de début et une heure de fin. Exemple
de liste de demandes de réservation : [[12.15, 14, 'a'], [13, 14, 'b'], [7, 13, 'z'], [15, 17, 'r']]
pour 4 demandes de réservations (de 12h15 à 14h, de 13h à 14h, etc.). La chaîne de caractère ('a', 'z'. . .) sert à
identifier une réservation. Elle n’est pas indispensable ici. L’objectif est de trouver une solution optimale, c’est-à-dire
de satisfaire le maximum de demandes.
• Quelques précisions :
* la salle ne peut être utilisée qu’une fois à un instant donné ;
* il n’y a pas besoin de prévoir de période de battement entre deux réservations ;
* on cherche à privilégier le nombre de réservations plutôt que la durée totale de réservation : par exemple, on
préfère réserver la salle trois fois une heure plutôt que une fois cinq heures, ou même que une fois trois heures.
• Pour chercher à répondre au problème avec un algorithme glouton, il faut choisir quel critère « glouton » utiliser. En
effet, on peut en envisager plusieurs, par exemple :
* durée croissante : on considère les réservations par ordre de durée croissante ;
* durée décroissante : on considère les réservations par ordre de durée décroissante ;
* début croissant : on considère les réservations par ordre chronologique de leur début ;
* fin croissante : on considère les réservations par ordre chronologique de leur fin ;
* le moins de conflits : on considère les réservations par ordre croissant de leur nombre de conflits, un conflit étant
ici le fait que deux réservations portent sur le même créneau horaire.
Q 114. On choisit le critère de fin croissante. La fonction tri_fin_croiss(L) est fournie (dans le fichier allocation_de_salle.py).
Elle prend en argument une liste L de demandes de réservations, et renvoie une nouvelle liste, où ces demandes sont
triées par date de fin croissante. Remarque : on verra plus tard dans l’année différents algorithmes de tri. Écrire la
fonction glouton_fin_croiss(L), qui prend en argument une liste de demandes de réservation L (non triée), et
renvoie une nouvelle liste, contenant les réservations acceptées, dans l’ordre chronologique.
35. Dans l’ordre : pound ou sovereign (240), half sovereign (120), crown (60), half crown (30), florin (24), shilling (12), sixpence (6), threepence
(3), penny. Et encore, il manque : guinea (252), double florin (48), halfpenny (0,5), farthing (0,25), 1/2 farthing (0,125), 1/3 farthing (≃ 0,083).
Voir fr.wikipedia.org/wiki/Livre_sterling.
Info S1 – page 36/ 43
Optimalité de l’algorithme glouton avec le critère de fin croissante
• On peut montrer que l’algorithme glouton avec le critère de fin croissante (earliest finish time) fournit une solution
optimale (c’est-à-dire qui permet d’accepter le maximum de demandes de réservation). Pour information, voici cette
preuve.
• Tout d’abord, il est certain qu’il existe au moins une solution optimale au problème (démonstration triviale, par
l’absurde). On la note S. Son cardinal est k. Ses éléments sont les {s1 , s2 · · · sk }. On note d(si ) et f (si ) les dates de
début et de fin de l’élément numéro i.
• L’algorithme glouton avec heure de fin croissante fournit une solution notée G. Son cardinal est ℓ. Ses éléments sont
notés {g1 , g2 . . . gℓ }.
• Tout d’abord, on montre qu’il existe une solution optimale T , de (de cardinal ℓ, donc) telle que les k premiers éléments
de T sont les mêmes que les k éléments de G (i.e. ∀i ∈ J1, jK, gi = ti ). On fait une démonstration par récurrence. La
propriété Pj est « il existe une solution optimale T telle que les j premiers éléments de T et de G sont identiques »
(j ≤ ℓ).
* Initialisation (P1 ) : S étant une solution optimale, on a :
Puisque G est une solution gloutonne avec heure de fin croissante, alors on a nécessairement f (g1 ) ≤ f (s1 ).
Donc g1 est compatible avec s2 (i.e. f (g1 ) ≤ d(s2 )) puisque f (s1 ) ≤ d(s2 ).
Donc l’ensemble T = {g1 , s2 , s3 · · · sk } est une solution optimale dont le premier élément est commun avec G.
P1 est vérifiée
* Propagation (Pj =⇒ Pj+1 ) : l’ensemble {g1 , · · · , gj , sj+1 , · · · , sk } vérifie Pj .
On a f (gj ) ≤ d(gj+1 ) (car G est une solution).
On a aussi f (gj+1 ) ≤ d(sj+2 ) car f (gj+1 ) ≤ f (sj+1 ) (car sj+1 est une demande dont la date de fin est minimale
dans l’ensemble des demandes auquel on a enlevé les demandes g1 à gj ) et f (sj+1 ) ≤ d(sj+2 ) (car S est une
solution).
Donc gj+1 est compatible avec gj et sj+2 .
Donc l’ensemble {g1 , · · · , gj+1 , sj+2 , · · · , sk } vérifie bien Pj+1 .
* Conclusion de la récurrence : il existe une solution optimale T = {g1 , · · · , gk , sk+1 , · · · , sℓ } dont les k premiers
éléments sont les mêmes que G.
• Il reste à montrer que k = ℓ (donc que G est optimale). Par l’absurde : on suppose k < ℓ. On remarque alors que
tk+1 est une demande compatible avec les demandes précédentes de G. Donc l’algorithme qui a fournit G n’aurait
pas du s’arrêter. C’est une contradiction.
Donc k = ℓ. Donc G est optimale.
b Version non-fractionnaire
• On a les fruits suivants :
Fruit quantité masse d’un fruit prix au kg
melon charentais 1 fruit 1,1 kg 4 €/kg
melon jaune 1 fruit 1,9 kg 2,5 €/kg
pastèque 2 fruits 3 kg 2 €/kg
• Cette fois, les éléments à transporter ne sont pas fractionnables. Par exemple, si on prend de la pastèque, c’est soit 0
kg, soit 3 kg (= 1 pastèque), soit 6 kg (= 2 pastèques), etc.
Q 119. Écrire une fonction sac_a_dos_nonfrac(cueillette, capacite) similaire à la précédente, sauf que :
• cueillette est par exemple de la forme [['melon charentais', 1, 1.1, 4], ['melon jaune', 1, 1.9, 2.5],
['pastèque', 2, 3, 2]] ;
• elle ne renvoie pas des masses mais des nombres de fruits.
Indication : on peut copier-coller le code de la fonction précédente et le modifier (beaucoup).
Q 120. Considérer la solution [['pastèque', 1], ['melon jaune', 1]]. La solution obtenue par l’algorithme à la ques-
tion précédente est-elle optimale ?
• En fait, cet algorithme glouton n’est pas adapté pour trouver une solution optimale du problème du sac à dos non
fractionnaire. Il présente bien la propriété de sous-structure optimale : la solution d’un sous-problème (= le choix
fait à une étape du problème) est bien optimal (c’est normal, c’est un algorithme glouton). Mais les choix gloutons
successifs n’engendrent pas une solution optimale globalement.
• Pour répondre de manière optimale à la question, il faudrait étudier toutes les combinaisons possibles, de préférence
de manière «efficace » : voir la programmation dynamique en deuxième année.
4 image = img.imread('logo.bmp')
5 plt.imshow(image)
6 plt.show()
Noter l’utilisation de la fonction img.imread() : elle prend en argument une chaîne de caractères contenant le nom
du fichier image à charger. Elle renvoie un tableau (un np.ndarray du module numpy), stockée ici dans la variable
image.
Noter aussi la fonction plt.imshow() qui prend en argument un np.ndarray(dont on va voir la forme ci-dessous),
et affiche l’image (ne pas oublier le plt.show()).
Passer la souris au-dessus de l’image. On voit que les coordonnées du pointeur de la souris s’affichent. On voit de plus
le contenu du pixel correspondant : un tableau à une dimension de 3 éléments, contenant les valeurs R, V et B pour
ce pixel.
Remarquer aussi que le pixel de coordonnées (0,0) est en haut à gauche.
Q 122. Ajouter au début du code la ligne import numpy as np, de manière à disposer des outils du module numpy pour
manipuler les images en mémoire (puisque Python les stocke sous la forme de np.ndarray). Ajouter ce code à la
suite du précédent (il faut aussi commenter le plt.show(), ou le déplacer en fin de code) :
Remarque : inutile d’exclure de la somme le terme tel que i = i′ et j = j ′ , car il est nul.
• La sous-matrice suivante, extraite de l’image globale, est constituée du pixel considéré plus ses 8 voisins :
pi−1,j−1 pi−1,j pi−1,j+1
pi,j−1 pi,j pi,j+1
pi+1,j−1 pi+1,j pi+1,j+1
les éléments du bord d’une matrice comme ceux situés sur les k premières et les k dernières lignes et colonnes.
Les éléments du bord de N sont identiques aux éléments des bords de M 38 .
* Pour tous les éléments qui ne sont pas sur les bords, on calcule ni,j de la manière suivante :
i+k
X j+k
X
ni,j = fi′ ,j ′ · mi′ ,j ′
i′ =i−k j ′ =j−k
Dit autrement : on effectue le produit terme à terme 39 des éléments du filtre F par les éléments du voisinage
du pixel concerné (F et ce voisinage ayant tous les deux une taille n × n).
Remarque : on voit bien qu’on ne peut pas calculer les ni,j sur les bords avec cette relation.
Q 133. Écrire une fonction filtrer(image:np.ndarray, filtre:np.ndarray) -> np.ndarray. Elle prend en argument
deux np.ndarray, qui sont une image et un filtre. Elle crée une nouvelle image, résultat de l’opération de filtrage
décrite ci-dessus, et la renvoie.
Rappel et indication : Si A et B sont deux np.ndarray de même format, alors A*B renvoie bien un tableau de même
format, où on a effectué un produit terme à terme 40 . De plus, np.sum(A) renvoie la somme des éléments de A.
• On peut tout à fait imaginer des filtres pour lesquels les valeurs obtenues seraient en dehors de l’intervalle possible
(i.e. moins de 0 ou plus de 255). Pour éviter cette situation, on peut imaginer trois stratégies :
* La première, qui est celle qu’on a utilisé ci-dessus, consiste à utiliser un filtre normalisé, c’est-à-dire tel que la
somme de ses coefficients soit égale à 1. On s’assure ainsi que le résultat ne dépassera jamais 255.
* La deuxième consiste à écrêter les valeurs qui dépassent. Il faut alors faire le calcul en utilisant un type qui
supporte des valeurs nettement plus grandes que 255 (et plus petites que 0), et ensuite mettre à 255 toutes les
valeurs supérieures à 255, et à 0 toutes les valeurs inférieures à 0.
* La troisième consiste à faire le calcul comme dans le cas précédent, mais en ramenant ensuite toutes les valeurs
dans l’intervalle [0,255] (avec une relation de proportionnalité par exemple).
Q 134. Essayer les filtres suivants (éventuellement en les appliquant à une image en niveaux de gris) :
−2 −1 0
* Repoussage : F2 = −1
1 1
0 1 2
0 1 0
* Contours : F3 = 1
−4 1
0 1 0
0 −1 0
* Réhausseur : F4 =
−1 5 −1
0 −1 0
1 2 1
1
* Moyennage gaussien 3 × 3 : F5 =
. Remarque : on a F5 = 1 · V ∗ t V , où V est la matrice
2 4 2
16
16
1 2 1
1
, ∗ est le produit matriciel, et t V est la transposée de V . En python, si A et B sont des np.ndarray,
2
1
alors np.dot(A, B) renvoie le produit matriciel de A par B (on peut aussi utiliser A@B, ou encore A.dot(B)),
et A.T renvoie la transposée de A.
38. En fait, il faudrait traiter ces éléments de façon spécifique, mais on fait ici l’opération de filtrage de manière simplifiée.
39. Ce n’est pas un produit matriciel.
40. Là aussi, ce n’est pas un produit matriciel.
Info S1 – page 42/ 43
1 4 6 4 1 1
4 16 24 16 4 4
1
Moyennage gaussien 5 × 5 : F6 = . Remarque : F6 = 1
·V ∗ t
, où .
* 6
34 36 24 6 256 V V =
6
256
4 16 24 16 4 4
1 4 6 4 1
1
V Tuples 14
X Dictionnaire 27
1 Le type dict . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2 Recherche dans un dictionnaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3 Comptage des éléments d’un tableau à l’aide d’un dictionnaire . . . . . . . . . . . . . . . . . . . . . . . . 29
XIILecture/écriture de fichiers 30
1 Commandes de base pour la lecture/écriture dans un fichier . . . . . . . . . . . . . . . . . . . . . . . . . 30
2 Répertoire courant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3 Premiers exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4 Loi de Benford (et tracé d’histogramme) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
XIIIAlgorithmes gloutons 33
1 Rendu de monnaie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2 Allocation de ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3 Problème du sac à dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
a Version fractionnaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
b Version non-fractionnaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
XIVManipulation d’images 37
1 Quelques éléments sur le codage numérique d’une image . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2 Premières manipulations d’image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3 Symétrie et rotation « simples » . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4 Conversion en niveaux de gris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5 Détection de contour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6 Application d’un filtre quelconque à une image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40