Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 106

Numérique et

Sciences Informatiques
Classe de Terminale
spécialité - 2020

Sandrine Piquard

Version du 6 octobre 2021


Table des matières

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
0.1 Sous-épreuve écrite de 3 h 30 (sur 12 points) . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
0.2 Sous-épreuve pratique de 1 heure (sur 8 points) . . . . . . . . . . . . . . . . . . . . . . . . . 3

1 Structure de données 4
1.1 Listes, piles, files : structures linéaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Dictionnaires, index et clé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3 Vocabulaire de la programmation orientée objet : classes, attributs, méthodes, objets . . . . . . 12
1.4 Arbres : structures hiérarchiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.5 Graphe : structure relationnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Exos : ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

2 Bases de données 28
2.1 De quoi est fait le modèle fonctionnel ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.2 Quelle démarche adopter pour représenter un diagramme de classe ? . . . . . . . . . . . . . . 30
2.3 Modèle physique de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.4 Modèle relationnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.5 Base de données relationnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.6 Système de gestion de bases de données relationnelles . . . . . . . . . . . . . . . . . . . . . . 39
2.7 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.8 On y va ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.9 Exécuter une requête . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.10 Pour aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.11 Langage SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.12 Interroger la base de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

3 Base de données avec Python 56


3.1 À faire vous-même 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.2 À faire vous-même 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.3 À faire vous-même 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.4 À faire vous-même 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.5 À faire vous-même 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.6 À faire vous-même 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.7 À faire vous-même 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.8 À faire vous-même 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.9 À faire vous-même 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.10 À faire vous-même 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.11 À faire vous-même 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.12 À faire vous-même 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.13 À faire vous-même 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.14 À faire vous-même 14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.15 À faire vous-même 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.16 Projets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

1
TABLE DES MATIÈRES

4 Architectures matérielles, systèmes d’exploitation et réseaux 64


4.1 Différentes architectures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.2 Composants intégrés d’un système sur puce . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3 Gestion des processus et des ressources par l’OS . . . . . . . . . . . . . . . . . . . . . . . . 66
4.4 Protocoles de routage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.5 Sécurisation des communications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

5 Langages et programmation 67
5.1 Calculabilité, décidabilité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.2 Récursivité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.3 Corrigés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.4 Modularité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.5 Paradigmes de programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.6 Mise au point de programme ; gestion des bugs . . . . . . . . . . . . . . . . . . . . . . . . . 79

6 Algorithmique 81
6.1 Algorithmes sur les arbres binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.2 Algorithmes des arbres binaires de recherche . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.3 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
6.4 Algorithmes sur les graphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.5 Méthode « diviser pour régner » . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.6 Programmation dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.7 Recherche textuelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Exos : ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

7 Grand Oral 97
7.1 Définition et objectifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.2 Épreuve orale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.3 Évaluation de l’épreuve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.4 Format et déroulement de l’épreuve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.5 Composition du jury . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Mediagraphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

8 Programme officiel 98
8.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
8.2 Éléments de programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
abcefghijklmnopqrstuvwxyz

Numérique et Sciences informatiques Terminale 2 sur 105 Sandrine Piquard


Modalités de l’épreuve finale

La démarche de projet est au cœur de l’enseignement de la spécialité NSI mais ne se prête pas à une évaluation
à l’écrit ou sur machine en temps limité.
L’épreuve obligatoire terminale orale permet à l’élève de présenter son projet et de valoriser le travail qu’il a
conduit durant sa formation.
L’épreuve terminale obligatoire de spécialité est composée de deux sous-épreuves : une épreuve écrite et
une épreuve pratique.

0.1 Sous-épreuve écrite de 3 h 30 (sur 12 points)


La sous-épreuve consiste en la résolution de trois exercices permettant d’évaluer les connaissances et les ca-
pacités attendues conformément au programme de terminale de la spécialité. Chaque exercice est noté sur 4
points.
Les connaissances et capacités mobilisées dans le programme de première de la spécialité mais non reprises
dans celui de terminale ne constituent pas le ressort principal des sujets composant l’épreuve ; elles doivent
toutefois être assimilées par les candidats qui peuvent avoir à les utiliser.
Les exercices permettent d’aborder différentes rubriques, sans obligation d’exhaustivité, un même exercice
pouvant en particulier permettre l’évaluation d’attendus relevant de plusieurs rubriques.

La sous-épreuve pratique permettant d’évaluer les compétences en programmation, cette évaluation n’est pas un
objectif de la sous-épreuve écrite. Toutefois le candidat peut être évalué sur la compréhension d’un algorithme
ou d’un programme, d’un point de vue plus théorique que pratique.

0.2 Sous-épreuve pratique de 1 heure (sur 8 points)


La sous-épreuve consiste en la résolution de deux exercices sur ordinateur, chacun étant noté sur 4 points.
Le premier exercice consiste à programmer un algorithme figurant explicitement au programme, ne pré-
sentant pas de difficulté particulière, dont on fournit une spécification. Il s’agit donc de restituer un algorithme
rencontré et travaillé à plusieurs reprises en cours de formation. Le sujet peut proposer un jeu de test avec les
réponses attendues pour permettre au candidat de vérifier son travail.
Pour le second exercice, un programme est fourni au candidat. Cet exercice ne demande pas l’écriture com-
plète d’un programme, mais permet de valider des compétences de programmation suivant des modalités va-
riées :
❧ le candidat doit, par exemple, compléter un programme « à trous » afin de répondre à une spécification
donnée,
❧ ou encore, corriger un programme volontairement erroné,
❧ ou encore, expliciter un argument de correction (comme un invariant de boucle),
❧ ou encore, proposer une documentation, etc.

3
Chapitre 1 Structure de données

En classe de première nous avons vu :


❧ Les types de données de base et leur encodage : nombres entiers, entiers relatifs, nombre réels (flottant),
booléens, caractères.
❧ Les types de données construits : p-uplets, tableaux, dictionnaires.
❧ Les données en tables.
Puis nous avons vu que ces données étaient stockées dans la mémoire de la machine (revoir le chapitre « Archi-
tectures matérielles et systèmes d’exploitation ») ; en particulier quand on définit un tableau, la machine réserve
des cases consécutives en mémoire. Si à un moment du programme, on dépasse la taille du tableau, au mieux
le programme plante, au pire il fait n’importe quoi : ce n’est pas dynamique.
Puis quand nous avons abordé le chapitre « Algorithmique », nous avons vu que si le problème devient com-
plexe, il faut mettre en œuvre une stratégie pour structurer ces données.
Nous allons approfondir ces concepts.

1.1 Listes, piles, files : structures linéaires


Les listes chaînées, piles et files sont des structures de données à une dimension permettant de stocker de
manière linéaire une suite finie de données. On les dénomme parfois structures linéaires.
Pour s’imaginer ces structures linéaires, imaginez un garagiste devant gérer les clients de la journée. Chaque
rendez-vous est inscrit sur une fiche qui contient toutes les informations nécessaires (nom du client, modèle
du véhicule, travaux à faire...) et qu’il devra stocker et à laquelle il devra accéder le moment venu. Devant lui
s’offrent plusieurs choix pour la gestion de ces fiches. Nous allons examiner les structures de liste chaînée, pile
et file et voir comment elles peuvent s’appliquer à notre exemple.

1.1.1 La structure liste chaînée


Une liste (ou liste chaînée) est une structure décrivant une succession linéaire de données dans laquelle il est
possible de lire, d’ajouter ou de supprimer des données au début, à la fin ou à l’intérieur de la liste. En langage
C, elles utilisent les pointeurs sur la mémoire.

Les listes chaînées sont une structure de donnée très souple et efficace. Il ne faut pas confondre cette structure
de liste avec les listes Python qui sont en fait une structure bien plus riche, que l’on pourrait qualifier de tableau
dynamique.

Les piles, les files et les listes chaînées sont des objets abstraits, ils n’existent pas nécessai-
rement dans tous les langages mais ce sont des structures de données à connaître que nous
pouvons créer si nécessaire.

Par exemple, en C mais aussi en java (initialement), le tableau dynamique (que l’on peut étendre en fonction
des besoins) n’existe pas. On doit choisir dès le début, puisque ce sont des langages fortement typés, la taille
du tableau que l’on va utiliser. Si le tableau est trop petit... on est coincé. Lorsque l’on code l’objet « tableau
dynamique » en C, on dit que l’on implémente l’objet abstrait.

Les listes chaînées permettent de construire un tableau dynamique dans ces langages, puisque
chaque élément contient l’adresse mémoire de l’élément suivant, les données ne sont donc plus
nécessairement stockées les unes à la suite des autres en mémoire : on n’a plus de problème
de taille.

4
CHAPITRE 1. STRUCTURE DE DONNÉES

Admettons que l’on veuille insérer un élément entre l’élément1 qui pointe vers l’adresse 0001 et l’élément2
qui est donc à l’adresse 0010. On crée un élément1bis qui pointe vers une adresse libre, par exemple 0101, on
change le pointeur de l’élément1 vers l’adresse 0101.
Supprimer un élément se fera en modifiant l’adresse de l’élément précédent, vers l’élément suivant.
Dans une liste chaînée, chaque élément contient la donnée et un pointeur vers l’élément suivant. On accède à la
liste via un pointeur a qui pointe toujours vers le premier élément (Figure 1.1) :

F IGURE 1.1 – Le pointeur a pointe sur le premier élément de la liste chaînée.

Question : quelle adresse se trouve à l’emplacement du dernier élément de la liste ?

On peut imaginer, pour notre exemple du garagiste, une boîte à chaussures contenant toutes les fiches client de
la journée. Notre garagiste peut les parcourir une à une, insérer au milieu une nouvelle fiche pour un client qu’il
voudrait intercaler, etc...

Exemples d’opérations sur les listes


Voici quelques opérations possibles sur les listes chaînées :
❧ Ajouter/retirer un élément au début, à la fin, à l’intérieur de la liste, (Figure 1.2)
❧ Concaténer deux listes,
❧ Scinder une liste en deux,
❧ Accéder au nième élément de la liste,
❧ Rechercher la présence d’un élément dans la liste.

Exercice n°1 :
1. Décrire en langage naturel des algorithmes simples permettant de réaliser les opérations ci-dessus. Vous
pourrez lorsque c’est utile vous aider de schémas.
2. Citez un avantage de la liste chaînée par rapport au tableau de taille fixe ou dynamique (comme le type
list en python).
Corrigé succinct : Créer nouvel élément ; mettre adresse du pointeur a à la fin du nouvel élément, puis l’adresse
du nouvel élément dans le pointeur a.

Numérique et Sciences informatiques Terminale 5 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

F IGURE 1.2 – Exemple : Ajout d’un élément au début de la liste chaînée

1.1.2 La structure de pile


La structure de pile est un cas particulier de liste chaînée dans lequel on accède uniquement au premier élément
de la liste : celui que l’on nomme le sommet de la pile (stack en anglais). On représente en général cette
structure sous forme verticale.
On peut prendre pour exemple une pile d’assiettes : La dernière assiette rangée sera la première que l’on
ressortira. On parle parfois de pile LIFO (Last Input First Output : dernier entré, premier sorti). Autre exemple :
la pioche dans un jeux de carte.

F IGURE 1.3 – Représentation d’une pile

Opérations valides sur les piles


Les opérations permises sur les piles sont plus simples que sur les listes : il est permis :
❧ d’empiler un nouvel élément (ajouter un élément au sommet de la pile),
❧ de dépiler le dernier élément saisi,
❧ de consulter la valeur de l’élément au sommet de la pile sans le dépiler,
❧ de tester si la pile est vide,
❧ de connaître le nombre d’éléments dans la pile.
Vous voyez donc qu’il n’est pas possible d’accéder directement au nième élément d’une pile : Il faudrait dépiler
les n − 1 premiers éléments et de ce fait, détruire partiellement notre pile.

Numérique et Sciences informatiques Terminale 6 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Exemples d’utilisation Les piles sont extrêmement utiles en informatique et vous les utilisez quotidienne-
ment, parfois même sans vous en rendre compte :
❧ La fonction « annuler » (Ctrl+Z) de votre traitement de textes par exemple est une pile : quand vous tapez
Ctrl+Z, vous annulez la dernière opération effectuée. Quand vous faites une nouvelle opération, celle-ci
est mémorisée au sommet de la pile. Vous ne pouvez pas annuler l’avant dernière opération sauf à annuler
la dernière.
❧ Le bouton retour de votre navigateur internet fonctionne également à l’aide d’une pile. Les pages web
consultées lors de votre navigation sur une page sont empilées et le bouton retour permet d’accéder à la
dernière page présente sur la pile.
❧ Certaines calculatrices fonctionnent à l’aide d’une pile pour stocker les arguments des opérations : c’est
le cas de beaucoup de calculatrices de la marque HP, dont la première calculatrice scientifique ayant
jamais été produite : la HP 35 de 1972, en notation polonaise inverse.

Exemple de calcul avec une pile Le mode de calcul avec une pile s’appelle RPN (Reverse Polish Notation
ou notation polonaise inverse). Dans cette logique postfixée, on saisit d’abord les arguments de l’opération puis
en dernier, l’opération à réaliser.
Exemple : Pour faire 2 + 3 on empilera 2, puis 3 et enfin on invoquera la fonction +. Cette logique est extrê-
mement efficace et rapide, en particulier dans les enchaînements d’opérations car elle ne nécessite pas de saisir
des parenthèses. Elle permet aussi de faire moins d’erreurs de calcul car on est obligé de réfléchir aux priorités
des opérations au moment de la saisie.

Exercice n°2 :
1. On tape sur la HP-45 la séquence de touche suivante : 12 ENTER 4 ENTER 3 x -. Quel est le calcul
effectué ? Quel est le résultat sur le sommet de la pile ?
2. On souhaite effectuer le calcul (12 − 4) ∗ 3. Quelle séquence de touche doit-on prévoir ?

Implémentation en Python L’implémentation des piles en python se fait facilement à l’aide des méthodes
append() et pop() du type list :

1 ma_pile.append(ma_valeur) # permet d’empiler une valeur
2 ma_pile.pop() # permet de dépiler une valeur
3 len(ma_pile) # renvoie la longueur de ma_pile
✝ ✆

Exercice n°3 :
Dans l’implémentation python proposée ci-dessus, quelle est la commande à taper pour connaître la valeur au
sommet de ma_pile sans la dépiler ?

1.1.3 Les files


Dans une file, les éléments sont placés les uns à cotés des autres comme dans une pile, sauf qu’on accède
au dernier élément de la liste, c’est à dire le plus ancien en premier. Cela correspond à ce qui se passe dans
une file d’attente : si on reprend l’exemple de notre garagiste, il reçoit les clients le matin et réceptionne leurs
fiches dans une file d’attente. Pour effectuer les travaux sur le véhicule, le garagiste va récupérer les fiches en
commençant par le premier client arrivé, c’est à dire la première fiche entrée dans la file. C’est la loi du premier
arrivé, premier servi ou FIFO (First Input First Output). C’est le comportement classique d’une file d’attente.

Numérique et Sciences informatiques Terminale 7 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

F IGURE 1.4 – Représentation d’une file

Exemple d’utilisation Dans le domaine informatique, on retrouve par exemple les files dans les files d’im-
pression où le premier document envoyé à l’imprimante sera le premier document à être imprimé.

Opérations valides sur les files Les opérations permises sur les files sont similaires aux piles. Il est possible :
❧ d’enfiler un nouvel élément (ajouter un élément à la fin de la file),
❧ de défiler le premier élément saisi (celui au début de la file),
❧ de consulter la valeur du premier élément au début de la file sans le sortir de la file,
❧ de tester si la file est vide,
❧ de connaître le nombre d’éléments dans la file.
Il n’est pas possible d’accéder directement au nième élément d’une file : Il faudrait défiler les n − 1 premiers
éléments et de ce fait, détruire partiellement notre file.

Implémentation en Python L’implémentation des files en python se fait de manière analogue aux piles :

1 ma_file.append(ma_valeur) # permet d’enfiler une valeur
2 ma_file.pop(0) # permet de défiler une valeur
3 len(ma_file) # renvoie la longueur de ma_file
✝ ✆

Exercice n°4 :
1. Dans l’implémentation python proposée ci-dessus, quelle est la commande à taper pour connaître la
valeur du premier élément de la file ?
2. On considère la file constituée des entiers 25, 31, 1, 54, 13.
2.1. On ajoute la valeur 35 à cette file puis on sort 2 valeurs. Quel est alors le contenu de la file.
2.2. Le contenu de la file change-t-il si on effectue ces opérations dans le sens contraire ? (on sort 2
valeurs puis on ajoute 35)

Exercice n°5 :
1. Créez un script python dans lequel vous définissez une file constituée des entiers 25, 31, 1, 54, 13.
2. Créez une fonction qui utilise la méthode append pour ajouter un élément à la fin de la file.
3. Créez une fonction qui utilise la méthode pop pour supprimer un élément au début de la file.
4. Créez une fonction qui permet de consulter la valeur du premier élément au début de la file, sans le sortir
de la file.
5. Créer une fonction qui renvoie False si la file est vide.

Numérique et Sciences informatiques Terminale 8 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

6. Créez une fonction qui donne le nombre d’éléments dans la file.


7. Insérer le tout dans une boucle while qui affiche le nombre d’éléments restant dans la file, tant que celle-ci
n’est pas vide.
8. Tester votre script.
Pour la liste des méthodes de Python3 disponibles :
https://docs.python.org/fr/3/tutorial/datastructures.html

1.2 Dictionnaires, index et clé


Les dictionnaires ont déjà été étudiés en classe de première. Pour rappel, ce type de données, aussi appelé
tableau associatif, permet de stocker des valeurs et d’y accéder au moyen d’une clé, contrairement au tableau
qui permet d’accéder à une donnée au moyen d’un indice.
Voici ce à quoi ressemble l’utilisation d’un dictionnaire en python :

1 dico = dict()
2 dico["Nom"] = "Lecluse"
3 dico["Prenom"] = "Olivier"
4
5 print( dico["Prenom"] + " " + dico["Nom"])
✝ ✆
Dans cet exemple, les clés sont les chaînes "Nom" et "Prenom". On affiche ensuite leurs valeurs.

1.2.1 Opérations classiques sur les dictionnaires


Les opérations classiques que l’on peut effectuer sur un dictionnaire sont :
❧ Ajouter une nouvelle entrée au dictionnaire en créant une nouvelle clé,
❧ Modifier la valeur associée à une clé existante,
❧ Supprimer une entrée dans un dictionnaire (méthode .pop()),
❧ Rechercher la présence d’une clé dans un dictionnaire.
La recherche dans un dictionnaire est optimisée pour s’effectuer sur les clés et non sur les valeurs. Par exemple
avec le dictionnaire que nous avons créé précédemment, la commande "Nom" in dico renverra True alors
que "Lecluse" in dico renverra False, car "Lecluse" est une valeur et non une clé.
Dans un dictionnaire, les clés et les valeurs ne jouent donc pas du tout le même rôle et ne sont pas interchan-
geables.

1.2.2 Les clés


Une clé peut être d’un autre type que chaîne de caractère, du moment que c’est un objet non mutable, c’est
à dire qui ne peut pas être modifié. Une clé ne peut pas être une liste par exemple car une liste est un objet
mutable que l’on peut modifier, par exemple au travers de la méthode .append().
Regardons ce qui se passe si on essaye de définir une clé de type list pour un dictionnaire :

1 dico[[2,1]] = "..."
2 ---------------------------------------------------------------------------
3 TypeError Traceback (most recent call last)
4 <ipython-input-4-d463baccae6e> in <module>()
5 ----> 1 dico[[2,1]]
6

7 TypeError: unhashable type: ’list’


✝ ✆
Le type list n’est pas pas hashable. Mais qu’est-ce que le hachage ?

Numérique et Sciences informatiques Terminale 9 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

1.2.3 Hachage
La notion de hachage est omniprésente en informatique et est au cœur du fonctionnement des dictionnaires.
Le hachage est un mécanisme permettant de transformer la clé en un nombre unique permettant l’accès à la
donnée, un peu à la manière d’un indice dans un tableau.

Définition d’une fonction de hachage


Une fonction de hachage est une fonction qui va calculer une empreinte unique à partir de la donnée fournie en
entrée. Elle doit respecter les règles suivantes :
❧ La longueur de l’empreinte (valeur retournée par la fonction de hachage) doit être toujours la même,
indépendamment de la donnée fournie en entrée.
❧ Connaissant l’empreinte, il ne doit pas être possible de reconstituer la donnée d’origine,
❧ Des données différentes doivent donner, dans la mesure du possible, des empreintes différentes,
❧ Des données identiques doivent donner des empreintes identiques.

Quelques utilisations du hachage


L’utilisation la plus courante est le stockage des mots de passe dans un système informatique un peu sécurisé.
En effet, lorsqu’on crée un compte sur un service en ligne, le mot de passe ne doit pas être stocké en clair, une
empreinte est générée afin que si le service est piraté et que les comptes sont dérobés, il ne soit pas possible
de reconstituer le mot de passe à partir de l’empreinte. Voici un exemple de fonctionnement d’une fonction de
hachage. Nous utiliserons le hachage MD5 accessible sous Linux au travers de la fonction md5sum :

1 $ echo Lecluse | md5sum
2 4dc334bdaa9047f6a86a2f002308b945 -
3

4 $ echo Lécluse | md5sum


5 da9586508c85a8fc1f385c28f477cfe3 -
6

7 $ echo "Olivier Lecluse" | md5sum


8 7136fe2a82df094189d74c82f9ed1c6e -
✝ ✆
On constate bien sur cet exemple que :
❧ toutes les empreintes ont la même longueur,
❧ un petit changement dans la valeur d’entrée fournit une empreinte totalement différentes.

Exercice n°6 :
Une autre utilisation du hachage est la détection de la modification d’un fichier.
1. Créer avec un éditeur de texte un texte de plusieurs paragraphes. (Vous pouvez générer un contenu quel-
conque sur internet grâce au site https://fr.lipsum.com/)
2. Dans un terminal, tapez la commande md5sum mon_fichier.txt en adaptant bien sûr le nom du
fichier.
3. Modifiez très légèrement votre fichier original en ajoutant un espace à la fin par exemple. Recalculer le
hash MD5. Que constatez-vous ?

Ainsi la fonction de hachage peut mettre en évidence des différences qui seraient invisibles à l’œil nu.
4. En Python, tester la fonction de hachage grâce à la fonction hash() :

1 >>> hash("Lecluse")
2 1004192856731890808
✝ ✆
5. Vérifiez à l’aide de quelques exemples que cette fonction hash() est bien une fonction de hachage.

Numérique et Sciences informatiques Terminale 10 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Table de Hachage
Regardez la vidéo ci-dessous sur les tables de hachage.https://youtu.be/IhJo8sXLfVw
Ce qui est important à retenir c’est que la recherche dans une table de hachage est indépendante du nombre
d’éléments dans cette table. Dans un tableau ou une liste chaînée au contraire, la recherche d’un élément prend
un temps proportionnel au nombre d’éléments dans la structure( complexité linéaire en O(n)).
Dans un dictionnaire, les clés sont stockées dans une table de hachage, ce qui explique le fait que le dictionnaire
est optimisé pour la recherche sur les clés.

Utilisation des dictionnaires en Python


Vous pouvez à présent regarder la vidéo suivante afin de réviser la manipulation des dictionnaires en Python
avant de passer aux travaux pratiques. https://youtu.be/VnhBoQAgIVs

Numérique et Sciences informatiques Terminale 11 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

1.3 Vocabulaire de la programmation orientée objet : classes, attributs,


méthodes, objets
Fil conducteur : créer un jeu qui utilise des cartes à jouer, par exemple bataille, rami, tarot, pocker....

1.3.1 La notion d’objet et de classe


Jusqu’ici, les programmes ont été réalisés en programmation procédurale, c’est à dire que chaque programme
a été décomposé en plusieurs fonctions réalisant des tâches simples.
Cependant lorsque plusieurs programmeurs travaillent simultanément sur un projet, il est nécessaire de pro-
grammer autrement afin d’éviter les conflits entre les fonctions.

Un objet se caractérise par 3 choses :


❧ son état,
❧ son comportement,
❧ son identité.

L’état est défini par les valeurs des attributs de l’objet à un instant t.
Par exemple, pour un téléphone, certains attributs sont variables dans le temps comme allumé ou éteint, d’autres
sont invariants comme le modèle de téléphone.
Le comportement est défini par les méthodes de l’objet : en résumé, les méthodes définissent à quoi sert l’objet
et/ou permettent de modifier son état.
L’identité est définie à la déclaration de l’objet (instanciation) par le nom choisi, tout simplement.
En programmation orientée objet, on fabrique de nouveau types de données correspondant aux besoin du
programme. On réfléchit alors aux caractéristiques des objets qui seront de ce type et aux actions possibles à
partir de ces objets.
Ces caractéristiques et ces actions sont regroupées dans un code spécifique associé au type de données, appelé
classe.

1.3.2 Classe : un premier exemple avec le type list


Le type de données list est une classe.

1 # Dans la console PYTHON
2 >>>l = [1,5,2]
3 >>>type(l)
4 <class’list’>
5
✝ ✆
Une action possible sur les objets de type liste est le tri de celle-ci avec la méthode nommée sort().
On parle alors de méthode et la syntaxe est : nom_objet.nom_méthode() comme avec la méthode de tri
liste.sort()

Numérique et Sciences informatiques Terminale 12 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES


1 # Dans la console PYTHON
2 >>>l = [1,5,2]
3 >>>l.sort()
4 >>>l[1,2,5]
5
✝ ✆

1.3.3 Classe : vocabulaire


❧ Le type de données avec ses caractéristiques et ses actions possibles s’appelle classe.
❧ Les caractéristiques (ou variables) de la classe s’appellent les attributs.
❧ Les actions possibles à effectuer avec la classe s’appellent les méthodes.
❧ La classe définit donc les attributs et les actions possibles sur ces attributs, les mé-
thodes.
❧ Un objet du type de la classe s’appelle une instance de la classe et la création d’un objet
d’une classe s’appelle une instanciation de cette classe.
❧ Lorsqu’on définit les attributs d’un objet de la classe, on parle d’instanciation.
❧ On dit que les attributs et les méthodes sont encapsulés dans la classe.

On peut afficher les méthodes associées à un objet avec la fonction dir(objet) :



1 # Dans la console PYTHON
2 >>>l = [1,5,2]
3 >>>dir(l)
4 [’__add__’,’__class__’,’__contains__’,’__delattr__’,’__delitem__’,’__dir__’,
5 ’__doc__’,’__eq__’,’__format__’,’__ge__’,’__getattribute__’,’__getitem__’,
6 ’__gt__’,’__hash__’,’__iadd__’,’__imul__’,’__init__’,’__init_subclass__’,
7 ’__iter__’,’__le__’,’__len__’,’__lt__’,’__mul__’,’__ne__’,’__new__’,
8 ’__reduce__’,’__reduce_ex__’,’__repr__’,’__reversed__’,’__rmul__’,
9 ’__setattr__’,’__setitem__’,’__sizeof__’,’__str__’,’__subclasshook__’,
10 ’append’,’clear’,’copy’,’count’,’extend’,’index’,’insert’,’pop’,
11 ’remove’,’reverse’,’sort’]
12
✝ ✆
On retrouve les méthodes connues concernant les listes : sort(), count(), append(), pop()... Les
autres méthodes encadrées par les underscores sont spéciales.

1.3.4 Création d’une classe : pas à pas


Un constructeur
On va créer une classe simple, la classe Carte correspondant à une carte d’un jeu de 32 ou 54 cartes.
Par convention, une classe s’écrit toujours avec une majuscule.

1 class Carte:
2 """Une carte d’un jeu de 32 ou 54 cartes"""
✝ ✆
Une méthode constructeur commence toujours par :

1 def __init__(self,...):
✝ ✆
Le paramètre particulier self est expliqué plus bas.
Avec deux tirets bas ou underscores (AltGr + 8) de part et d’autre de init.
On va définir les attributs de la carte qui seront :
❧ sa valeur : 2, 3, . . . 10, 11 pour Valet, 12 pour Dame, 13 pour Roi et 14 pour As ;
❧ et sa couleur : Carreau, Coeur, Trèfle, Pique.

Numérique et Sciences informatiques Terminale 13 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES


1 class Carte: # Définition de la classe
2 """Une carte d’un jeu de 32 ou 54 cartes"""
3 def __init__(self,valeur,couleur): # méthode 1 : constructeur
4 self.valeur = valeur # 1er attribut valeur {de 2 à 14 pour as}
5 self.couleur = couleur # 2e attribut {’pique’,’carreau’,’coeur’,’trefle’}
6
✝ ✆
Création d’une instance de la classe Carte :

1 # Dans la console PYTHON
2 >>>x=Carte(5,’carreau’)
3
✝ ✆
Lorsque l’on créé un objet, son constructeur est appelé implicitement et l’ordinateur alloue de la mémoire pour
l’objet et ses attributs. On peut d’ailleurs obtenir l’adresse mémoire de notre objet créé x.

1 # Dans la console PYTHON
2 >>>x = Carte(5,’carreau’)
3 >>>x
4 <__main__.Carte object at 0x7f7f57d4ae90>
5 # Rappel: un nombre qui commence par 0x...........
6
✝ ✆
Par ailleurs, l’obtention de la valeur d’un attribut d’un objet se fait par l’utilisation de l’opérateur d’accessibilité
point : nom_objet.nom_attribut .

Cela peut se lire ainsi de droite à gauche nom_attribut appartenant à l’instance nom_objet :

1 # Dans la console PYTHON
2 >>>x = Carte(5,’carreau’)
3 >>>x.valeur
4 5
5 >>>x.couleur
6 ’carreau’
✝ ✆

La variable self
La variable self, dans les méthodes d’un objet, désigne l’objet auquel s’appliquera la méthode.
Elle représente l’objet dans la méthode en attendant qu’il soit créé.

1 # Dans l’éditeur PYTHON
2 class Carte: # Définition de la classe
3 """Une carte d’un jeu de 32 ou 54 cartes"""
4 def __init__(self,valeur,couleur): # constructeur
5 self.valeur = valeur # 1er attribut
6 self.couleur = couleur # 2e attribut
✝ ✆

1 # Dans la console PYTHON
2 >>>x = Carte(5,’carreau’)
3 >>>y = Carte(14,’pique’)
✝ ✆
Dans cet exemple, la méthode __init__ (constructeur) est appelée implicitement.
"self" fait référence à l’objet x dans la première ligne et à l’objet y dans la seconde.

Encapsulation : les accesseurs ou "getters"


On ne va généralement pas utiliser la méthode précédente nom_objet.nom_attribut permettant d’accéder aux
valeurs des attributs car on ne veut pas forcement que l’utilisateur ait accès à la représentation interne des
classes.

Numérique et Sciences informatiques Terminale 14 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Pour utiliser ou modifier les attributs, on utilisera de préférence des méthodes dédiées dont le rôle est de faire
l’interface entre l’utilisateur de l’objet et la représentation interne de l’objet (ses attributs).
Les attributs sont alors en quelque sorte encapsulés dans l’objet,c’est à dire non accessibles directement par le
programmeur qui a instancié un objet de cette classe.

❧ L’encapsulation désigne le principe de regrouper des données brutes avec un ensemble


de routines (méthodes) permettant de les lire ou de les manipuler.
❧ But de l’encapsulation : cacher la représentation interne des classes.
❧ Pour simplifier la vie du programmeur qui les utilise.
❧ Pour masquer leur complexité (diviser pour régner).
❧ Pour permettre de modifier celle-ci sans changer le reste du programme.
❧ La liste des méthodes devient une sorte de mode d’emploi de la classe.

Pour obtenir la valeur d’un attribut nous utiliserons la méthode des accesseurs(ou "getters") dont le nom est
généralement : getNom_attribut() . Par exemple ici :

1 # Dans l’éditeur PYTHON
2 class Carte: # Définition de la classe
3 """Une carte d’un jeu de 32 ou 54 cartes"""
4 def __init__(self,valeur,couleur): # méthode 1 : constructeur
5 self.valeur = valeur # 1er attribut valeur {de 2 à 14 pour as}
6 self.couleur = couleur # 2e attribut{’pique’,’carreau’,’coeur’,’trefle’}
7

8 def getAttributs(self): # méthode 2 : pour accéder aux valeurs des attributs


9 return(self.valeur,self.couleur)
✝ ✆

1 # Dans la console PYTHON
2 >>>x = Carte(5,’carreau’)
3 >>>y = Carte(14,’pique’)
✝ ✆
Puis dans la console, faire :

1 # Dans la console PYTHON
2 >>>x = Carte(5,’carreau’)
3 >>>y = Carte(14,’pique’)
4 >>>x
5 # notez le résultat
6 >>>y
7 # notez le résultat
8 >>>x.getAttributs()
9 # notez le résultat
10 >>>y.getAttributs()
11 # notez le résultat
✝ ✆

Exercice n°7 :
Créer deux autres méthodes permettant de récupérer la valeur de la carte et la couleur avec les "getters" (acces-
seurs) : getCouleur() et getValeur()

Numérique et Sciences informatiques Terminale 15 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Modifications contrôlées des valeurs des attributs : les mutateurs ou "setters"


On va devoir contrôler les valeurs attribuées aux attributs. Pour cela, on passe par des méthodes particulières
appelées mutateurs (ou "setters") qui vont modifier la valeur d’une propriété d’un objet.
Le nom d’un mutateur est généralement : setNom_attribut() .

1 # Dans l’éditeur PYTHON
2 class Carte: # Définition de la classe
3 """Une carte d’un jeu de 32 ou 54 cartes"""
4 def __init__(self,valeur,couleur): # constructeur
5 self.valeur = valeur # 1er attribut {de 2 à 14}
6 self.couleur = couleur # {’pique’, ’carreau’, ’coeur’, ’trefle’}
7

8 def getAttributs(self): # méthode 2 : accesseur


9 return(self.valeur,self.couleur)
10

11 def getValeur(self): # méthode 3 : accesseur


12 return self.valeur
13

14 def getCouleur(self): # méthode 4 : accesseur


15 return self.valeur
16
17 def setValeur(self,v): # mutateur avec contrôle
18 if 2 <= v <= 14:
19 self.valeur = v
20 return True
21 else:
22 return False
✝ ✆
Par exemple on va créer une carte c1, un 7 de cœur puis modifier sa valeur en la passant à 10.

1 # Dans la console PYTHON
2 >>>c1 = Carte(7,’coeur’)
3 >>>c1.getAttributs()
4 (7,’coeur’)
5 >>>c1.setValeur(10)
6 True
7 >>>c1.getAttributs()
8 (10,’coeur’)
9 >>>
✝ ✆

Exercice n°8 :
1. Créer le mutateur de l’attribut couleur sous la forme setCouleur(self,c).
2. Créer une carte c2, un Roi de cœur puis modifier sa valeur en la passant à une Dame.
3. Modifier la couleur de la carte c2 en la passant à pique.
4. Modifier la carte c2 en la passant à 8 de carreau.

Numérique et Sciences informatiques Terminale 16 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

1.4 Arbres : structures hiérarchiques


Les structures de données en arbre sont des structures non linéaires. Elles peuvent être intéressantes dans la
représentation des données lorsque celle-ci sont hiérarchisées, et particulièrement si leur nombre est important.
On parle alors d’arbre, de structure arborescente ou d’arborescence. Par exemple un arbre généalogique, l’or-
ganigramme d’une société avec le directeurs, les sous-directeurs, les secrétaires, etc... L’explorateur de fichiers
de l’ordinateur donne une représentation hiérarchisée du rangement des fichiers, c’est l’arborescence.

Un arbre est défini par divers éléments : des nœuds, des feuilles, des racines, des branches. On
peut aussi le définir sur le modèles des liens de parenté avec des parents et des enfants, des
ancêtres, des frères. Les branches permettent d’aller de la racine jusqu’à chaque feuille.

Les arbres sont des cas particuliers des graphes présentés dans la section suivante. On peut donc aussi utiliser
le vocabulaire des graphes et parler de sommets, d’arêtes et de chemins.

C:

Windows Users Programs

System32 2 3 Toto Tintin Zozo 1 2 3

Explorer.exe 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3

Un nœud peut avoir un nombre arbitraire d’enfants.

1.4.1 Arbre binaire


Définition 1 : Un arbre binaire est un arbre pour lequel chaque père a au plus 2 fils.

Donc
❧ soit l’arbre est vide,
❧ soit il est composé d’une racine et d’un sous-arbre droit et d’un sous-arbre gauche.
Les deux sous-arbres sont eux aussi des arbres binaires.
On peut utiliser la structure de liste en Python pour implémenter une structure d’arbre. Par exemple pour un
arbre binaire :
❧ une liste vide pour un arbre vide,
❧ une liste contenant 3 éléments, la valeur ou clé de la racine, le sous-arbre gauche et le sous-arbre droit.
Il est nécessaire de définir les fonctions pour :
❧ créer un arbre vide ou un arbre dont la racine est donnée ;
❧ savoir si un arbre est vide ou pas ;
❧ obtenir le sous-arbre gauche ou le sous-arbre droit à partir d’une racine ;
❧ insérer un nœud.

Numérique et Sciences informatiques Terminale 17 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Construisons un arbre binaire pour lequel chaque élément inséré obéit à la règle suivante : si la valeur à insérer
est inférieure à la racine, on l’insère dans le sous-arbre gauche, sinon on l’insère dans le sous-arbre droit.

1 def creer_arbre(r=None):
2 """
3 Renvoie un arbre vide ou un arbre de racine r
4 """
5 if r:
6 return [r, [], []]
7 else:
8 return []
9

10

11 def arbre_vide(a):
12 return a==[] # le résultat est True ou False
13
14 def fils_gauche(a):
15 if not arbre_vide(a):
16 return a[1]
17

18 def fils_droit(a):
19 if not arbre_vide(a):
20 return a[2]
21
22 def insere(a, val):
23 if arbre_vide(a):
24 a.append(val)
25 a.append([])
26 a.append([])
27 elif val <= a[0]:
28 # si la valeur a insérer est inf à la racine,
29 #on l’insère dans le sous-arbre gauche
30 insere(a[1], val)
31 else:
32 #sinon on l’insère dans le sous-arbre droit
33 insere(a[2], val)
✝ ✆
Et on peut ajouter dans le script les lignes de test :

1
2 if __name__ == "__main__":
3 a = creer_arbre(12)
4 print(arbre_vide(a))
5 insere(a,15)
6 insere(a,14)
7 insere(a,8)
8 insere(a,17)
9 print(a)
✝ ✆
Ce qui donne :

Numérique et Sciences informatiques Terminale 18 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

12

8 15

14 17

1.4.2 Notions de hauteur et de profondeur


On peut compter le nombre de nœuds en suivant une branche de la racine à une feuille.

Définition 2 : La hauteur d’un arbre est le nombre de niveau des nœuds. Un nœud de niveau k est le fils d’un
nœud de niveau k − 1. La racine du nœud a le niveau 0.

C’est aussi le nombre de nœuds de la branche la plus longue, racine comprise.

Définition 3 : La profondeur d’un nœud est le nombre de nœuds de la branche partant de la racine et allant
jusqu’au nœud considéré, extrémité comprise.

Dans l’exemple précédent, l’arbre a pour hauteur 3 et le nœuds 15 est à la profondeur 2.

1.4.3 Cas de l’arbre complet

15

12 17

11 14 16 21

L’arbre ci-dessus est un arbre complet, car chaque nœuds a exactement deux fils, sauf les feuilles.
Si l’arbre a une hauteur égale à 4, alors le nombre de nœuds est donné par :
1 + 21 + 22 + 23 = 24 − 1
soit 15 nœuds et 23 = 8 feuilles.
De manière générale, dans un arbre binaire complet, le nombre de feuilles est 2h−1 et le
nombre de nœuds est 2h − 1.

Dans le cas de l’arbre complet, on peut définir cet arbre binaire complet par une liste simple :

1 >>> arbre = [15, 12, 17, 11, 14, 16, 21]
✝ ✆
L’astuce est que les deux fils du nœud d’indice i se trouvent aux indices 2i + 1 et 2i + 2.
Nous verrons les algorithmes sur les arbres dans le chapitre 6.

Numérique et Sciences informatiques Terminale 19 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

1.4.4 Arbre binaire de recherche


Un arbre binaire de recherche est un arbre binaire, c’est à dire que chaque nœud a au plus deux enfants, donc
un sous-arbre droit et un sous-arbre gauche. Les enfants à gauche d’un nœud ont des valeurs inférieures à celle
du nœud et les enfants à droite d’un nœud ont des valeurs supérieures à celle du nœud.
Il faut donc disposer d’une relation d’ordre sur l’ensemble des valeurs. On suppose que toutes les valeurs sont
distinctes.
Il y a plusieurs façons d’implémenter un arbre binaire de recherche. Nous allons utiliser une structure de classe.
Un nœud a deux enfants et mise à part la racine, a un parent. Chaque nœud a une valeur, on crée donc une
classe Noeud avec quatre attributs : une valeur, un parent et deux fils. On suppose pour commencer que deux
enfants d’un nœud sont des nœuds.

1 class Noeud:
2 def __init__(self, valeur):
3 self.valeur = valeur
4 self.parent = None
5 self.gauche = None
6 self.droite = None
7
8 def __str__(self):
9 return str(self.valeur)
10
11 def ajoute(self, valeur):
12 if valeur < self.valeur:
13 if self.gauche is None:
14 self.gauche = Noeud(valeur)
15 self.gauche.parent = self
16 else:
17 self.gauche.ajoute(valeur)
18 elif valeur > self.valeur:
19 if self.droite is None:
20 self.droite = Noeud(valeur)
21 self.droite.parent = self
22 else:
23 self.droite.ajoute(valeur)
✝ ✆
Les principales opérations à effectuer sur un tel arbre sont l’insertion et la suppression d’un nœud, ainsi que la
recherche d’une valeur : un minimum, un maximum, un successeur ou un prédécesseur. Pour cela on est amené
à parcourir l’arbre.
Par exemple, la valeur la plus petite se trouve à gauche. A partir de la racine, on prend toujours le fils gauche,
si un nœud n’a pas de fils, ou seulement un fils droite, on a trouvé la plus petite valeur.
On approfondira les algorithmes en chapitre 6.

Numérique et Sciences informatiques Terminale 20 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Une seconde manière de procéder est de raisonner de manière récursive : les deux enfants d’un nœud sont des
arbres.

1 class Arbre:
2 def __init__(self, val):
3 self.valeur = val
4 self.gauche= None
5 self.droite = None
6

7 def __str__(self):
8 return str(self.valeur)
9

10 def inserer(self, val):


11 if val < self.valeur:
12 if self.gauche != None:
13 self.gauche.inserer(val)
14 else:
15 self.gauche = Arbre(val)
16 if val > self.valeur:
17 if self.droite != None:
18 self.droite.inserer(val)
19 else:
20 self.droite = Arbre(val)
21
22

23 monarbre = Arbre(15)
24
25 liste = [12, 5, 8, 19, 14, 16]
26 for n in liste:
27 monarbre.inserer(n)
28

29 print(monarbre)
30 print(monarbre.gauche,monarbre.droite )
31 print(monarbre.gauche.gauche,monarbre.gauche.droite, monarbre.droite.gauche,
monarbre.droite.droite )
32 print(monarbre.gauche.gauche.droite)
✝ ✆

Exercice n°9 :
Dessiner un arbre binaire de recherche contenant les valeurs 11, 13, 14, 15, 17, 18, 19 satisfaisant la condition
donnée dans chacun des cas ci-dessous :
1. Dans le premier cas, la hauteur de l’arbre est 3.
2. Dans le deuxième cas, l’arbre n’a qu’une feuille au niveau 4, qui n’est ni la plus petite ni la plus grande
valeur.

Numérique et Sciences informatiques Terminale 21 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

1.5 Graphe : structure relationnelle


Alice est sur Wikipédia, en train de lire la page Algorithme du lièvre et de la tortue. Trois clics plus tard, elle
se surprend à lire Cathédrale Saint-Étienne de Metz et se demande comment elle en est arrivé là a . D’autres
questions se précipitent alors dans sa tête.
❧ Est-il possible de revenir à la page Algorithme du lièvre et de la tortue en suivant uniquement les liens
entre les articles de Wikipédia ?
❧ Combien de clics seraient nécessaires pour cela ?
❧ Certains articles de Wikipédia sont-ils tout simplement inatteignables à partir de cette page ?
Immédiatement, Alice comprend qu’il lui faudra écrire un programme si elle veut répondre à de telles ques-
tions, car tenter d’y répondre par une exploration manuelle prendrait trop de temps. Mais Alice sait également
qu’il y a plus de deux millions de pages sur fr.wikipedia.org, et des dizaines de liens dans chaque
page, alors elle se demande si un programme a une chance de répondre dans un délai raisonnable.
En cherchant à écrire des programmes pour répondre à ces questions Alice découvre la théorie des graphes,
inventée il y a presque 300 ans par le mathématicien Leonhard Euler, et qu’elle peut effectivement déterminer
si il existe un chemin d’une page à une autre dans Wikipédia avec un algorithme b qui s’écrit en seulement 5
lignes de code.
a. Le fait que l’informaticien Donald Knuth joue de l’orgue est un indice.
b. Il s’agit du parcours en profondeur, étudié au chapitre 6.

1.5.1 Vocabulaire de la théorie des graphes

F IGURE 1.5 – Exemple de graphe

Définition 4 : Graphe : un graphe est un ensemble de sommets reliés entre eux par des arcs.

❧ deux sommets reliés par un arc sont dits adjacents.


❧ le degré d’un sommet est le nombre d’arc liés à ce sommet,
❧ l’ordre d’une graphe est le nombre de sommets,
❧ si nous supposons un sens de parcours des arcs, le graphe est dit orienté,
❧ si deux sommets quelconques sont adjacents (sous-entendu la proposition est vraie pour tous les sommets
du graphe) alors le graphe est complet.
❧ le voisinage d’un sommet est constitué par l’ensemble des sommets reliés par des arc à ce sommet.
Dans l’exemple de la figure 1.5, il s’agit d’un graphe d’ordre 4, le degré du sommet 1 est 2, le degré du sommet
2 est 2, le degré du sommet 3 est 4, le degré du sommet 2 est 2, il n’est pas complet, mais il est orienté.
Pour Alice, chaque sommet représente une page de Wikipédia, et les arcs représentent les liens entre les pages.

Numérique et Sciences informatiques Terminale 22 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

1.5.2 Chemin dans un graphe


Dans notre exemple, Alice a suivi un chemin entre les pages de Wikipédia.
Dans un graphe non orienté, s’il existe un chemin qui va de x à y, alors il existe un chemin qui va de y à x.
Si le graphe est orienté, il peut exister un chemin qui va de x à y, sans qu’un chemin soit possible de y à x.
Exemples de graphes :
Réseau Un réseau routier est naturellement un graphe. A grande échelle, les sommets sont des villes reliées
par des arcs qui représentent les routes entre ces villes. si toutes les routes sont à double sens, le graphe
sera naturellement non-orienté.
On peut également imaginer un réseau routier à l’intérieur d’une ville, où les sommets sont plutôt les
intersection entre les rues et les arcs les portion de rues reliant les intersections. Le graphe est alors
orienté, puisqu’il existe des rues à sens unique.
Un navigateur GPS implémente un algorithme de recherche de chemin dans un tel graphe.
Carte Un célèbre théorème mathématiques, le théorème des quatre couleurs, énonce que les pays d’une carte
planaire peuvent toujours être coloriés avec quatre couleurs seulement sans que deux pays limitrophes
aient la me couleur. En terme de graphe on dit que tout graphe peut-être dessiné dans le plan, sans que
deux arcs se croisent, il peut donc voir ses sommets coloriés avec quatre couleurs au plus sans que deux
sommets reliés par un arc n’aient la même couleur.
Labyrinthe Un labyrinthe constitué de salles reliées par des portes peut-être vu comme un graphe non-orienté.
Les sommets sont les salles du labyrinthe et et deux sommets sont reliés par un arc s’il existe une porte
entre les deux salles.
Arbre Un arbre peut-être naturellement vu comme un graphe où chaque nœud constitue un sommet et est relié
aux racines des ses sous-arbres, le cas échéant. On peut le voir comme un graphe orienté ou non-orienté.

1.5.3 Représentation d’un graphe en Python


Il existe de nombreuses façons de représenter un graphe en machine, selon la nature du graphe, mais aussi selon
la nature des opérations et des algorithmes à effectuer sur ce graphe. Quelle que soit la représentation, on doit
retrouver deux sortes d’opérations :
❧ Les opérations de construction de graphe : la création d’un graphe vide, l’ajout de sommets ou d’arcs...
❧ Les opérations de parcours d’un graphe, comme parcourir tous les sommets, tous les arcs, ou encore tous
les arcs issus d’un sommet donné.
Pour simplifier ce cours nous allons considérer que les sommets sont des entiers (mais ils pourraient être des
chaînes de caractère, ou encore des objets) et que le graphe est orienté. Nous verrons ensuite comment traiter
les graphe non-orientés.

Matrice d’adjacence
Reprenons notre graphe :

F IGURE 1.6 – Exemple de graphe

Numérique et Sciences informatiques Terminale 23 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Soit N = 4 le nombre total de sommets de ce graphe.


On peut représenter ce graphe par une matrice d’adjacence de taille N × N, contenant des booléens. Cette
matrice est initialisée à False.
Puis s’il existe un arc entre les sommets i et j, alors le booléen adj[i][j] = True indique la présence de
cet arc.

1 adj = [[False] * N for i in range (N)]
✝ ✆

False True True False


 
 False False False False 
 
 False True False True 
False False True False
La matrice d’adjacence ci-dessus représente les arcs du graphe figure 1.6.
Pour ajouter un arc, il suffit alors de mettre True à la position correspondante dans la matrice.
Pour parcourir tous les sommets du graphe, il suffit de parcourir la liste adj.
Pour parcourir tous les voisins d’un sommet, il suffit d’ajouter un test.

1 class Graphe:
2 """
3 Un graphe représenté par une matrice d’adjacence,
4 où les sommets sont les entiers n= 0, 1, ... n-1
5 """
6 def __init__(self,n):
7 self.n = n
8 self.adj = [[False] * n for i in range(n)]
9
10 def __str__(self):
11 return str(self.adj)
12
13 def ajouterArc(self, s1, s2):
14 self.adj[s1][s2] = True
15
16 def arc(self, s1, s2):
17 return self.adj[s1][s2]
18

19 def voisins(self, s):


20 v=[]
21 for i in range(self.n):
22 if self.adj[s][i]:
23 v.append(i)
24 return v
✝ ✆
Listing 1.1 – Graphe représenté par une matrice d’adjacence. graphs.py

Le programme ci-dessus encapsule une telle matrice dans une classe Graphe. Le constructeur de cette classe
prend en argument le nombre de sommets et alloue la matrice. La méthode ajouterArc permet d’ajouter un
arc au graphe. On peut alors tester :

1 if __name__ == "__main__":
2 g = Graphe(4)
3 g.ajouterArc(0,1)
4 g.ajouterArc(0,3)
5 g.ajouterArc(1,2)
6 g.ajouterArc(3,1)
✝ ✆

Exercice n°10 :
1. Que fait la méthode arc ?

Numérique et Sciences informatiques Terminale 24 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

2. Que fait la méthode voisins ?


Cette représentation à quelques défauts : elle occupe un espace en mémoire proportionnel à N 2 , même si le
graphe a très peu d’arcs ; et parcourir tous les voisins d’un sommet donné nécessite de parcourir toute une ligne
de la matrice , soit N booléens, même si il y a très peu de voisins. Enfin elle limite les sommets à des entiers
consécutifs et dont le nombre est connu à l’avance.

Dictionnaire d’adjacence
On peut représenter le graphe précédent en utilisant un dictionnaire d’adjacence. Le graphe est alors un diction-
naire qui associe à chaque sommet l’ensemble de ses voisins.
La première conséquence est que les sommets ne sont pas limités à des entiers et qu’il n’est pas nécessaire de les
connaître tous a priori. En effet, il suffit d’ajouter une nouvelle entrée au dictionnaire pour ajouter un nouveau
sommet au graphe. L’ensemble des sommets du graphe est exactement l’ensemble des clé du dictionnaire. On
peut alors parcourir tous les sommets du graphe avec la boucle :

1 for s in graphe:
2 ...
✝ ✆
Les voisins du sommet s sont donnés par l’ensemble graphe[s], on peut donc les parcourrir en faisant :

1 for v in graphe[s]:
2 ...
✝ ✆
La deuxième conséquence est que complexité de cette nouvelle représentation est alors proportionnelle au
nombre de voisin de s, indépendamment du nombre de sommet du graphe.

1 """
2 Graphe représenté par un dictionnaire d’adjacence
3 """
4
5 class Graphe:
6 """
7 Un graphe représenté par undictionnaire d’adjacence,
8

9 """
10 def __init__(self):
11 self.adj = {}
12
13 def __str__(self):
14 return str(self.adj)
15
16 def ajouterSommet(self, s):
17 if s not in self.adj:
18 self.adj[s]= set()
19

20 def ajouterArc(self, s1, s2):


21 self.ajouterSommet(s1)
22 self.ajouterSommet(s2)
23 self.adj[s1].add(s2)
24

25 def arc(self, s1, s2):


26 return s2 in self.adj[s1]
27

28 def sommets(self):
29 return list (self.adj)
30

31 def voisins(self, s):


32 return self.adj[s]
✝ ✆
Listing 1.2 – Graphe représenté par un dictionnaire d’adjacence. graph2.py

Numérique et Sciences informatiques Terminale 25 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

Et on pourra alors tester :



1 if __name__ == "__main__":
2 g = Graphe()
3 g.ajouterArc(0,1)
4 g.ajouterArc(0,3)
5 g.ajouterArc(1,2)
6 g.ajouterArc(3,1)
✝ ✆

1.5.4 Graphe non-orienté


On représente un tel graphe exactement comme un graphe orienté en s’assurant de vérifier l’invariant suivant :

Il y a un arc reliant le sommet x au sommet y


si et seulement si
il y a un arc reliant le sommet y au sommet x.

Il suffit donc que les opération ajouetrArc et supprimerArc ajoutent et enlèvent systématiquement une
paire d’arc :

1 self.adj[s1][s2]=True
2 self.adj[s2][s1]=True
✝ ✆
ou

1 self.ajouterSommet(s1)
2 self.ajouterSommet(s2)
3
4 selj.adj[s1].add(s2)
5 selj.adj[s2].add(s1)
✝ ✆

Exercice n°11 :
1. Ajouter à la classe Graphe du programme graphs.py une méthode afficher pour afficher le
graphe sous la forme suivante :

1 0 -> 13
2 1 -> 23
3 2 -> 3
4 3 -> 1
✝ ✆
c’est à dire une ligne par sommet, avec pour chacun la liste de ses voisins.
2. Ajouter une méthode degre(s) qui donne le nombre d’arcs issus du sommet s.
3. Ajouter une méthode nbArcs() qui donne le nombre total d’arcs du graphe.
4. Ajouter une méthode supprimerArcs(s1,s2) pour supprimer l’arc entre les sommets s1 et s2.
On pourra utiliser la méthode remove.

Exercice n°12 :
1. Ajouter à la classe Graphe du programme graph2.py une méthode afficher pour afficher le
graphe sous la forme suivante :

1 0 {1, 3}
2 1 {2, 3}
3 3 {1}
4 2 {3}
✝ ✆

Numérique et Sciences informatiques Terminale 26 sur 105 Sandrine Piquard


CHAPITRE 1. STRUCTURE DE DONNÉES

c’est à dire une ligne par sommet, avec pour chacun la liste de ses voisins. L’ordre des sommets n’a pas
d’importance.
2. Ajouter la méthode nbSommets() qui donne le nombre de sommets du graphe.
3. Ajouter une méthode degre(s) qui donne le nombre d’arcs issus du sommet s.
4. Ajouter une méthode supprimerArcs(s1,s2) pour supprimer l’arc entre les sommets s1 et s2.
On pourra utiliser la méthode remove.
https://www.numerique-sciences-informatiques.fr/terminale_nsi/cours_structure_donnees_pile_file.php
https://pixees.fr/informatiquelycee/n_site/nsi_term.html

https://www.math93.com/images/pdf/NSI/terminale/NSI_Classes.pdf

Numérique et Sciences informatiques Terminale 27 sur 105 Sandrine Piquard


Chapitre 2 Bases de données

Le développement des traitements informatiques nécessite la manipulation de données de plus en plus nom-
breuses. Leur organisation et leur stockage constituent un enjeu essentiel de performance. Le recours aux bases
de données relationnelles est aujourd’hui une solution très répandue. Ces bases de données permettent d’orga-
niser, de stocker, de mettre à jour et d’interroger des données structurées volumineuses utilisées simultanément
par différents programmes ou différents utilisateurs. Cela est impossible avec les représentations tabulaires
étudiées en classe de première.
Des systèmes de gestion de bases de données (SGBD) de très grande taille (de l’ordre du pétaoctet) sont au
centre de nombreux dispositifs de collecte, de stockage et de production d’informations.
L’accès aux données d’une base de données relationnelle s’effectue grâce à des requêtes d’interrogation et de
mise à jour qui peuvent par exemple être rédigées dans le langage SQL (Structured Query Language). Les
traitements peuvent conjuguer le recours au langage SQL et à un langage de programmation.
Nous allons voir comment structurer une base de donnée : la manière de structurer ces données s’appelle le
domaine fonctionnel.
Puis nous verrons comment gérer une base de données et effectuer des requêtes dans cette base de données.

2.1 De quoi est fait le modèle fonctionnel ?


Il faut tout d’abord identifier les différents éléments qui composent le domaine fonctionnel.
On utilise une démarche orientée objet, et un langage de modélisation normalisé : UML.

2.1.1 Définitions préalables


Sur la figure 2.1, on observe une classe « car » contenant des objets « polo, mini, beetle » et à cette classe sont as-
sociées des méthodes « refuel(), getFuel(), setSpeed(), getSpeed(), drive() », et des attributs « fuel, maxspeed ».
Pour commencer, nous allons définir tout ce vocabulaire.

F IGURE 2.1 – Les différents éléments d’une base de données.

Un objet
En informatique, les objets correspondent à des concepts plutôt qu’à des objets physiques : un compte en
banque, une équation mathématique, une histoire. Les objets peuvent également être virtuels : internet, un
mirage, un objet virtuel en optique géométrique.
Un objet informatique est défini à la fois par des informations et par des comportements : on dit qu’un objet
encapsule des informations et un comportement.

28
CHAPITRE 2. BASES DE DONNÉES

Définition 5 : La notion d’objet : Un objet est un élément autonome, réel (un crayon, une voiture...) ou
abstrait (un ensemble, une liste...). En UML tout objet possède un ensemble d’attributs (sa structure) et un
ensemble de méthodes (son comportement). Un attribut est une variable destinée à recevoir une valeur. Une
méthode est un ensemble d’instructions prenant des valeurs en entrée et modifiant les valeurs des attributs ou
produisant un résultat.

Un objet peut également être anonyme, on précède alors son nom d’un double point. Exemple : élèves et
professeurs (cf. figure 2.2).

F IGURE 2.2 – Représentation d’objets anonymes en UML.

Une classe
Dans la démarche orientée objet, on définit :

Définition 6 : La classe : Une classe est le modèle abstrait d’un objet qui représente une entité, un élément du
domaine fonctionnel. Tous les objets ayant les mêmes propriétés sont rassemblés dans ce qu’on appelle une
classe. Une classe définit les propriétés de tous les objets qui lui sont associés. Une classe est schématisée par
un rectangle dans lequel le nom de la classe n’est pas souligné. Il y figure les attributs et les comportements
de tous les objets de cette classe (nom, liste des attributs)(cf. figure 2.3).

Par exemple : la classe voiture.

F IGURE 2.3 – Représentation de la classe d’un objet en UML.

Les classes doivent être nommées avec un nom pertinent (ex Restaurant ne pourra pas contenir aussi les bars,
sinon il faut changer le nom en « établissement ».

Numérique et Sciences informatiques Terminale 29 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Une instance
Au sein d’une classe, les objets portent le nom d’instance de la classe. Le terme instance insiste sur l’apparte-
nance à une classe alors que le terme objet est plus générique.

Définition 7 : L’instance : L’instance est un objet de la classe, elle est créée à partir de la classe et elle
possède les mêmes attributs et comportements que toutes les autres instances de la classe.

ex : une Polo est une instance de la classe voiture.

Représentation d’un objet comme instance d’une classe


Une instance se représente sous la forme d’un rectangle dans lequel le nom de l’objet est souligné.
A sa suite, séparé par un double point, est indiqué le nom de la classe (cf. figure 2.4).
Les traits qui relient les objets symbolisent les liens qui existent entre les objets.
Les commentaires sont représentés dans des rectangles à coins supérieurs droits repliés, ils sont reliés à
l’objet sur lequel ils portent par un trait discontinu.
Exemples : clients d’une banque et comptes associés à chacun d’eux (cf. figure 2.4).

F IGURE 2.4 – Représentation des liens entre les instances en UML.

2.2 Quelle démarche adopter pour représenter un diagramme de classe ?


L’objet possède donc plusieurs caractéristiques, entre autres :
❧ un nom : nom commun unique, il donne le type de l’objet (ex. : voiture)
❧ des attributs : ce sont les propriétés de l’objet (ex. : couleur, masse à vide, longueur...)
Un objet est défini par trois caractéristiques : un état, un comportement, une identité :

objet = état + comportement + identité

2.2.1 État d’un objet


L’état d’un objet regroupe tous ses attributs, c’est-à-dire ses propriétés. L’état à un instant donné de l’objet est
défini par les valeurs de ses attributs (les termes "variables d’instance" et "données" sont également utilisées).
Les états d’un objet peuvent évoluer au cours du temps.
Leur type est indiqué entre le nom de l’attribut et sa valeur : "string" pour une chaîne de caractère, "integer"
pour des entiers, "boolean" pour une grandeur de type booléenne ("vrai" ou "faux"), "double" pour les nombres
réels à virgule, etc.
Dans l’exemple de la figure 2.5 , l’état de l’objet "Xsara Picasso" est défini par les attributs de la classe voiture
"marque", "couleur" et "contenu du réservoir".

Numérique et Sciences informatiques Terminale 30 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

F IGURE 2.5 – Représentation de l’état d’un objet en UML.

2.2.2 Comportement d’un objet


Le comportement regroupe les compétences d’un objet et décrit ses actions ou ses réactions qu’on désigne par
le terme opérations de l’objet.

2.2.3 Identité d’un objet


L’objet possède une identité, qui permet de le distinguer des autres objets, indépendamment de son état. On
construit généralement cette identité grâce à un identifiant découlant naturellement du problème (par exemple
un produit pourra être repéré par un code-barre, une voiture par un numéro de série, une personne par son
numéro de sécurité sociale, un livre par son ISBN, ...).

2.2.4 Représentation des classes


Une classe est schématisée par un rectangle dans lequel le nom de la classe n’est pas souligné. Il y figure
ses attributs et son comportement (opérations) (cf. figure 2.6). Il est possible de définir des compartiments
supplémentaires, par exemple pour lister les responsabilités ou les exceptions.

F IGURE 2.6 – Représentation de la classe d’un objet en UML.

Le nom d’une classe est au singulier, constitué d’un nom commun précédé ou suivi d’un ou plusieurs adjectifs
qualifiant le nom. Le nom est représentatif de l’ensemble des instances constituant la classe.
Les attributs contiennent les données encapsulées dans les objets de la classe. Les opérations représentent les
comportements des objets de cette classe.
Par exemple dans un logiciel concernant la sécurité sociale, une classe "Personne" pourrait définir les attributs
"NumeroSS" qui représente le numéro de sécurité sociale, "Nom", "Prénom" et le comportement des instances
pourrait indiquer qu’elles peuvent changer d’adresse (cf. figure 2.7).

F IGURE 2.7 – Autre exemple de classe

Numérique et Sciences informatiques Terminale 31 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

2.3 Modèle physique de données


Le fait d’identifier correctement à la fois les classes, les instances de classe, leurs états, comportement et identité
permet de construire le modèle physique de données (MPD). Ce MPD correctement définit permettra à plusieurs
personnes d’utiliser la même base de données.
Bilan : Synthèse des règles à respecter.

En UML :
Pour le nom des classes :
❧ au singulier
❧ avec des lettres non accentuées
❧ commençant par une majuscule
❧ utilisant la notation chameau (une majuscule au début de chaque mot suivant)
Ex : PierrePrecieuse
Pour les attributs :
❧ au singulier
❧ avec des lettres non accentuées
❧ commençant par une minuscule
❧ utilisant la notation chameau (une majuscule au début de chaque mot suivant)
Ex : couleurYeux
Dans le MPD :
Pour le nom des tables et des attributs :
❧ au singulier
❧ avec des lettres non accentuées
❧ en minuscules
❧ les mots étant séparés par des underscores (_)
Ex : pierre_precieuse, couleur_yeux

Exercice n°13 :
Par groupe de 2 ou 3 :
1. 10 minutes : brain storming : dans le but de concevoir une base de données, trouver un sujet (en ayant
peut-être à l’esprit un site internet par-dessus) qui pourrait être décrit par une classe, des attributs de la
classe et des actions applicables à la classe.
2. 10 minutes : structurer vos objets en utilisant la notation UML,
3. 30 minutes : représenter votre modèle physique de données à la manière des Figures 2.3 à 2.7, sur une
grande feuille en laissant de la place pour compléter le schéma.

2.4 Modèle relationnel


Le modèle relationnel permet de décrire les différents type de relations qui peuvent exister entre les classes du
MDP.

Numérique et Sciences informatiques Terminale 32 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

2.4.1 Relation d’instanciation


La relation d’instanciation qui existe entre une classe et un objet est représentée par une flèche en pointillé
allant de la classe vers l’objet (cf. figure 2.8).

F IGURE 2.8 – Relation d’instanciation entre une classe et une de ses instances.

2.4.2 Classe vs Objet


Un diagramme de classes montre la construction de la base de données d’un point de vue général. Un dia-
gramme d’objet représente plutôt un cas particulier, une situation concrète à un instant donné.
Bien que le système final manipule principalement des objets, la description du système se fait principalement
à travers les classes. Ceci permet de simplifier le problème posé en décrivant toutes les caractéristiques des
classes de manière synthétique, et non de chaque objet.
Nous allons voir comment définir des relations entre les instances de différentes classes.

Définition 8 : Le lien qui existe entre deux classes d’objets est appelé une association.

Exemple : Dans une médiathèque un adhérent peut emprunter au maximum trois exemplaires. Sur place il peut
consulter autant d’exemplaires qu’il le souhaite.
Il existe un lien entre les objets de la classe "Adhérent" et les objets de la classe "Exemplaire". Cette association
est représentée par un trait plein entre les deux classes. Elle peut avoir un nom (un verbe à l’infinitif en général)
par exemple "emprunter" et "consulter" (cf. figure 2.9).

F IGURE 2.9 – Associations entre deux classes.

2.4.3 Cardinalité
Les cardinalités de l’association représentent le nombre d’instances impliquées dans l’association.

Numérique et Sciences informatiques Terminale 33 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Dans l’exemple de la figure 2.9, la cardinalité "0..3" indique qu’un adhérent peut être associé à 0, 1, 2 ou 3
livres, c’est-à-dire qu’il peut emprunter au maximum trois livres. A l’inverse un livre ne peut être emprunté que
par un seul adhérent (cardinalité "0..1"). Le symbole "*" sert à représenter une valeur potentiellement infinie.
Les cardinalités se trouvent du coté de l’objet qu’elles décrivent.

2.4.4 Rôle
Sur le lien peuvent également figurer les noms des rôles des classes. Ils permettent de lever des ambiguïtés.
Sans indication de rôles dans la figure 2.10 nous ne pouvons pas savoir si c’est la société d’intérimaire qui
fournit du personnel à une société de nettoyage ou si c’est une société de nettoyage qui travaille dans les locaux
d’une société d’intérim. Dans le diagramme de la figure 2.10 c’est la seconde hypothèse qui est la bonne.

F IGURE 2.10 – Rôles et sens de lecture.

2.4.5 Sens de lecture


Le sens de lecture peut être indiqué par une flèche à coté du nom de l’association (<, >, ˆ, ou v) ou par
une navigabilité sur le lien. Dans l’exemple de la figure 2.10 elle indique que le sujet de "travaillerPour" est
"SociétéNettoyage" et que son complément d’objet est "SociétéInterim" : la société de nettoyage travaille pour
la société d’intérimaire.
En première lecture, passer au point 2.5, les éléments suivants constituent des compléments.

2.4.6 Associations n-aires


Une association entre plus de deux classes se représente via un losange (cf. exemple de la figure 2.11).

F IGURE 2.11 – Association ternaire.

2.4.7 Association réflexive


Une même classe peut se trouver à chaque extrémité d’une association. Il s’agit alors d’une association ré-
flexive, reliant entre elles les instances d’une même classe. Dans l’exemple de la figure 2.12 l’association
indique que chaque chat a exactement une mère et un père.

Numérique et Sciences informatiques Terminale 34 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

F IGURE 2.12 – Association "ascendant/descendant direct" entre chats.

2.4.8 Classe d’association


Lorsque des informations sont nécessaires pour décrire une association, il est possible de les stocker dans
une classe d’associations. Elle se représente de la même manière qu’une classe classique, elle est reliée à
l’association par une ligne en pointillé (cf. figure 2.13).

F IGURE 2.13 – Classe d’associations.

2.4.9 Contrainte
Une contrainte permet de préciser le contexte du modèle en positionnant des restrictions. Elles sont indiquées
entre deux accolades. Elles sont le plus souvent exprimées en langage naturel. On relie la contrainte avec les
éléments concernés par un trait en pointillé (cf. figures 2.14).

F IGURE 2.14 – Utilisation d’une contrainte.

2.4.10 Attribut calculé


Un attribut calculé est un attribut dont la valeur est déduite des valeurs d’autres attributs. Le nom d’un attribut
calculé est précédé d’un / et il est suivi d’une contrainte donnant le moyen de calculer sa valeur (cf. figure 2.15).

F IGURE 2.15 – Attribut calculé.

Numérique et Sciences informatiques Terminale 35 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

2.4.11 Signature des méthodes


Une méthode d’une classe peut prendre des paramètres et renvoyer un résultat. Les paramètres sont des valeurs
transmises :
❧ à l’aller lors de l’envoi d’un message appelant la méthode ;
❧ au retour d’appel de la méthode.
Le résultat est une valeur transmise à l’objet appelant lors du retour d’appel. Devant le nom du paramètre il est
possible d’indiquer par un mot clé la direction dans laquelle il est transmis :
❧ in : la valeur du paramètre est en entrée seule et non modifiée par l’exécution de l’opération ;
❧ out : la valeur du paramètre n’est transmise qu’au retour de l’appel, l’appelant peut ainsi récupérer des
informations ;
❧ inout : la valeur du paramètre est transmise à l’appel et au retour.
Si aucun mot clé n’est précisé, la valeur du paramètre n’est transmise qu’à l’appel (cf. figure 2.16).

F IGURE 2.16 – Signature des méthodes

2.5 Base de données relationnelle


Nous allons maintenant voir comment identifier nos classes pour créer une base de données relationnelle.
Dans l’exemple ci-dessous, pour identifier un tuple, on ne peut pas se baser :
❧ ni sur le prénom : Phoebe Buffay et Phoebe Abbott ont le même prénom ;
❧ ni sur le nom : Monica Geller et Ross Geller ont le même nom ;
❧ et encore moins sur la couleur des yeux... !

prenom nom couleur_yeux


Chandler Bing bleu
Phoebe Buffay vert
Monica Geller bleu
Ross Geller marron
Rachel Green bleu
Joey Tribbiani marron
Phoebe Abbott bleu

Dans ce cas, afin de vous assurer de pouvoir identifier un tuple de manière unique, il y a deux solutions :
1. identifier un tuple par le nom ET le prénom ;
2. ajouter un attribut (une colonne) pour accueillir un identifiant dont on sera sur qu’il sera unique.

2.5.1 Notion de clé primaire


On ajoute donc un attribut spécifique id dans la table Ami :

Numérique et Sciences informatiques Terminale 36 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

prenom nom couleur_yeux


1 Chandler Bing bleu
2 Phoebe Buffay vert
3 Monica Geller bleu
4 Ross Geller marron
5 Rachel Green bleu
6 Joey Tribbiani marron
7 Phoebe Abbott bleu

Cet identifiant n’a rien de concret. Parfois, on ajoute le nom de la table en préfixe (ami_id).

Définition 9 : Lors de la création de la table dans la base de données, un attribut spécifique sera indiqué
comme l’identifiant des tuples de la table : c’est la clé primaire, ou primary key (PK) en anglais.

Dans le modèle physique de données (MPD), la clé primaire est située dans la partie du milieu et porte l’indi-
cation PK :

ami
id : INTEGER NOT NULL [PK]
prenom : VARCHAR
nom : VARCHAR
couleur_yeux : VARCHAR

Pour assurer la cohérence du modèle relationnel, la clé primaire doit être unique et surtout, elle ne doit jamais
changer.
Avant de faire un lien entre les tuples de différentes tables (ou instances de classes), il faut pouvoir les identifier
de manière unique et certaine. La clé primaire va servir de d’identifiant unique pour relier les tuples de la table
à ceux d’une autre table !

2.5.2 Notion de clé étrangère


Mettre en relation les lignes des tables entre elles grâce aux clés étrangères.

Définition 10 : Clé étrangère : Dans une table, on ajoute une colonne qui fait référence à la clé primaire
d’une autre table : c’est ce que l’on appelle une clé étrangère ou foreign key (FK) en anglais.

La valeur de la clé étrangère dans un tuple n’est rien d’autre que la valeur de la clé primaire du tuple lié.
Imaginons l’association pilote.machine entre un Motard et une Moto.

Numérique et Sciences informatiques Terminale 37 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

F IGURE 2.17 – Exemple de clés primaires qu’il faut compléter d’une clé étrangère

Exercice n°14 :
1. Reprenez votre schéma de base de données,
2. définissez le modèle relationnel : les relations, la cardinalité, les rôles éventuellement, les clés primaires
et clés étrangère qui mettent en relation ce qui deviendra vos tables de données.
3. Quand le résultat vous semble complet : remettre au propre votre schéma.

Exercice n°15 :

id titre realisateur ann_sortie note_sur_10


1 Alien, le huitième passager Scott 1979 10
2 Dune Lynch 1985 5
3 2001 : l’Odyssée de l’espace Kubrick 1968 9
4 Blade Runner Scott 1982 10

TABLE 2.1 – Relation FILMS

1. En partant de la relation FILMS ci-dessus, créez une relation REALISATEURS (attributs de la relation
REALISATEURS : id, nom, prénom, ann_naissance) à l’aide d’une recherche.
2. Modifiez ensuite la relation FILMS afin d’établir un lien entre les relations FILMS et REALISATEURS.
3. Préciser l’attribut qui jouera le rôle de clé étrangère.

On appelle schéma relationnel l’ensemble des relations présentes dans une base de données.

Quand on vous demande le schéma relationnel d’une base de données, il est nécessaire de fournir les informa-
tions suivantes :
❧ Les noms des différentes relations ;
❧ Pour chaque relation, ma liste des attributs avec leur domaine respectif ;
❧ Pour chaque relation, la clé primaire et éventuellement la clé étrangère.
Voici un exemple pour les relation LIVRES et AUTEURS :

Numérique et Sciences informatiques Terminale 38 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

AUTEURS (id : INT, nom : TEXT, prenom : TEXT, ann_naissance : INT, langue_ecriture :
TEXT)
LIVRES (id : INT, titre : TEXT, #id_auteur : INT, ann_publi : INT, note : INT)
Les attributs soulignés sont les clés primaires, le # signifie qu’on a une clé étrangère.

2.6 Système de gestion de bases de données relationnelles


Il existe différents types de logiciels qui sont des systèmes de gestion de bases de données : SGBD.
❧ gratuit et libre : PostgreSQL ou mariaBD
❧ gratuit mais sous licence propriétaire : Microsoft SQLserveur express
❧ payant : Oracle Database, Microsoft SQLserveur ou Sybase
❧ sous Linux : MySQL, SQlite, Base(Libreoffice), phpMyAdmin + MySQLworkbench, le module php-
mysql.
❧ PostgreSQL : langage spécifique SQL : standardisé (Structured Query Language).
❧ liste non héxaustive

2.7 Installation
Nous allons utiliser les Raspberry.
Pour cela commencez par installer « DB Browser for SQLite » : https://sqlitebrowser.org/

2.7.1 Ubuntu and Derivatives


For Ubuntu and derivaties, @deepsidhu1313 provides a PPA with the latest release here :

https ://launchpad.net/ linuxgndu/+archive/ubuntu/sqlitebrowser


To add this ppa just type in these commands in terminal :

sudo add-apt-repository -y ppa :linuxgndu/sqlitebrowser


Then update the cache using : sudo apt-get update
Install the package using : sudo apt-get install sqlitebrowser

2.7.2 Debian
Note that Debian focuses more on stability rather than newest features. Therefore packages will typically
contain some older version, compared to the latest release.

Update the cache using : sudo apt-get update


Install the package using : sudo apt-get install sqlitebrowser

2.8 On y va !
Après avoir lancé « DB Browser for SQLite » vous devriez avoir quelque chose qui ressemble à ceci :

Numérique et Sciences informatiques Terminale 39 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Cliquez sur « Nouvelle base de données » après avoir choisi un nom pour votre base de données (par exemple :
« db_livres.db ») Une fenêtre s’ouvre, cliquez sur annuler : nous avons juste crée la base de données, mais elle
ne contient encore aucune table.
Cliquez sur l’onglet « Executer SQL » ; la fenêtre change de forme :

Copier la commande SQL dans la fenêtre SQL1. Exécuter en appuyant sur le triangle vert ou sur F5. Un
message de bonne exécution apparaît.

Nous avons déclaré une table est ses attributs, en précisant le domaine de chaque attribut. On peut ajouter la
définition de la clé primaire, qui évitera les erreurs sur Id. (Pour cela effacer d’abord la table pour en créer une
nouvelle).

Ajoutons les données :

Numérique et Sciences informatiques Terminale 40 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Les données sont saisies dans la table, on le vérifie par l’onglet : « Parcourir les données ».

2.9 Exécuter une requête


Exécuter la commande ci-dessous :

SELECT id, titre, auteur, ann_publi, note
FROM LIVRES
✝ ✆
Après un petit temps, vous devez voir apparaître le résultat de la requête dans la sous-fenêtre intermédiaire.

Numérique et Sciences informatiques Terminale 41 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Tester la requête ci-dessous, que se passe-t-il ? :



SELECT titre, auteur
FROM LIVRES
✝ ✆
Sélectionner selon une condition : testez maintenant cette requête, que se passe-t-il ?

SELECT titre, auteur, ann_publi
FROM LIVRES
WHERE auteur=’Asimov’
✝ ✆

Ecrivez et testez une requête permettant d’obtenir uniquement les titres des livres écrits par Philip K.Dick.
Essayez maintenant des requêtes de ce type :

SELECT titre, auteur, ann_publi
FROM LIVRES
WHERE auteur=’Asimov’ AND ann_publi > 1953
✝ ✆
En triant les résultats :

SELECT titre, ann_publi
FROM LIVRES
WHERE auteur=’Asimov’ ORDER BY ann_publi DESC
✝ ✆
Que se passe-t-il si vous tapez ceci ?

SELECT auteur
FROM LIVRES
✝ ✆
Pour résoudre ce problème, saisissez :

SELECT DISTINCT auteur
FROM LIVRES
✝ ✆

2.9.1 Avec deux tables


Créez une seconde table avec les auteurs :

CREATE TABLE AUTEURS
(id INT, nom TEXT, prenom TEXT, ann_naissance INT, lang_ecriture TEXT, PRIMARY
KEY (id));
✝ ✆
Y saisir les données :

INSERT INTO AUTEURS
(id, nom, prenom, ann_naissance, lang_ecriture)
VALUES
(1, ’Orwell’, ’George’, 1903, ’anglais’),
(2, ’Herbert’, ’Franck’, 1920, ’anglais’),
(3, ’Asimov’, ’Isaac’, 1920, ’anglais’),
(4, ’Huxley’, ’Aldous’, 1894, ’anglais’),

Numérique et Sciences informatiques Terminale 42 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

(5, ’Bradbury’, ’Ray’, 1920, ’anglais’),


(6,’K.Dick’, ’Philip’, 1928, ’anglais’),
(7, ’Barjavel’, ’René’, 1911, ’français’),
(8,’Boulle’, ’Pierre’, 1912, ’français’),
(9, ’Van Vogt’, ’Alfred Elton’, 1912, ’anglais’),
(10, ’Verne’, ’Jules’, 1828, ’français’)
✝ ✆

2.9.2 Les jointures


Et maintenant nous allons joindre les informations issues des deux tables, en identifiants les auteurs :

SELECT *
FROM LIVRES
INNER JOIN AUTEURS ON LIVRES.auteur = AUTEURS.nom
✝ ✆

Remarquez que nous aurions pu définir, dans la table LIVRES que le nom de l’auteur se trouve dans une autre
table et utiliser une Foreign Key.
Pour cela remonter sur l’onglet « Structure de la base de données » et cliquez sur la table LIVRES. Supprimer
la table (faites une sauvegarde avant), et Définissez une clé étrangère :

CREATE TABLE LIVRES
(id INT, titre TEXT, id_auteur INT, ann_publi INT, note INT, PRIMARY KEY (id),
FOREIGN KEY (id_auteur) REFERENCES AUTEURS(id));
✝ ✆

INSERT INTO LIVRES
(id, titre, id_auteur, ann_publi, note)
VALUES
(1 , ’1984’, 1, 1949,10),
(2 , ’Dune’, 2, 1965 ,8),
(3 , ’Fondation’, 3, 1951 ,9),
(4 , ’Le meilleur des mondes’,4, 1931 ,7),
(5 , ’Fahrenheit 451 ’, 5, 1953 ,7),
(6 , ’Ubik’, 6, 1969 ,9),
(7 , ’Chroniques martiennes’, 5, 1950 ,8),
(8 , ’La nuit des temps’, 7, 1968 ,7),
(9 , ’Blade Runner’, 6, 1968 ,8),
(10 , ’Les Robots’, 3, 1950 ,9),
(11 , ’La planète des singes’, 8, 1963 ,8),
(12 , ’Ravage ’, 7, 1943 ,8),

Numérique et Sciences informatiques Terminale 43 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

(13 , ’Le maitre du haut chateau’,6, 1962 ,8),


(14 , ’Le monde des A ’, 9, 1945 ,7),
(15 , ’La fin de l éternité’, 3, 1955 ,8),
(16 , ’De la Terre à la Lune’, 10, 1865 ,10)
✝ ✆
Puis tapez la requête SQL précédente :

SELECT *
FROM LIVRES
INNER JOIN AUTEURS ON LIVRES.id_auteur = AUTEURS.id
✝ ✆

Exercice n°16 :
Dans le cas d’uns jointure, il est tout à fait possible de sélectionner certains attributs et pas d’autres.
1. Afficher le nom le prénom et le titre de la table AUTEURS en y joignant les informations de la table
LIVRES
2. Affichez le titre, le nom et le prénom de la table LIVRES e, y joignant les informations de la table
AUTEURS
3. Affichez le titre, le nom et le prénom de la table LIVRES e, y joignant les informations de la table
AUTEURS dont les années de publications sont postérieures à 1950.

Nous avons vu la jointure la plus simple INNER JOIN, il faut savoir qu’il en existe des plus
complèxes (CROSS JOIN, LEFT JOIN, RIGHT JOIN) mais qui ne sont pas au programme
cette année.

2.9.3 Mise à jour de la base de données


Supposons que nous voulons ajouter un livre dans la base. Dans la table LIVRES il y a une clé étrangère qui
pointe sur l’auteur. On va donc commencer par ajouter un nouvel auteur dans la table AUTEURS :

INSERT INTO AUTEURS
(id, nom, prenom, ann_naissance, lang_ecriture)
VALUES
(11, ’Simmons’, ’Dan’, 1948, ’anglais’)
✝ ✆
Puis on peut lui attribuer un livre dans la table LIVRES :

INSERT INTO LIVRES
(id, titre, id_auteur, ann_publi, note)
VALUES
(17 , ’Hypérion’, 11, 1989,8)
✝ ✆
Aïe ! ! ! la note attribuée à Hypérion est 7 et non 8 :

UPDATE LIVRES
SET note =7
WHERE titre = ’Hypérion’
✝ ✆

Exercice n°17 :
1. En suivant la même logique, supprimer à l’aide de DELETE le livre dont le titre est Hypérion
2. Puis supprimer tout ceux publiés avant 1945.

Numérique et Sciences informatiques Terminale 44 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

2.10 Pour aller plus loin


Une bonne manière de procéder pour créer une base de données est de suivre les étapes suivantes :
1. décrire le domaine fonctionnel à l’aide d’un diagramme de classe (UML) ;
2. à l’aide du diagramme de classe, élaborer un modèle physique de données(MPD) ;
3. avec l’outil de modélisation du MPD, on génère un script SQL, permettant la création de la base ;
4. on utilise le script sur le système de gestion de bases de données pour créer la base ;
5. et enfin, on importe les données à l’aide de requêtes SQL.

2.11 Langage SQL


SQL permet d’interagir avec la base de données. Pour cela on peut générer un script SQL.
On passe du modèle physique de données au script à l’aide de SQL.
Voici un script SQL avec 2 instructions :

CREATE SEQUENCE public.niveau_bug_id_seq;
CREATE SEQUENCE public.statut_id_seq;
✝ ✆
Il est possible d’ajouter des commentaires qui seront ignorés par le SGBD, avec – ou /* */ :

-- Voici un commentaire sur une ligne

/* Voici un commentaire
sur plusieurs lignes */
✝ ✆
Les chaînes de caractères sont mises entre simples guillemets ’ :

SELECT * FROM projet
WHERE nom = ’Mon super projet’;
✝ ✆
Les éléments sont nommés en utilisant la notation pointée element.sous_element :

--- afficher la table "projet" se trouvant dans le schéma "public" :
SELECT * FROM public.projet;
✝ ✆
Par habitude, bien que cela ne soit pas obligatoire, les commandes SQL, et plus généralement tous les mots clés
du langage, sont écrits en majuscules. Cela améliore la lisibilité des requêtes.
Les espaces multiples et les retours à la ligne n’ont pas de signification en SQL. N’hésitez pas à les utiliser pour
obtenir une requête SQL plus lisible :

-- je suis sur que vous trouvez celle-ci plus lisible :

SELECT
projet.nom,
ticket.numero,
ticket.titre
FROM
public.ticket
JOIN public.projet ON projet.id = ticket.projet_id
WHERE
projet_id = 10
AND date >= ’2016-01-01’
AND date <= ’2016-01-20’;

-- que celle là :

select projet.nom,ticket.numero,ticket.titre from


public.ticket join public.projet on projet.id=ticket.projet_id
where projet_id=10 and date>=’2016-01-01’ and date<=’2016-01-20’;
✝ ✆

Numérique et Sciences informatiques Terminale 45 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Toutes les commandes SQL utilisables avec PostgreSQL et leurs syntaxes sont décrites dans la documentation :
http://docs.postgresql.fr/9.6/sql-commands.html.
Création des séquences

CREATE SEQUENCE public.niveau_bug_id_seq;
✝ ✆
Cela va créer la séquence niveau_bug_id_seq dans le schéma public.

ALTER SEQUENCE public.niveau_bug_id_seq OWNED BY public.niveau_bug.id;
✝ ✆
Cela va associer la séquence niveau_bug_id_seq à la colonne id de la table niveau_bug. Ainsi, si la colonne est
supprimée, la séquence sera automatiquement supprimée.

Création des tables



CREATE TABLE public.niveau_bug (
id INTEGER NOT NULL DEFAULT nextval(’public.niveau_bug_id_seq’),
ordre INTEGER NOT NULL,
libelle VARCHAR(100) NOT NULL,
CONSTRAINT niveau_bug_pk PRIMARY KEY (id)
);
✝ ✆
Ceci va créer la table niveau_bug dans le schéma public avec les colonnes suivantes :
nom type nullable valeur par défaut
id INTEGER non nextval(’public.niveau_bug_id_seq’)
ordre INTEGER non
libelle VARCHAR(100) non
La clé primaire de la table sera composée de la colonne id et sera associée à une contrainte d’unicité nommée
niveau_bug_pk. Ce sera également le nom de l’index de clé primaire créé automatiquement par le SGBD.
La valeur par défaut dans la colonne id est nextval(’public.niveau_bug_id_seq’). Cela veut dire que si on ne
précise rien, lors de l’ajout d’une ligne, la valeur suivante de la séquence public.niveau_bug_id_seq sera utilisée
dans la colonne id. C’est comme cela que sont gérées les colonnes auto-incrémentées avec PostgreSQL.
Un autre exemple de création de table :

CREATE TABLE public.historique_statut (
ticket_numero INTEGER NOT NULL,
statut_id INTEGER NOT NULL,
date TIMESTAMP NOT NULL,
commentaire_id INTEGER,
utilisateur_id INTEGER NOT NULL,
CONSTRAINT historique_statut_pk PRIMARY KEY (ticket_numero, statut_id)
);
✝ ✆
Ici, la clé primaire de la table historique_statut est constituée des colonnes ticket_numero et statut_id. La co-
lonne commentaire_id est nullable (car la multiplicité de l’association est 0..1).
Création de relations (clés étrangères) Les clés étrangères sont ajoutées une fois que toutes les tables ont été
créées.

ALTER TABLE public.commentaire ADD CONSTRAINT utilisateur_commentaire_fk
FOREIGN KEY (utilisateur_id)
REFERENCES public.utilisateur (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
✝ ✆
Ceci modifie la table commentaire du schéma public pour y ajouter une contrainte de clé étrangère nommée
utilisateur_commentaire_fk, sur la colonne utilisateur_id et pointant sur la colonne id de la table utilisateur du
schéma public.

Numérique et Sciences informatiques Terminale 46 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

ON DELETE NO ACTION signifie qu’en cas de suppression d’une ligne dans la table utilisateur, si une ligne
de la table commentaire utilise la valeur de la clé primaire de cette ligne dans sa clé étrangère, alors une erreur
est produite.
ON UPDATE NO ACTION signifie qu’en cas de modification de la valeur d’une clé primaire dans la table
utilisateur, si une ligne de la table commentaire utilise cette valeur dans sa clé étrangère, alors une erreur est
produite.
NOT DEFERRABLE signifie que les erreurs (dues au ON DELETE NO ACTION et ON UPDATE NO AC-
TION) sont remontées immédiatement et non à la validation de la transaction. (Nous verrons ce qu’est une
transaction plus loin dans ce cours.)

ALTER TABLE public.bug_version_affectee ADD CONSTRAINT
version_bug_version_affectee_fk
FOREIGN KEY (version\_affectee_projet_id, version_affectee_numero)
REFERENCES public.version (projet_id, numero)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;
✝ ✆
Ici la clé étrangère est constituée d’un couple de colonnes (version_affectee_projet_id ;version_affectee_numero)
pointant sur la clé primaire elle aussi constituée de deux colonnes (projet_id ;numero).

2.11.1 Execution du script SQL


Exécutez le script SQL de création du schéma Nous arrivons à l’avant-dernière étape de création de la base de
données : la création du schéma.
Il suffit pour cela de faire exécuter le script SQL par PostgreSQL.
Eh bien, là encore, vous allez utiliser pgAdmin (ou SQuirreL SQL...) :
Connectez-vous au serveur PostgreSQL en utilisant l’utilisateur propriétaire de la base de données.
Cliquez sur l’icône SQL.
Une fenêtre s’ouvre vous permettant de saisir des requêtes SQL.
Ouvrez le fichier create_db.sql que vous avez enregistré depuis SQL Power Architect.
Exécutez le script SQL en cliquant sur l’icône .
Félicitations, vous venez de créer votre première base de données !
Dernière étape : ajouter des données dans la base de données et utiliser cette base.

2.11.2 Utiliser la basse de donnée :


Utiliser une base de données peut se résumer à quatre opérations principales, le fameux CRUD :
❧ Create : créer des données
❧ Read : lire des données
❧ Update : modifier des données
❧ Delete : supprimer des données

Create avec la commande INSERT


La création de données consiste à insérer de nouvelles lignes dans les tables. Pour cela, on va utiliser la com-
mande SQL INSERT :

INSERT INTO public.utilisateur (
nom,
prenom
) VALUES (
’Dalton’,
’Joe’
);
✝ ✆

Numérique et Sciences informatiques Terminale 47 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Avec cette instruction, je viens d’ajouter l’utilisateur Joe Dalton dans la table utilisateur.
La colonne id a pour valeur par défaut nextval(’utilisateur_id_seq’). Comme je n’ai précisé aucune valeur pour
la colonne id dans la requête INSERT, une valeur a été attribuée automatiquement par PostgreSQL en utilisant
la séquence utilisateur_id_seq.

Read avec la commande SELECT


Pour récupérer toutes les informations du projet System42 :

SELECT * FROM projet
WHERE nom = ’System42’;
✝ ✆
Il est aussi possible de croiser des informations en s’appuyant sur les clés étrangères. Pour récupérer le nom et
le prénom du responsable du projet System42 :

SELECT nom, prenom
FROM utilisateur
WHERE
id IN (
SELECT responsable_id FROM projet
WHERE nom = ’System42’
)
;
✝ ✆
Et même fusionner les tables (on appelle cela une jointure) :

/* Je récupère toutes les colonnes de la table projet
+ le nom et le prénom de l’utilisateur */
SELECT
projet.*,
utilisateur.nom, utilisateur.prenom
FROM
projet,
JOIN utilisateur ON utilisateur.id = projet.responsable_id
WHERE
projet.nom = ’System42’
;
✝ ✆

Update avec la commande UPDATE


Voici comment modifier le titre et le statut actuel du ticket numéro 256 :

UPDATE ticket SET
titre = ’Le nouveau titre’,
statut_actuel_id = 3
WHERE numero = 256;
✝ ✆

Delete avec la commande DELETE


Par suppression, on entend suppression de ligne dans une table, et non suppression de valeur dans une colonne.
Si vous voulez supprimer une valeur dans une colonne, il faut faire un requête d’UPDATE pour passer la valeur
de la colonne à NULL.
Voici comment supprimer le projet ayant l’id 18 :

DELETE FROM projet WHERE id = 18;
✝ ✆
Pour supprimer tous les utilisateurs qui s’appellent Dalton :

DELETE FROM utilisateur WHERE nom = ’Dalton’;
✝ ✆

Numérique et Sciences informatiques Terminale 48 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Les transactions
Il est possible de regrouper les instructions dans une transaction comme s’il s’agissait d’un seul bloc.
Une transaction est ouverte avec l’instruction BEGIN TRANSACTION ; et elle est clôturée de deux manières :
soit en la validant (instruction COMMIT ;)
soit en l’annulant (instruction ROLLBACK ;)
Ainsi, les modifications apportées par les commandes dans la transaction se seront pas visibles en dehors
de la transaction tant que celle-ci n’aura pas été validée. De même, si la transaction est annulée, alors les
modifications ne seront pas appliquées.
Cela est particulièrement utile pour former un ensemble d’instructions cohérent.
Imaginez par exemple que vous vouliez supprimer un projet de la base. À cause des clés étrangères, vous allez
aussi devoir supprimer les lignes des autres tables qui référencent ce projet (ticket, version) et ceci en cascade
(tables bug, evolution, bug_version_affectee...).
Vous allez donc :
démarrer une transaction
faire toutes les requêtes de DELETE de la grappe concernée par le projet (en commençant par les feuilles)
valider la transaction
Ce qui va se faire comme ceci :

BEGIN TRANSACTION; -- Ouverture de la transaction

-- Projet à supprimer : projet.id = 18

DELETE FROM evolution


WHERE EXISTS (
SELECT 1 FROM ticket
WHERE ticket.numero = evolution.ticket_numero
AND ticket.projet_id = 18
);

/*
DELETE FROM bug_version_affectee ...
DELETE FROM bug ...
...
DELETE FROM ticket ...
DELETE FROM version ...
*/

DELETE FROM projet WHERE id = 18;

COMMIT; -- Validation de la transaction


✝ ✆

2.11.3 Ajouter des données


Ajouter des valeurs explicites

INSERT INTO public.utilisateur -- INSERT INTO <schéma>.<table>
(
id, nom, prenom -- <colonnes dont vous allez donner les valeurs>
)
VALUES
(
4, ’Dalton’, ’Joe’ -- <valeurs des colonnes listées (dans le même
ordre)>
);
✝ ✆
Je vous conseille de garder une symétrie entre la liste des colonnes et la liste des valeurs. Essayez aussi de
grouper les colonnes par paquet de 5.

Numérique et Sciences informatiques Terminale 49 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

La requête sera plus facile à lire et à vérifier :



INSERT INTO table (
col1, col2, col3, col4, col5,
col6, col7, col8, col9, col10,
col11, col12, col13, col14, col15
) VALUES (
val1, val2, val3, val4, val5,
val6, val7, val8, val9, val10,
val11, val12, val13, val14, val15
);
✝ ✆
Il est possible d’ajouter plusieurs lignes dans une même requête, en séparant les ensembles de valeurs par une
virgule :

INSERT INTO public.utilisateur
(id, nom, prenom)
VALUES
(4, ’Dalton’, ’Joe’), -- ligne 1
(5, ’Dalton’, ’William’), -- ligne 2
(6, ’Dalton’, ’Jack’), -- ligne 3
(7, ’Dalton’, ’Averell’) -- ligne 4
;
✝ ✆

Les valeurs par défaut


Les colonnes non listées dans votre requête d’INSERT prendront :
leur valeur par défaut telle que précisée dans la requête de création de la table
si aucune valeur par défaut n’a été précisée, elles prendront la valeur NULL. Si la colonne n’accepte pas les
NULL, une erreur sera relevée et aucune ligne ne sera ajoutée à la table.
Prenons la table tache suivante :

CREATE TABLE tache (
id INTEGER NOT NULL DEFAULT nextval(’tache_id_seq’),
libelle VARCHAR(100) NOT NULL,
priorite INTEGER NOT NULL DEFAULT 1,
realisateur_id INTEGER,
CONSTRAINT tache_pk PRIMARY KEY (id)
);
INSERT INTO tache
(libelle)
VALUES
✝ ✆
(’Corriger le bug n°6’) ;
Si la valeur de la séquence était 14, la requête précédente va ajouter la ligne :
id libelle priorite realisateur_id
15 Corriger le bug n°6 1 NULL
Pour récupérer la valeur de la séquence qui a été utilisée lors de l’ajout de la ligne :

SELECT currval(’tache_id_seq’);
-- Renvoie : 15
✝ ✆
Si vous utilisez la requête dans une transaction, la valeur de la séquence passera à 15 même si vous annulez
(ROLLBACK) la transaction. Les séquences ne sont pas « sensibles » aux transactions.

Les clés étrangères


Lorsqu’une colonne est clé étrangère, l’ajout de ligne se fait comme pour les autres colonnes. Cependant, vous
devez vous assurer que la valeur que vous donnez existe bien dans la table de référence.
Avec la table utilisateur suivante :

Numérique et Sciences informatiques Terminale 50 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

id nom prenom
4 Dalton Joe
5 Dalton William
6 Dalton Jack
7 Dalton Averell
Vous pourrez utiliser les valeurs 4, 5, 6 et 7 dans la colonne responsable_id de la table projet mais aucune autre
valeur.

Ajouter le résultat d’une requête de sélection


Dans une requête d’INSERT, il est aussi possible d’utiliser une requête SELECT comme source au lieu de
fournir explicitement des valeurs.
Imaginez que vous vouliez copier un projet par exemple, vous pouvez le faire facilement ainsi :

INSERT INTO projet (
nom, date_creation, cloture, responsable_id
)
SELECT
nom, date_creation, cloture, responsable_id
FROM projet
WHERE id = 18;
✝ ✆
Vous pouvez aussi fixer certaines valeurs en les intégrant à votre requête de sélection. Voici, par exemple,
comment imposer la valeur de la colonne cloture :

INSERT INTO projet (
nom, date_creation, cloture, responsable_id
)
SELECT
nom, date_creation, FALSE, responsable_id
FROM projet
WHERE id = 18;
✝ ✆
Vous pouvez aussi utiliser des fonctions et des opérateurs dans la requête de SELECT. Cette souplesse permet
de construire des requêtes INSERT parfaitement adaptées à votre besoin :

INSERT INTO projet (
nom,
date_creation, cloture, responsable_id
)
SELECT
projet.nom || ’ Perso ’ || utilisateur.prenom,
CURRENT_TIMESTAMP, FALSE, projet.responsable_id
FROM
projet
JOIN utilisateur ON utilisateur.id = projet.responsable_id
WHERE projet.id IN (18, 42);
✝ ✆
Remarquez encore la symétrie entre la liste des colonnes de l’INSERT et celle du SELECT.
Ici, je copie les projets 18 et 42 :
en ajoutant « Perso » et le nom du responsable après le nom du projet ;
en mettant la date et l’heure actuelle dans la colonnedate_creation ;
en fixant la valeur de la colonne cloture à FALSE.
Ce qui va donner la table projet suivante :

Numérique et Sciences informatiques Terminale 51 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

id nom date_creation cloture responsable_id


18 Projet X 2016-31-12 08 :30 :00.000 TRUE 4
20 Projet Y 2017-01-06 16 :30 :00.000 TRUE 2
42 Projet Z 2016-02-21 14 :45 :00.000 FALSE 6
...
61 Projet X Perso Joe 2017-03-31 16 :15 :00.000 FALSE 4
62 Projet Z Perso Jack 2017-03-31 16 :15 :00.000 FALSE 6

2.12 Interroger la base de données


Il est possible ensuite d’interroger la base de données. on peut rechercher dans une table selon plusieurs critères,
mais on peut aussi recouper des informations disponibles dans plusieurs tables et faire des calculs !
La documentation de PostgreSQL concernant les requêtes de type SELECT est disponible ici : http://
docs.postgresql.fr/9.6/sql-select.html

2.12.1 Rechercher dans la table


Afficher l’id et le nom de tous les projets.

SELECT
id, nom -- <liste des colonnes à afficher>
FROM
public.projet -- <schéma>.<table>
;
✝ ✆
Quelques explications sur la construction de la requête :

SELECT
✝ ✆
suivi de la liste des informations à afficher (liste des colonnes), puis

FROM
✝ ✆
la table contenant les données.
Allégeons un peu l’écriture :
Si vous voulez afficher toutes les colonnes, vous pouvez utiliser * au lieu de lister l’ensemble des colonnes
Si la table est dans le schéma par défaut (public dans PostgreSQL) vous pouvez omettre le nom du schéma
Afficher le contenu complet de la table utilisateur

SELECT * FROM utilisateur;
✝ ✆

2.12.2 Gerer les doublons


Les lignes en doublons sont remontées.
Si vous avez la table utilisateur suivante :
id nom prenom
3 Bracame Édouard
4 Dalton Joe
5 Dalton William
6 Dalton Jack
7 Dalton Averell
La requête suivante remontera 5 lignes :

SELECT nom FROM utilisateur;
✝ ✆

Numérique et Sciences informatiques Terminale 52 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

nom
Bracame
Dalton
Dalton
Dalton
Dalton
Vous pouvez supprimer les lignes en doublon grâce au mot clé DISTINCT :

SELECT DISTINCT nom FROM utilisateur;
✝ ✆
Cette requête ne va plus retourner que 2 lignes :
nom
Bracame
Dalton

2.12.3 Ajouter des critères de recherche


Ajoutons maintenant quelques critères pour filtrer notre sélection. On appelle aussi cela des conditions.
Afficher tous les utilisateurs portant le nom de « Dalton ».

SELECT *
FROM utilisateur
WHERE nom = ’Dalton’;
✝ ✆
Les critères sont précédés par le mot clé WHERE placé après le nom de la table. Il est possible de combiner
plusieurs critères grâce aux mots clés AND (ET) et OR (OU). Vous pouvez inverser un critère avec le mot clé
NOT et ajouter des parenthèses si besoin.
Afficher la liste des projets :
non gérés par Édouard Bracame,
ET :
clôturés
OU créés entre le 10 et le 30 janvier 2017 (inclus)

SELECT * FROM projet
WHERE
(NOT responsable_id = 3)
AND (cloture = TRUE
OR (date_creation >= ’2017-01-10’
AND date_creation < ’2017-01-31’)
)
;
✝ ✆
Dans la requête précédente, j’ai mis date_creation strictement inférieure au 31/01/2017 car il faut prendre
en compte les heures. Si j’avais mis date_creation <= ’2017-01-30’, j’aurais eu comme borne supérieure le
30/01/2017 0h00 et donc un projet créé le 30/01/2017 à 13h00 ne serait pas remonté !
Opérateurs de conditions Voici les principaux opérateurs permettant d’écrire des conditions :
Opérateurs Définition
= égal à
<> ou != différent de
<, > (strictement) inférieur à, supérieur à
<=, >= inférieur ou égal à, supérieur ou égal à
IS NULL est NULL
IS NOT NULL n’est pas NULL
LIKE correspond au motif*
NOT LIKE ne correspond pas au motif*
IN dans la liste de valeurs
NOT IN n’est pas dans la liste de valeurs

Numérique et Sciences informatiques Terminale 53 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Opérateur LIKE
L’opérateur LIKE (et NOT LIKE) permet de faire une recherche par motif dans un texte :
_ correspond à 1 caractère
% correspond à une chaîne de 0 à plusieurs caractères
Voici le résultat des conditions de sélection avec les noms d’utilisateurs Bracame et Dalton :
SELECT ... WHERE Résultat
nom LIKE ’Dal%’ Dalton
nom LIKE ’dal%’
nom LIKE ’%a%’ Bracame, Dalton
nom LIKE ’_a%’ Dalton
nom LIKE ’a’
nom LIKE ’Bracame’ Bracame
Opérateur IN
Les opérateurs IN et NOT IN permettent de vérifier si la valeur est présente ou non dans une liste :

-- Afficher les informations des projets 18 et 42
SELECT * FROM projet
WHERE id IN (18, 42);

-- Afficher les informations des projets autres que 18 et 42


SELECT * FROM projet
WHERE id NOT IN (18, 42);
✝ ✆

Faire des opérations et appliquer des fonctions


Il existe des fonctions et des opérateurs standards pour réaliser certaines actions, en voici quelques exemples :
+ realise une somme ; chaîne || chaîne réalise ne concaténation de chaîne ; lower(chaîne) converti le contenu de
chaîne en minuscule ...
Il est possible de faire ces opérations aussi bien dans la liste des colonnes à afficher que dans les conditions :

SELECT
nom || prenom
FROM utilisateur
WHERE lower(nom) LIKE ‘dal%‘;
✝ ✆
Vous n’êtes pas obligés de retenir toutes les opérations/fonctions ! Je vous invite à regarder la documentation de
PostgreSQL pour voir rapidement ce qui existe, et à y revenir quand vous en aurez besoin : http://docs.
postgresql.fr/9.6/functions.html

Ordonner des valeurs


Il est possible d’ordonner les lignes de résultat à l’aide de la clause ORDER BY. Cette clause est à placer après
les critères de recherche.
Afficher la liste des utilisateurs dont le nom contient un a triée :
par nom croissant
puis par prénom décroissant

SELECT * FROM utilisateur
WHERE lower(nom) LIKE ’%a%’
ORDER BY nom ASC, prenom DESC;
✝ ✆

Suivez les clés étrangères dans vos critères de recherche


Vous pouvez définir des critères de recherche en croisant les valeurs avec celles contenues dans une autre table.
Pour cela vous allez faire ce qu’on appelle une sous-requête.
Celle-ci sera mise entre parenthèses et vous utiliserez les opérateurs IN ou EXISTS.

Numérique et Sciences informatiques Terminale 54 sur 105 Sandrine Piquard


CHAPITRE 2. BASES DE DONNÉES

Afficher la liste des projets gérés par les utilisateurs dont le nom est « Dalton ».
Voici comment faire avec l’opérateur IN :

-- le croisement se fait sur projet.responsable_id = utilisateur.id

SELECT * FROM projet


WHERE
responsable_id IN ( -- le croisement se fait
ici
SELECT utilisateur.id FROM utilisateur -- et là
WHERE utilisateur.nom = ’Dalton’
)
;
✝ ✆
Voici comment faire avec l’opérateur EXISTS :

-- le croisement se fait sur projet.responsable_id = utilisateur.id

SELECT * FROM projet


WHERE EXISTS (
SELECT 1 FROM utilisateur
WHERE utilisateur.id = projet.responsable_id -- le croisement se fait
ici
AND utilisateur.nom = ’Dalton’
)
;
✝ ✆
Afficher la liste des tickets qui ne sont pas de bugs.

SELECT * FROM ticket
WHERE
numero NOT IN ( -- j’utilise ici NOT IN
SELECT ticket_numero FROM bug
)
;
✝ ✆
Afficher la liste des projets n’ayant pas de tickets associés.

SELECT * FROM projet
WHERE NOT EXISTS ( -- j’utilise ici NOT EXISTS
SELECT 1 FROM ticket
WHERE ticket.projet_id = projet.id
)
;
✝ ✆

Fusionnez le contenu de plusieurs tables


Pour fusionner le contenu de plusieurs tables, il vous faut faire des jointures.
INNER JOIN Afficher la liste des noms de projets gérés par les « Dalton » avec les informations du responsable
associé.

SELECT projet.nom AS "Nom du projet", utilisateur.*
FROM projet
INNER JOIN utilisateur ON utilisateur.id = projet.responsable_id
WHERE utilisateur.nom = ’Dalton’;
✝ ✆
La clause INNER JOIN va réaliser un produit cartésien des tables. C’est-à-dire qu’à chaque ligne de la table
projet sera associée toutes les lignes de la table utilisateur.
La clause ON va permettre de ne conserver que les lignes qui remplissent les conditions définies après ON.
Ici, on ne conserve que les associations de lignes où la valeur de projet.responsable_id correspond à celle de
utilisateur.id.
♣ REPENSER LA STRUCTURE DANS LE BUT D ’ UNE ACTIVITÉ POUR LES ÉLÈVES

Numérique et Sciences informatiques Terminale 55 sur 105 Sandrine Piquard


Chapitre 3 Base de données avec Python

Maintenant que nous avons vu comment fonctionnent les requêtes SQL, nous allons travailler sur les re-
quêtes SQL effectuées depuis un programme Python (Sur les raspberry). Sources : https://pixees.fr/
informatiquelycee/n_site/nsi_term_projet_1.html

3.1 À faire vous-même 1


Après avoir créé un répertoire "projet_bd" dans votre espace de travail, créez, à l’aide de l’éditeur de texte, un
fichier Python (à vous de choisir le nom) puis saisissez et exécutez le programme suivant :

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

cur.execute("CREATE TABLE LIVRES(id INT, titre TEXT, auteur TXT, ann_publi INT,
note INT)")

conn.commit()

cur.close()
conn.close()
✝ ✆
Analysons le programme ci-dessus :
Ce programme va vous permettre de vous "connecter" à une base de données (si cette dernière n’existe pas, elle
sera créée). Ensuite nous créons une table (une relation) nommée LIVRES, cette table contient 4 attributs : id
(de type entier), titre (de type texte), auteur (de type texte), ann_publi (de type entier) et note (de type entier).
Entrons un peu dans les détails en analysant le programme ligne par ligne :

import sqlite3
✝ ✆
Nous commençons par importer la bibliothèque sqlite3. Cette bibliothèque va nous permettre d’effectuer des
requêtes SQL sur une base de données. Comme dans le cours sur les bases de données, nous utiliserons le
SGBD SQLite.

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()
✝ ✆
Nous créons un objet de type "connection" (conn) qui va nous permettre d’interagir avec la base de données
"baseDonnees.db" (comme dit plus haut, si cette base de données n’existe pas, elle est créée). Vous devriez
donc avoir un fichier "baseDonnees.db" dans le même répertoire que votre fichier Python.
Nous créons ensuite un objet de type "cursor" à partir de l’objet de type "connection". Cet objet de type "cursor"
va nous permettre de manipuler la base de données et d’obtenir des résultats lorsque nous effectuerons des
requêtes.

cur.execute("CREATE TABLE LIVRES(id INT, titre TEXT, auteur TXT, ann_publi INT,
note INT)")
✝ ✆
La méthode "execute" de notre objet de type "cursor" nous permet d’effectuer une requête SQL. Cette requête
SQL est en tout point identique à ce que nous avons vu dans le cours sur les bases de données.

conn.commit()
✝ ✆
Pour véritablement exécuter les requêtes, il est nécessaire d’appliquer la méthode "commit" à l’objet de type
"connection".

56
CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON


cur.close()
conn.close()
✝ ✆
Avant de terminer le programme, il nécessaire de "fermer" l’objet de type "cursor" et l’objet de type "connec-
tion".
Nous allons systématiquement retrouver cette structure dans nos futurs programmes :
❧ création d’un objet de type "connection"
❧ création d’un objet de type "cursor"
❧ préparation d’une ou plusieurs requête(s) (méthode "execute" sur l’objet de type "cursor")
❧ exécution réelle des requêtes (méthode "commit" sur l’objet de type "connection")
❧ "fermeture" de l’objet de type "cursor" puis de l’objet de type "connection"
Si vous exécutez une deuxième fois le programme proposé au "À faire vous-même 1", vous aurez droit à une
erreur : "OperationalError : table LIVRES already exists". Afin d’éviter ce genre de problème, il est possible de
modifier le programme afin que la requête de création de la table LIVRES ne se fasse pas si la table LIVRES
existe déjà :

3.2 À faire vous-même 2


Après avoir effacé le fichier "baseDonnees.db", saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

cur.execute("CREATE TABLE IF NOT EXISTS LIVRES(id INT, titre TEXT, auteur TXT,
ann_publi INT, note INT)")

conn.commit()

cur.close()
conn.close()
✝ ✆
Comme vous pouvez le constater, si vous exécutez le programme plusieurs fois de suite, il n’y a plus d’erreur.

3.3 À faire vous-même 3


Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que la table
LIVRES a bien été créée.
Maintenant que notre table LIVRES a été créée, nous allons pouvoir commencer à la "remplir" avec des don-
nées :

3.4 À faire vous-même 4


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

cur.execute("CREATE TABLE IF NOT EXISTS LIVRES(id INT, titre TEXT, auteur TXT,
ann_publi INT, note INT)")

Numérique et Sciences informatiques Terminale 57 sur 105 Sandrine Piquard


CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON

cur.execute("INSERT INTO LIVRES(id,titre,auteur,ann_publi,note) VALUES


(1,’1984’,’Orwell’,1949,10)")

conn.commit()

cur.close()
conn.close()
✝ ✆
Rien de particulier à signaler, la requête INSERT est identique à ce qui a été vu dans le cours sur les bases de
données.

3.5 À faire vous-même 5


Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que les données
ont bien été ajoutées à la table LIVRES.
Nous avons inclus les données à insérer directement dans la requête. Il est possible de procéder autrement en
séparant les données à insérer et la requête (cela s’avérera particulièrement pratique dans le futur)

3.6 À faire vous-même 6


Après avoir effacé le fichier "baseDonnees.db", saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

data = (1,’1984’,’Orwell’,1949,10)

cur.execute("CREATE TABLE IF NOT EXISTS LIVRES(id INT, titre TEXT, auteur TXT,
ann_publi INT, note INT)")
cur.execute("INSERT INTO LIVRES(id,titre,auteur,ann_publi,note) VALUES(?, ?, ?,
?, ?)", data)
conn.commit()

cur.close()
conn.close()
✝ ✆
Première chose à remarquer, nous avons créé un tuple (data) contenant toutes les informations. En effet, la
méthode "execute" peut prendre un deuxième paramètre un tuple contenant les données à insérer. Les points
d’interrogation présents dans la requête indiquent l’emplacement des données à insérer. Le premier ? sera rem-
placé par le premier élément du tuple (dans notre cas 1), le deuxième ? sera remplacé par le deuxième élément
du tuple (dans notre cas ’1984’) et ainsi de suite...
Si l’on désire insérer plusieurs données, il est possible de regrouper toutes les données à insérer dans un tableau
et d’utiliser la méthode "executemany" à la place de la méthode "execute".

3.7 À faire vous-même 7


Après avoir effacé le fichier "baseDonnees.db", saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

datas = [
(1,’1984’,’Orwell’,1949,10),

Numérique et Sciences informatiques Terminale 58 sur 105 Sandrine Piquard


CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON

(2,’Dune’,’Herbert’,1965,8),
(3,’Fondation’,’Asimov’,1951,9),
(4,’Le meilleur des mondes’,’Huxley’,1931,7),
(5,’Fahrenheit 451’,’Bradbury’,1953,7),
(6,’Ubik’,’K.Dick’,1969,9),
(7,’Chroniques martiennes’,’Bradbury’,1950,8),
(8,’La nuit des temps’,’Barjavel’,1968,7),
(9,’Blade Runner’,’K.Dick’,1968,8),
(10,’Les Robots’,’Asimov’,1950,9),
(11,’La Planète des singes’,’Boulle’,1963,8),
(12,’Ravage’,’Barjavel’,1943,8),
(13,’Le Maître du Haut Château’,’K.Dick’,1962,8),
(14,’Le monde des A’,’Van Vogt’,1945,7),
(15,’La Fin de l éternité’,’Asimov’,1955,8),
(16,’De la Terre à la Lune’,’Verne’,1865,10)
]

cur.execute("CREATE TABLE IF NOT EXISTS LIVRES(id INT, titre TEXT, auteur TXT,
ann_publi INT, note INT)")
cur.executemany("INSERT INTO LIVRES(id,titre,auteur,ann_publi,note) VALUES(?,
?, ?, ?, ?)", datas)
conn.commit()

cur.close()
conn.close()
✝ ✆

3.8 À faire vous-même 8


Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que les données
ont bien été ajoutées à la table LIVRES.
Il n’est pas très pratique d’avoir à gérer l’id (clé primaire). En effet, si je veux ajouter un nouveau livre, il faudra
que je connaisse l’id du précédent (incrémentation de l’id). Heureusement, il est possible d’automatiser cette
incrémentation.

3.9 À faire vous-même 9


Après avoir effacé le fichier "baseDonnees.db", saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

datas = [
(’1984’,’Orwell’,1949,10),
(’Dune’,’Herbert’,1965,8),
(’Fondation’,’Asimov’,1951,9),
(’Le meilleur des mondes’,’Huxley’,1931,7),
(’Fahrenheit 451’,’Bradbury’,1953,7),
(’Ubik’,’K.Dick’,1969,9),
(’Chroniques martiennes’,’Bradbury’,1950,8),
(’La nuit des temps’,’Barjavel’,1968,7),
(’Blade Runner’,’K.Dick’,1968,8),
(’Les Robots’,’Asimov’,1950,9),
(’La Planète des singes’,’Boulle’,1963,8),
(’Ravage’,’Barjavel’,1943,8),
(’Le Maître du Haut Château’,’K.Dick’,1962,8),
(’Le monde des A’,’Van Vogt’,1945,7),

Numérique et Sciences informatiques Terminale 59 sur 105 Sandrine Piquard


CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON

(’La Fin de l éternité’,’Asimov’,1955,8),


(’De la Terre à la Lune’,’Verne’,1865,10)
]

cur.execute("CREATE TABLE IF NOT EXISTS LIVRES(id INTEGER PRIMARY KEY


AUTOINCREMENT UNIQUE, titre TEXT, auteur TXT, ann_publi INT, note INT)")
cur.executemany("INSERT INTO LIVRES(titre,auteur,ann_publi,note) VALUES(?, ?,
?, ?)", datas)
conn.commit()

cur.close()
conn.close()
✝ ✆
Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que les données
ont bien été ajoutées à la table LIVRES.
Vous pouvez constater que nous avons bien l’attribut "id", même si ce dernier n’a pas été renseigné dans les
données (absence d’id dans le tableau datas). Désormais l’id sera incrémenté automatiquement grâce au "id
INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE" (attention il est nécessaire d’utiliser INTEGER
à la place du INT habituel) présent dans la requête de création de la table LIVRES. Attention, de bien penser
à supprimer un ? dans la requête d’insertion (chaque tuple contient maintenant 4 éléments (nous en avions 5
quand l’id n’était pas géré automatiquement)).
Il est tout à fait possible de rajouter une nouvelle donnée :

3.10 À faire vous-même 10


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()
nvx_data = (’Hypérion’,’Simmons’,1989,8)

cur.execute("INSERT INTO LIVRES(titre,auteur,ann_publi,note) VALUES(?, ?, ?, ?)


", nvx_data)
conn.commit()

cur.close()
conn.close()
✝ ✆
Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que les données
ont bien été ajoutées à la table LIVRES.
Vous pouvez remarquer que le nouvel enregistrement a bien l’id 17 et que nous n’avons pas eu à nous en
occuper.
Il est possible de modifier des données déjà présentes dans la table.

3.11 À faire vous-même 11


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

modif = (7, ’Hypérion’)


cur.execute(’UPDATE LIVRES SET note = ? WHERE titre = ?’, modif)
conn.commit()

Numérique et Sciences informatiques Terminale 60 sur 105 Sandrine Piquard


CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON

cur.close()
conn.close()
✝ ✆
Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que les données
ont bien été modifiées dans la table LIVRES.
Comme vous pouvez le constater, il est possible d’utiliser la clause WHERE avec un ?
Il est aussi possible de supprimer une donnée :

3.12 À faire vous-même 12


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

suppr = (’Hypérion’,)
cur.execute(’DELETE FROM LIVRES WHERE titre = ?’, suppr)
conn.commit()

cur.close()
conn.close()
✝ ✆
Ouvrez le fichier "baseDonnees.db" à l’aide du logiciel "DB Browser for SQLite" et vérifiez que l’entrée "Hy-
périon" a été supprimée de la table LIVRES.
Attention, le deuxième paramètre de la méthode "execute" doit être un tuple, si on écrit seulement suppr =
(’Hypérion’), suppr est une chaine de caractère et pas un tuple. Pour avoir un tuple avec un seul élément il est
nécessaire d’ajouter une virgule (d’où le suppr = (’Hypérion’,))
Enfin, pour terminer cette introduction sur l’utilisation de sqlite en Python, nous devons nous intéresser aux
requêtes de type "SELECT" :

3.13 À faire vous-même 13


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

cur.execute(’SELECT * FROM LIVRES’)


conn.commit()

liste = cur.fetchall()

cur.close()
conn.close()
✝ ✆
À l’aide de la console, déterminez la valeur référencée par la variable liste
Comme vous pouvez le constater, la variable liste est un tableau qui contient des tuples. Chaque tuple est
un enregistrement de la table LIVRES. La méthode "fetchall" d’un objet de type "cursor" renvoie un tableau
contenant des tuples
Il est possible d’avoir des requêtes plus sélectives :

Numérique et Sciences informatiques Terminale 61 sur 105 Sandrine Piquard


CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON

3.14 À faire vous-même 14


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

cur.execute(’SELECT titre FROM LIVRES WHERE ann_publi < 1970’)


conn.commit()

liste = cur.fetchall()

cur.close()
conn.close()
✝ ✆
À l’aide de la console, déterminez la valeur référencée par la variable liste
Vous pouvez constater que l’on obtient bien un tableau contenant des tuples (nous avons bien des tuples même
si seuls les titres ont été sélectionnés)
Il est possible d’utiliser les points d’interrogation dans une requête de type SELECT :

3.15 À faire vous-même 15


Saisissez le programme ci-dessous, puis exécutez-le

import sqlite3

conn = sqlite3.connect(’baseDonnees.db’)
cur = conn.cursor()

recherche = (1960, 8)

cur.execute(’SELECT titre FROM LIVRES WHERE ann_publi < ? AND note > ?’,
recherche)
conn.commit()

liste = cur.fetchall()

cur.close()
conn.close()
✝ ✆
À l’aide de la console, déterminez la valeur référencée par la variable liste

3.16 Projets
3.16.1 Installer flask s’il n’est pas présent
sudo apt-get update
sudo apt-get install python3-pip python3-dev nginx
sudo pip3 install virtualenv
mkdir /flaskproject
cd /flaskproject
virtualenv flaskprojectenv
This will install a local copy of Python and pip into a directory called flaskprojectenv within your project
directory. Before we install applications within the virtual environment, we need to activate it. You can do so
by typing :

Numérique et Sciences informatiques Terminale 62 sur 105 Sandrine Piquard


CHAPITRE 3. BASE DE DONNÉES AVEC PYTHON

source flaskprojectenv/bin/activate
Your prompt will change to indicate that you are now operating within the virtual environment. It will look
something like this (flaskprojectenv)user@host : /flaskproject$.
mouspad flaskproject.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def greeting():
return "<h1 style=’color:green’>Hello World!</h1>"

if __name__ == "__main__":
app.run(host=’0.0.0.0’)
✝ ✆
sudo ufw allow 5000
python3 flaskproject.py

3.16.2 Projet
A l’aide de tout ces outils, créer une page web qui interagisse avec votre base de données.

Numérique et Sciences informatiques Terminale 63 sur 105 Sandrine Piquard


Chapitre 4 Architectures matérielles,
systèmes d’exploitation et réseaux

En 1975, Gordon E. Moore (cofondateur de la société Intel) énonça la conjecture suivante sur l’évolution des
capacités des circuits intégrés, appelés familièrement puces électroniques :
Dans les microprocesseurs, le nombres de transistors sur une puce va doubler tout les 2 ans.

Bien que fondée sur un constat empirique de l’industrie des fabricants de circuits entre les années 1965 et
1975, cette prédiction, qu’on appelle aussi loi de Moore, s’est révélée incroyablement juste. On est ainsi passé
de 2250 transistors en 1971 sur un microprocesseur Intel 4004 à plusieurs dizaines de milliards aujourd’hui
sur les derniers microprocesseurs, où la taille des transistors n’est que de 7 nanomètres, soit à peine plus que
l’épaisseur de quelques dizaines d’atomes de silicium.
Cette miniaturisation des tous les composants a permis une augmentation de la puissance de calcul des ordina-
teurs, une baisse des coûts, une diminution de leur tension de fonctionnement et donc de leur consommation
énergétique à puissance de calcul équivalente. On peut aujourd’hui rassembler sur une même puce tous les
systèmes embarqués (microprocesseur, mémoire, interfaces d’entrées-sorties, etc...) sur un microcontrôleur.

4.1 Différentes architectures


4.1.1 Microcontrôleurs
Les microcontrôleurs sont des circuits intégrés qui regroupent sur une même puce un microprocesseur, la mé-
moire, les ports d’entrées-sorties, les périphériques et les bus de communication.
La fréquence d’horloge (nombre d’instructions exécutées par seconde) sont de quelques dizaine de Mhz.
Ce sont souvent des composants autonomes, qui dès qu’ils sont sous tension, exécutent le programme contenu
dans leur mémoire. La principale différence entre un microcontrôleur et celle d’un ordinateur basée sur l’archi-
tecture de Von Neumann (cours de première) est que la mémoire qui contient les programmes n’est pas la même
que celle qui contient les données. La mémoire programme est une mémoire morte, c’est à dire qui ne perd pas
l’information qu’elle contient quand le microcontrôleur n’est plus alimenté en électricité (ROM, EEPROM,
FLASH...).
La mémoire des données est une mémoire vive (RAM), on peut modifier les données, mais elles sont perdues
quand on coupe l’alimentation.
Certains microcontrôleurs profitent de la séparation de la mémoire de programme et de données pour implémen-
ter une architecture de Harvard (Figure 4.1). Dans cette architecture, on accède au programme et aux données
par l’intermédiaire de deux bus distincts ce qui permet de gagner du temps en transférant simultanément les
instructions et les données, et permet d’avoir des mots mémoire de tailles différentes : 16 bits pour l’un et 8 bits
pour l’autre, par exemple, ce qui est intéressant dans un système embarqué).

F IGURE 4.1 – Architecture de Harvard

64
CHAPITRE 4. ARCHITECTURES MATÉRIELLES, SYSTÈMES D’EXPLOITATION ET RÉSEAUX

4.1.2 Microcontrôleur PIC


Les microcontrôleurs PIC sont fabriqués par la société Microchip. Ils s’appuient sur l’architecture de Harvard
avec un jeu d’instructions réduit : RISC (Reduced Instruction Set Computer).
Voir exemple du PIC 18F (Figure 4.2).

F IGURE 4.2 – Architecture des microcontrôleurs de la famille PIC 18F.

4.1.3 Système sur puce


Un système sur puce (Système on Chip : SoC) rassemble sur un même circuit intégré tous les composants habi-
tuellement présents sur la carte mère d’un ordinateur. Les caractéristiques d’un SoC sont donc très proches de
celles d’un ordinateur. (Figure 4.3) Ils ont une puissance de calcul comparable qui repose sur des microproces-
seurs de dernière génération avec de nombreux cœurs cadencés à plusieurs gigahertz, ainsi que des processeurs
dédiés (graphique, sécurité...). Leur capacité mémoire se mesure en gigaoctets et ils incluent des mémoires
RAM et FLASH. Enfin ils contiennent de nombreux périphériques ; tout cela sur une puce d’environ 100 mm2 .

F IGURE 4.3 – Architecture d’un System on Chip

4.2 Composants intégrés d’un système sur puce


En classe on visualisera les Raspberry, Micro : :bit, Makeblock.
https://pixees.fr/informatiquelycee/n_site/nsi_term_archi_soc.html

Numérique et Sciences informatiques Terminale 65 sur 105 Sandrine Piquard


CHAPITRE 4. ARCHITECTURES MATÉRIELLES, SYSTÈMES D’EXPLOITATION ET RÉSEAUX

4.3 Gestion des processus et des ressources par l’OS


En classe : à faire avec les Raspberry. https://pixees.fr/informatiquelycee/n_site/nsi_
term_archi_proc.html

4.4 Protocoles de routage


En autonomie à la maison.
A l’aide du logiciel Filius, réaliser pas à pas l’activité suivante :
https://pixees.fr/informatiquelycee/n_site/nsi_term_archi_routage.html
en faisant abstraction des algorithmes sur les graphes que nous verrons plus tard.
Au besoin s’aider de l’activité de première proposée sur le même site.
Rétroaction à la fin de l’activité :

Identifier, suivant le protocole de routage utilisé, la route empruntée par un paquet.


Expliquer le fonctionnement du routage dans un réseau, que faut-il paramétrer et comment pour que le paquet
circule.

4.5 Sécurisation des communications


En autonomie à la maison.
Réaliser l’activité ci-dessous sauf le « A faire vous-même 1 »
https://pixees.fr/informatiquelycee/n_site/nsi_term_archi_secu.html

4.5.1 À faire vous-même 1


Réaliser un programme python qui :
1. demande un mot de chiffrement ;
2. demande un message ;
3. chiffre le message et affiche le message codé ; par exemple en utilisant le code césar, et les méthodes
python chr et ord(), ou en faisant un XOR bit à bit (après une petite recherche sur la manipulation du
binaire en python.
4. déchiffre le message et affiche le message décodé.
Envoyer votre code python par Ecole Directe.

Numérique et Sciences informatiques Terminale 66 sur 105 Sandrine Piquard


Chapitre 5 Langages et programmation

On peut considérer Alan Turing (1912-1954) comme le père de l’informatique, bien qu’il ait été secondé par
d’autres. Il s’est penché en particulier sur le problème de la calculabilité et a fait le lien avec celui de la
décidabilité en arithmétique, problème posé initialement par le mathématicien David Hilbert en 1928.
En 1936, il présente la machine de Turing, une expérience de pensée qui permet de préciser la notion de
procédé calculable, et à partir de cette notion de définir clairement ce qu’il appelle un programme.
Il démontre l’indécidabilité du problème de l’arrêt en prouvant qu’on ne peut pas répondre à cette question
avec un algorithme. Sa démonstration est liée à la notion de calculabilité qu’il définit par ce qui peut se calculer
mécaniquement avec un algorithme.
Plus tard, Alan Turing participe à un débat sur l’intelligence artificielle et présente ce qu’on appelle le test de
Turing. L’objet central de l’informatique est le calcul. Un algorithme ne traite pas que des nombres, il est aussi
appliqué à toutes sortes d’objets.

Dans tous les cas, un algorithme, et en particulier un calcul sur des nombres, est une succession
de tâches simples appelées opérations, à exécuter dans un ordre précis et qui va produire un
résultat après un nombre fini d’étapes.

Un programme utilisable sur une machine est alors une description de l’algorithme dans un langage (de pro-
grammation) compréhensible par la machine.

Tout programme est aussi une donnée :

Un programme est écrit dans un fichier ce fichier peut être édité et lu. Le contenu peut être du texte,
plus ou moins compréhensible, le code source, qui est une suite d’instructions écrites dans un langage de
programmation. En Python ces instructions sont interprétées, elles sont traduites en des instructions en binaires,
en langage machine. En C, par exemple, le programme est d’abord compilé, un fichier exécutable est créé.

Un programme peut être exécuté seul, ou utilisé par un autre programme Il est alors considéré comme
une donnée par cet autre programme, qui le reçoit en argument.

5.1 Calculabilité, décidabilité


5.1.1 Calculabilité
Définition 11 : Un nombre est calculable s’il peut être obtenu par une machine de Turing.

On est bien avancé !

Définition 12 : Une multiplication peut se décomposer en une série d’additions. Une opération plus complexe
peut se décomposer en une série d’opérations moins complexes, et ainsi de suite jusqu’à obtenir des additions.
Or les additions sont calculables par une machine de Turing.

Tous les nombres sont-ils calculables ? La réponse est non.


√ √
Les nombres calculables sont par exemple les rationnels, 2, 5, π est calculable. Les nombres non calculables
sont ceux pour lesquels un algorithme ne parvient qu’à donner un nombre limité de décimales.

Définition 13 : fonctions calculables : une fonction f est calculable si on peut obtenir f (u) en un nombre fini
d’étapes pour tout u donné, donc avec une succession d’opérations simples, et que le programme termine.

On peut se demander si un problème peut être résolu par un algorithme. On ramène ce problème à une fonction,
la question est alors de savoir si cette fonction est calculable ou pas.

67
CHAPITRE 5. LANGAGES ET PROGRAMMATION

5.1.2 Décidabilité
La décidabilité est une notion utilisée en logique.

Définition 14 : Soit une proposition, il s’agit de démontrer qu’elle est vraie ou fausse. Si cette démonstration
est possible, on dit que la proposition est décidable. Sinon, on dit que la proposition est indécidable.

Examinons un exemple simple avec le programme ci-dessous ou n est un entier naturel.



def pair(n):
while n > 0:
n=n- 2
return n == 0
✝ ✆
Ce programme permet de déterminer si n est pair ou pas. La fonction renvoie si n est pair et
si n est impair.
Question : est-ce qu’il existe un programme qui prend en argument un entier naturel n quelconque et détermine
si cet entier naturel est pair ou pas ?
La réponse est oui, donc ce problème est décidable.

Exercice n°1 :
1. Ecrire un programme qui détermine pour n’importe quel entier si cet entier est premier ou pas.
2. Répondre à la question : est-ce qu’il existe un programme qui prend en argument un entier naturel n
quelconque et détermine si cet entier naturel est premier ou pas ?
3. Ce problème est-il décidable ?

5.1.3 L’arrêt d’un programme est indécidable


Peut-on écrire un programme dont le rôle est d’analyser un programme quelconque et déterminer si ce pro-
gramme termine dans tous les cas, quelles que soient les données fournies en entrée ? Et ce programme doit
permettre de répondre à la question en un temps fini.
Si un programme entre dans une boucle sans fin, on dit qu’il ne termine pas, et l’algorithme n’est pas valide.
Soit le programme suivant :

def machine(prog):
if termine (prog, prog):
print("termine")
while True:
print("boucle")
else:
print("boucle indéfiniment")

def termine(prog, data):


return prog is data
✝ ✆
L’expression termine(machine, machine) vaut True , autrement dit le programme termine, et pour-
tant machine(machine) boucle indéfiniment. (Appuyez sur Ctrl+C pour l’arrêter)
Modifions la fonction termine :

def termine(prog, data):
return prog is not data
✝ ✆
L’expression termine(machine, machine) vaut False , autrement dit le programme boucle indéfini-
ment, et pourtant machine(machine) termine.
L’arrêt d’un programme est indécidable !

Numérique et Sciences informatiques Terminale 68 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

5.2 Récursivité
En classe de première, de nombreux programmes ont été écrits avec des boucles et des affectations.
La notion de fonction permet d’éviter de réécrire un jeu d’instructions qui revient plusieurs fois dans le pro-
gramme.
Le style des programmes est alors :
❧ impératif, les séquences d’instructions sont exécutées l’une après l’autre.
❧ itératif, des instructions sont exécutées dans des boucles while ou for.
Une fonction qui s’appelle elle-même va permettre de supprimer par exemple une boucle for. Que fait le
programme suivant :

for i in range(10):
print(i)
✝ ✆
Que vaut i à la sortie du programme ?
Considérons maintenant :

def affiche(k):
if k <10:
print(k)
affiche(k+1)

affiche(0)
✝ ✆
Décrire les étapes de ce programme :

Nous souhaitons effectuer un compte à rebours, par exemple de 5, 4, 3, 2, 1, 0 ; soit le programme suivant :

def rebours(n):
""" n est un entier naturel,
affiche les entiers de n à 0
"""
while n >= 0:
print(n)
n=n- 1
✝ ✆
Regardons maintenant celui-ci :

def rebours2(n):
if n >= 0:
print(n)
rebours2(n - 1) # récursivité terminale
✝ ✆
Décrire les étapes de ce programme, combien y a-t-il eu d’appel à la fonction rebours2 ? Combien y a-t-il
eu de print ?

Numérique et Sciences informatiques Terminale 69 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

Et celui là :

def rebours3(n):
if n >= 0:
rebours3(n - 1) # récursivité non terminale
print(n)
✝ ✆
Décrire les étapes de ce programme, combien y a-t-il eu d’appel à la fonction rebours3 ? Combien y a-t-il
eu de print et dans quel ordre ?

5.2.1 Principe de la récursivité

Définition 15 : Une fonction est dite récursive si elle s’appelle elle-même.

En règle générale on fait appel à une fonction récursive quand on sait résoudre le problème à petite échelle, et
qu’on veut le résoudre à grande échelle.
Exemple, on veut calculer la fonction factorielle(n) n!, qui est définie ainsi :
n
f ( n) = n ∗ ( n − 1) ∗ . . . ∗ 2 ∗ 1 = ∏ x
x= 1

et f (0) = 1.
Avec une boucle for on a :

def factorielle(n):
f=1
for i in range(2, n+1):
f=f*i
return f
✝ ✆
Avec une structure récursive :

def factorielle(n):
""" n est de type int positif
renvoie n!
"""
if n > 0:
return n * factorielle(n - 1) # récursivité profonde
else:
return 1
✝ ✆
Décrire les étapes de ce programme pour n=5, combien y a-t-il eu d’appel à la fonction factorielle ? A
quel moment est réalisé le calcul ?

Numérique et Sciences informatiques Terminale 70 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

principe généraux :

❧ Une fonction récursive doit contenir une ou des conditions d’arrêt. Sinon le programme
boucle indéfiniment.
❧ Les valeurs passées en paramètres dans les appels récursifs doivent être différentes. Si-
non le programme boucle indéfiniment.
❧ Après un nombre fini d’appels, la ou les valeurs passées en paramètres doivent permettre
de satisfaire la condition d’arrêt.

5.2.2 Ecrire un programme récursif


Exemple classique de la suite de fibonacci :
La suite de Fibonacci est définie par récurrence :
❧ f0 = 0, f1 = 1
❧ fn = fn−1 + fn−2 pour n > 1

Exercice n°2 :
1. Ecrire le programme qui calcule la valeur de la suite de Fibonacci pour n = 10 de manière itérative.
2. Ecrire le programme qui calcule la valeur de la suite de Fibonacci pour n = 10 de manière récursive.
3. Déssinez sous forme d’un arbre les différents appels de la fonction, Combien de fois calcule-t-on f(2) ?
Commentez.

5.2.3 Exercices
(simples puis progressif)

Exercice n°3 :
Les seules opérations autorisées sur les nombres entiers sont soit ajouter 1, soit retrancher 1. La fonction somme
renvoie la somme des entiers positifs a et b.

def somme(a,b):
while b > 0:
a=a+1
b=b- 1
return a
✝ ✆
Ecrire une version récursive de cette fonction.

Numérique et Sciences informatiques Terminale 71 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

Exercice n°4 :
En s’inspirant de l’exercice précédent, écrire une fonction qui renvoie la somme de deux entiers quelconques.
Les seules opérations autorisées sur les nombres entiers sont soit ajouter 1, soit retrancher 1.
1. Ecrire une fonction somme itérative avec une boucle while.
2. Ecrire une fonction somme_rec récursive non terminale.
3. Ecrire une fonction somme_rec_term récursive terminale.

Exercice n°5 :
On reprend la fonction du cours permettant de savoir si un entier naturel est pair ou non.

def pair(n):
while n > 0:
n=n- 2
return n == 0
✝ ✆
Ecrire une version récursive de cette fonction.

Exercice n°6 :
1. Ecrire une fonction récursive puissance qui prend en paramètres un flottant x non nul et un entier
naturel n et qui renvoie xn . On se base sur la définition mathématique : x0 = 1 et xn = x × xn−1 pour
n > 0.
2. Dans la première question, on se contente de traduire la définition, et la version obtenue n’est pas ré-
cursive terminale. Modifier le code pour que la fonction soit récursive terminale. On peut s’inspirer des
exemples du cours concernant la factorielle.

Exercice n°7 :
Ecrire une fonction récursive puissance qui prend en paramètres un flottant x non nul et un entier naturel
n et qui renvoie xn . Cette fois-ci on note que x0 = 1 et on remarque que si n = 2k, alors xn = x2k = (x2 )k et si
n = 2k + 1 alors xn = x2k+1 = x(x2 )k .
1. Ecrire une première version récursive non terminale.
2. Ecrire une version récursive terminale.
3. Ecrire une version itérative.

Exercice n°8 :
1. Ecrire une fonction récursive somme qui prend en paramètre une liste de nombres et renvoie la somme
des termes de cette liste.
2. Expliquer avec l’exemple somme([4, 7, 2]) le déroulement de l’exécution de cette fonction.( Des-
siner un arbre d’appel.)
3. Que penser du coût en temps et en espace d’une telle fonction ?

Numérique et Sciences informatiques Terminale 72 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

Exercice n°9 :
1. Expliquer quel est le résultat renvoyé par la fonction mystere.

def mystere(n):
if n < 2:
return str(n)
else:
return mystere(n//2) + str(n % 2)
✝ ✆
2. Ecrire une fonction récursive binaire qui prend en paramètres un entier relatif r et un entier n stricte-
ment positif, et qui renvoie la représentation en machine de r sur n bits.
3. Compléter le code avec un compteur passé en paramètre afin d’obtenir un résultat composé de n carac-
tères, donc avec éventuellement des 0 au début.

Exercice n°10 :
On programme le tri par insertion de manière récursive. (voir cours de première)
1. Ecrire une fonction récursive insertion qui prend trois paramètres : une liste, un élément de la liste
x et son indice n. Si n est strictement positif, on suppose que les éléments d’indice 0 à n-1 sont triés et
la fonction insère l’élément x à la bonne place.
2. Ecrire la fonction récursive tri_insertion qui prend en paramètres une liste et la longueur de la
liste et qui trie la liste.

Exercice n°11 :
La valeur du pgcd (plus grand commun diviseur) de deux entiers naturels a et b est calculable à l’aide de
l’algorithme d’Euclide : pgcd (a, b) = pgcd (b, r) ou r est le reste dans la division euclidienne de a par b. En
voici une version itérative :

def pgcd(a, b):
if b == 0:
return a
while b != 0:
a, b = b, a % b
return a
✝ ✆
Ecrire une version récursive de cette fonction.

Numérique et Sciences informatiques Terminale 73 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

5.3 Corrigés

def premier(n):
"""
un nombre est premier si il n’est divisible que par un ou par lui même
"""
i = n-1
while i > 1 and n % i !=0:
i=i- 1
if i > 1:
return False
else:
return True
✝ ✆

# avec une boucle while:
def fibo1(n):
u, v, cpt = 0 , 1 , 0
while cpt < n:
u, v, cpt = v, u+v, cpt + 1
return u

print("fibonacci(10) =", fibo1(10))

# avec une boucle for:


def fibo2(n):
u, v = 0, 1
for i in range(n):
u, v = v, u + v
return u

print("fibonacci(10) =", fibo2(10))

def fibo3(n):
"""
un fonction récursive doit toujours contenir une condition de sortie
"""
#print(n)
if n == 0 or n== 1:

return n
else:
if (n-1) == 2 or (n-2) == 2:
print(n)
return fibo3(n-1) + fibo3(n-2)

print("fibonacci(10) =", fibo3(10))

# f(2) est calculé 34 fois!


# La méthode récursive n’est pas obtimum pour calculer la suite de fibbonacci.
✝ ✆

def somme(a,b):
while b > 0:
a=a+1
b=b- 1
return a

print("itératif: ", somme(5,7))

def somme2(a,b):
if b == 0:

Numérique et Sciences informatiques Terminale 74 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

return a
else:
return 1 + somme2(a,b-1) # récursif terminal

print("récursif terminal: ", somme2(5,7))

def somme3(a,b):
if b == 0:
return a
else:
return somme3(a+1,b-1) # récursif non terminal

print("récursif non terminal: ", somme3(5,7))


✝ ✆

import sys
sys.setrecursionlimit(5000)

def pair(n):
while n > 0:
n=n- 2
return n == 0

def pair2(n):
if n <= 1:
return n == 0
else:
return pair2(n-2)

n= int(input("entrez un nombre "))


print(n, "est pair?: ", pair(n))

print(n, "est pair?: ", pair2(n))


✝ ✆

def puissance(x,n):
if x == 0:
print("x ne peut être nul")
return 0
if n==0:
return 1
else:
return x * puissance(x,n-1)

x=10

n=997
print(x, "puissance ",n,"= ", puissance(x,n))
✝ ✆

def puissance(x,n):
if n==0:
return 1
else:
p = puissance(x,n//2)
if n%2 == 0:
return p*p
else:
return x*p*p

def puis(a,n): # non terminal


if n == 0:
return 1

Numérique et Sciences informatiques Terminale 75 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

elif n % 2:
return a * puis(a * a, n // 2)
else:
return puis(a * a, n // 2)

def puis_t(a, n, acc=1): # terminal


if n == 0:
return acc
elif n % 2:
return puis_t(a*a, n//2, a*acc)
else:
return puis_t(a*a, n//2, acc)

def puis_it(a,n):
acc = 1
while n!=0:
if n%2:
acc = a*acc
a , n = a * a, n//2
return acc

print("puissance :", puissance(5,5))


print(puis(5,5))
print(puis_t(5,5))
print(puis_it(5,5))
✝ ✆

def somme(L): # cout quadratique, une nouvelle liste est créée à chaque appel
print(L)
if L == []:
return 0
else:
return L[0] + somme(L[1:])

print(somme([4, 7, 2]))

def somme2(L): # cout linéaire, la liste est modifiée en place, mais elle est d
étruite.
if L == []:
return 0
else:
x = L.pop()
return x + somme2(L)

print(somme2([4, 7, 2]))
✝ ✆

N=8
R= 15
def mystere(n):
if n < 2:
return str(n)
else:
return mystere(n // 2) + str( n % 2 )

print(mystere(R))

def binaire(r,n):
if r < 0:
r = r + 2** n
if r < 2:

Numérique et Sciences informatiques Terminale 76 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

return str(r)
else:
return binaire(r//2, n) + str(r%2)

print(binaire(R,N))

def binaire2(r, n, cpt=0):


if r < 0:
r = r + 2**n
if r < 2:
return (n-cpt-1)*’0’ + str(r)
else:
return binaire2(r//2, n, cpt+1) + str(r%2)

print(binaire2(R,N))
✝ ✆

def insertion(liste, n, x):
if n == 0 or x >= liste[n-1]:
liste[n] = x
else:
liste[n] = liste[n-1] # on inverse
insertion(liste, n-1, x)

def tri_insertion_rec(liste, n):


if n > 1:
tri_insertion_rec(liste,n-1)
insertion(liste, n-1, liste[n-1])

L = [2, 5,1, 8, 6, 3]
print(L)
insertion(L, 4, 12)
print(L)
tri_insertion_rec(L, len(L))
print(L)
✝ ✆

def pgcd(a,b):
if b == 0:
return(a)
else:
return pgcd(b, a%b)

print(pgcd(100, 90))
✝ ✆

Numérique et Sciences informatiques Terminale 77 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

5.4 Modularité
En général, quand on écrit un programme, on essaye de ne pas réécrire ce qui existe déjà.
Soit on utilise des programmes écrits par nous-même. On place des fonctions dans un module ou un pa-
ckage, et on importe ces fonctions avec la commande import monModule (voir cours de première). Soit
on utilise des bibliothèques existantes, par exemple : Pygame, Tkinter, math, time, Turtle,
Matplotlib, Numpy, PIL, Django....

5.5 Paradigmes de programmation


En informatique, la programmation impérative est un paradigme de programmation qui décrit les opérations en
séquences d’instructions exécutées par l’ordinateur pour modifier l’état du programme. Ce type de program-
mation est le plus répandu parmi l’ensemble des langages de programmation existants, et se différencie de la
programmation déclarative.
La programmation déclarative est un paradigme de programmation qui consiste à créer des applications sur
la base de composants logiciels indépendants du contexte et ne comportant aucun état interne. Autrement dit,
l’appel d’un de ces composants avec les mêmes arguments produit exactement le même résultat, quel que soit
le moment et le contexte de l’appel.
En programmation déclarative, on décrit le quoi, c’est-à-dire le problème. Par exemple, les pages HTML sont
déclaratives car elles décrivent ce que contient une page (texte, titres, paragraphes, etc.) et non comment les affi-
cher (positionnement, couleurs, polices de caractères ...). Alors qu’en programmation impérative (par exemple,
avec le C ou Java), on décrit le comment, c’est-à-dire la structure de contrôle correspondant à la solution.
C’est une forme de programmation sans effets de bord, ayant généralement une correspondance avec la logique
mathématique.
Il existe plusieurs formes de programmation déclarative :
❧ la programmation descriptive, à l’expressivité réduite, qui permet de décrire des structures de données,
comme HTML ou LaTeX ;
❧ la programmation fonctionnelle, qui perçoit les applications comme un ensemble de fonctions mathéma-
tiques, comme Lisp, Caml, Haskell et Oz ;
❧ la programmation logique, pour laquelle les composants d’une application sont des relations logiques,
comme Prolog et Mercury ;
❧ la programmation par contraintes.
La programmation fonctionnelle appelle des fonctions avec des arguments et retourne un résultat. Comme une
fonction peut-être un argument, on peut créer un programme contenant une fonction qui s’appelle elle-même :
c’est la récursivité. Elle découle du concept de diviser pour mieux régner : Pour résoudre un grand problème ,
on regarde si on a la solution pour pour le même problème beaucoup plus petit.
La programmation évènementielle est fondée sur des évènements : c’est le contraire de la programmation
séquentielle. On n’exécute pas une suite d’instructions les unes à la suite des autres, mais on programme des
réactions aux différents évènements qui peuvent se produire : cela revient à une gestion des interruptions. Ce
type de programmation est très utilisé en robotique, et est aussi le principe de fonctionnement du smartphone :
son état principal est d’être en veille, et dès qu’on l’utilise, on interrompt la veille.

Numérique et Sciences informatiques Terminale 78 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

5.6 Mise au point de programme ; gestion des bugs


Le programme ci-dessous dessine un morceau du flocon de Koch en utilisant turtle.

from turtle import *

def m1(taille):
forward(taille)
left(60)
forward(taille)
right(120)
forward(taille)
left(60)
forward(taille)

def courbe(longueur, niveau, motif):


if niveau == 1:
motif(longueur)
else:
longueur = longueur /3
niveau = niveau -1
courbe(longueur, niveau, motif)
lt(60)
courbe(longueur, niveau, motif)
rt(120)
courbe(longueur, niveau, motif)
lt(60)
courbe(longueur, niveau, motif)

up()
goto (-500, 150)
down()
speed(0)
ht()
width(2)
color(’black’)
courbe(200, 4, m1)
n=int(input())
✝ ✆
En vous inspirant de ce programme, choisissez sur l’image suivante un motif, et réalisez le programme qui le
dessine en permettant de faire varier le niveau de fractale.
Livrez-le selon ce cahier des charges :
❧ expliquez le motif choisi, sa modélisation mathématique ; (un petit historique peut-être ?)
❧ présentez votre algorithme pour sa résolution en précisant si ce dernier est itératif ou récursif ;
❧ en généralisant à un ordre supérieur, quelle est la complexité de votre algorithme ?
❧ Utilisez des fonctions, des modules, mettez en évidence vos tests de fonction et traitement des erreurs.

Numérique et Sciences informatiques Terminale 79 sur 105 Sandrine Piquard


CHAPITRE 5. LANGAGES ET PROGRAMMATION

Numérique et Sciences informatiques Terminale 80 sur 105 Sandrine Piquard


Chapitre 6 Algorithmique

6.1 Algorithmes sur les arbres binaires


Afin de pouvoir tester les différents programmes dans la suite, nous allons utiliser deux manières d’implémenter
un arbre.
Prenons par exemple la représentation ci-dessous :

B F

C D G H

E I J K

Nous pouvons écrire cet arbre sous la forme : a= [’A’, [’B’, [’C’, [], [’E’,[], []] ],[’D’,
[], []]], [’F’, [’G’, [’I’, [], []],[]],[’H’,[’J’, [], []],[’K’, [], []] ]]]

Une deuxième manière est d’utiliser une classe arbre :



1 class Arbre:
2 def __init__(self, val):
3 self.valeur = val
4 self.gauche= None
5 self.droit = None
6
7 def __str__(self):
8 return str(self.valeur)
9
10 def ajout_gauche(self, val):
11 self.gauche=Arbre(val)
12

13 def ajout_droit(self, val):


14 self.droit=Arbre(val)
15

16

17
18 b=Arbre(’A’)
19 b.ajout_gauche(’B’)
20 b.ajout_droit(’F’)
21

22 b.gauche.ajout_gauche(’C’)
23 b.gauche.ajout_droit(’D’)
24 b.gauche.gauche.ajout_droit(’E’)
25
26 b.droit.ajout_gauche(’G’)

81
CHAPITRE 6. ALGORITHMIQUE

27 b.droit.ajout_droit(’H’)
28 b.droit.gauche.ajout_gauche(’I’)
29 b.droit.droit.ajout_gauche(’J’)
30 b.droit.droit.ajout_droit(’K’)
31 print(b)
32 print(b.gauche,b.droit )
33 print(b.gauche.gauche,b.gauche.droit, b.droit.gauche, b.droit.droit )
34 print(b.gauche.gauche.droit, b.droit.gauche.gauche, b.droit.droit.gauche, b.
droit.droit.droit)
✝ ✆

6.1.1 Taille et hauteur d’un arbre binaire


Taille Il s’agit de déterminer le nombre total de nœuds : Principe de l’algorithme :
❧ si l’arbre est vide, il y a 0 nœud,
❧ sinon, nous comptons le nœud racine plus le nombre de nœuds du sous-arbre gauche et le nombre de
nœuds du sous-arbre droit.
Pour la première implémentation, un programme en Python s’écrit sous la forme récursive :

1 def taille1(arbre):
2 if arbre == []:
3 return 0
4 else:
5 return 1 + taille1(arbre[1]) + taille1(arbre[2])
✝ ✆
arbre[1] et arbre[2] sont les sous-arbres gauche et droit respectivement, ils correspondent aux éléments
numéro 1 et 2 de la liste de listes.
Pour la deuxième implémentation, le programme Python s’écrit sous la forme :

1 def taille2(arbre):
2 if arbre is None:
3 return 0
4 else:
5 return 1 + taille2(arbre.gauche) + taille2(arbre.droit)
✝ ✆
On peut aussi écrire une seule fonction taille pour les deux implémentations :

1 def vide(arbre):
2 return (arbre == []) or (arbre is None)
3

4 def gauche(arbre):
5 if isinstance(arbre, list): # teste si la variable arbre est une liste
6 return arbre[1]
7 else:
8 return arbre.gauche
9

10 def droit(arbre):
11 if isinstance(arbre, list): # teste si la variable arbre est une liste
12 return arbre[2]
13 else:
14 return arbre.droit
15
16

17 def taille(arbre):
18 if vide(arbre):
19 return 0
20 else:
21 return 1 + taille(gauche(arbre)) + taille(droit(arbre))
✝ ✆

Numérique et Sciences informatiques Terminale 82 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Exercice n°12 :
Tester les deux implémentations et vérifier qu’on obtient bien le même résultat pour les deux.

Hauteur Il s’agit de déterminer le nombre maximal de nœuds se trouvant entre la racine et une feuille.
Principe de l’algorithme :
❧ si l’arbre est vide, il contient 0 nœud.
❧ sinon, nous comptons le nœud racine plus le maximum entre le nombre de nboeud du sous-arbre gauche
et le nombre de noeud du sous arbre droit.
Un programme Python peut s’écrire sous la forme :

1 def hauteur(arbre):
2 if vide(arbre):
3 return 0
4 else:
5 return 1 + max(hauteur(gauche(arbre)), hauteur(droit(arbre))) # import
math?
✝ ✆
On peut aussi insérer ces fonction directement dans la classe Arbre, il suffit d’y ajouter :

1 def taille(self):
2 tg = self.gauche.taille() if self.gauche else 0
3 td = self.droit.taille() if self.droit else 0
4 return 1 + td + tg
5

6 def hauteur(self):
7 hg = self.gauche.hauteur() if self.gauche else 0
8 hd = self.droit.hauteur() if self.droit else 0
9 return 1 + max(hg, hd)
✝ ✆

Exercice n°13 :
Tester la classe Arbre avec ces méthodes.

6.1.2 Parcours d’un arbre binaire


On parcourt un arbre pour calculer sa taille ou sa hauteur, mais aussi pour chercher une valeur particulière, ou
pour afficher les différentes valeurs... Il existe différentes manières de parcourir un arbre. Nous distinguons un
parcourt en profondeur d’abord et un parcourt en largeur d’abord.

Parcours en profondeur d’abord


En anglais Deep-First Search(DFS).
Principe : on explore chaque branche complètement avant d’explorer la branche voisine. La programmation
récursive donne :
❧ si l’arbre est non vide, on parcourt de manière récursive son sous-arbre gauche, puis sont sous-arbre droit.
❧ sinon, c’est terminé.
On distingue trois cas suivant le moment où est traitée une racine d’un sous-arbre :
❧ Si la racine est traitée avant ses deux sous-arbres, il s’agit d’un ordre préfixe.
❧ Si la racine est traitée entre ses deux sous-arbres, il s’agit d’un ordre infixe.
❧ Si la racine est traitée après ses deux sous-arbres, il s’agit d’un ordre postfixe.
Nous reprenons les deux arbres précédents, et nous voulons afficher les différentes valeurs des sommets :

Numérique et Sciences informatiques Terminale 83 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE


1 def affiche_valeur(arbre):
2 if not vide(arbre):
3 if isinstance(arbre, list):
4 print(arbre[0])
5 else:
6 print(arbre.valeur)
✝ ✆
Nous disposons des fonctions vues plus haut : vide, gauche, droit et affiche_valeur.
Nous définissons alors les fonctions de parcours :

1 def dfs_prefixe(arbre):
2 if not vide(arbre):
3 affiche_valeur(arbre)
4 dfs_prefixe(gauche(arbre))
5 dfs_prefixe(droit(arbre))
6

7 def dfs_infixe(arbre):
8 if not vide(arbre):
9 dfs_infixe(gauche(arbre))
10 affiche_valeur(arbre)
11 dfs_infixe(droit(arbre))
12
13 def dfs_postfixe(arbre):
14 if not vide(arbre):
15 dfs_postfixe(gauche(arbre))
16 dfs_postfixe(droit(arbre))
17 affiche_valeur(arbre)
✝ ✆

Exercice n°14 :
En utilisant les deux arbres a et b précédents :
1. Executer dfs_prefixe(a) et dfs_prefixe(b), notez le résultat.
2. Executer dfs_infixe(a) et dfs_infixe(b), notez le résultat.
3. Executer dfs_postfixe(a) et dfs_postfixe(b), notez le résultat.
4. Légendez les schémas ci-dessous, en indiquant en rouge le sens de parcourt de l’arbre.

B F

C D G H

E I J K

Numérique et Sciences informatiques Terminale 84 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

B F

C D G H

E I J K

B F

C D G H

E I J K

Numérique et Sciences informatiques Terminale 85 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Parcours en largeur d’abord


Le cas du parcours d’un arbre en largeur d’abord (BFS : Breadth-First Search) pose quelques difficultés. En
effet, il s’agit de parcourir un arbre niveau par niveau en considérant tous les sommets de chaque niveau.
On commence donc par la racine, puis les deux racines des deux sous-arbre, puis les 4 racines des sous-arbres,
et ainsi de suite.
La méthode la plus pratique consiste à utiliser une structure de file (Voir Chap 1). En effet, la structure de file
est : « Premier entré, Premier sorti », et si on descend dans notre arbre niveau pas niveau, on est bien dans une
logique : « Premier niveau, Premier exploré » .
Principe de l’algorithme :
On suppose l’arbre non vide :
❧ On place l’arbre dans la file.
❧ Tant que la file n’est pas vide, on défile un élément, qui est un arbre, on affiche la valeur de la racine et
on place dans la file chacun des deux sous-arbres s’ils ne sont pas vides.
Cet algorithme est itératif.
Nous reprenons l’exemple de la classe Arbre, nous utilisons ici le module queue.
(Rappel : dans la console Python, importer le module : »> import queue puis »>help(queue) pour
connaître les fonctions ou les méthodes de ce module.)

1 class Arbre:
2 def __init__(self, val):
3 self.valeur = val
4 self.gauche= None
5 self.droit = None
6
7 def __str__(self):
8 return str(self.valeur)
9

10 def ajout_gauche(self, val):


11 self.gauche = Arbre(val)
12

13 def ajout_droit(self, val):


14 self.droit = Arbre(val)
15

16 def taille(self):
17 tg = self.gauche.taille() if self.gauche else 0
18 td = self.droit.taille() if self.droit else 0
19 return 1 + td + tg
20

21 b=Arbre(’A’)
22 b.ajout_gauche(’B’)
23 b.ajout_droit(’F’)
24

25 b.gauche.ajout_gauche(’C’)
26 b.gauche.ajout_droit(’D’)
27 b.gauche.gauche.ajout_droit(’E’)
28

29 b.droit.ajout_gauche(’G’)
30 b.droit.ajout_droit(’H’)
31 b.droit.gauche.ajout_gauche(’I’)
32 b.droit.droit.ajout_gauche(’J’)
33 b.droit.droit.ajout_droit(’K’)
34 #-----
35 from queue import *
36

37 def parcours_largeur(arbre):
38 f = Queue(arbre.taille()) # taille de la file
39 f.put(arbre) # l’arbre est placé dans la file

Numérique et Sciences informatiques Terminale 86 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

40 k=0
41 while not f.empty():
42 a = f.get() # un élément est retiré de la file
43 print(a.valeur, end = " ")
44 if a.gauche is not None:
45 f.put(a.gauche)
46 if a.droit in note None:
47 f.put(a.droit)
✝ ✆

Exercice n°15 :
1. Tester le résultat de parcours_largeur(b)
2. Représenter les différentes étapes, avec le contenu de la file :

6.2 Algorithmes des arbres binaires de recherche


Un arbre binaire de recherche est un arbre qui vérifie les propriétés suivantes :
Pour un sommet quelconque :
❧ les valeurs de son sous-arbre gauche sont inférieures à la valeur du sommet ;
❧ les valeurs de son sous-arbre droit sont supérieures à la valeur du sommet.
On suppose que toutes les valeurs sont distinctes.

6.2.1 Implémenter un ABR


On reprends l’implémentation d’un arbre avec la classe Arbre utilisée précédemment. Les deux méthodes
ajout_gauche et ajout_droit doivent être modifiées pour construire un ABR.
Principe :
On crée une méthode ajoute qui permet d’ajouter une valeur en respectant la propriété d’un ABR. L’insertion
d’une valeur dans l’arbre intervient donc après une recherche :

1 class ABR:
2 def __init__(self, val):
3 self.valeur = val
4 self.gauche = None
5 self.droit = None
6
7 def __str__(self):
8 return str(self.valeur)
9

10 def ajoute(self, valeur):


11 if valeur < self.valeur:
12 if self.gauche is None:
13 self.gauche = ABR(valeur)

Numérique et Sciences informatiques Terminale 87 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

14 else:
15 self.gauche.ajoute(valeur)
16 elif valeur > self.valeur:
17 if self.droit is None:
18 self.droit = ABR(valeur)
19 else:
20 self.droit.ajoute(valeur)
21

22 monArbre = ABR(17)
23 monArbre.ajoute(10)
24 monArbre.ajoute(23)
25 monArbre.ajoute(19)
26 monArbre.ajoute(25)
27 monArbre.ajoute(2)
28 monArbre.ajoute(20)
29 monArbre.ajoute(8)
✝ ✆
Pour rechercher une valeur dans l’arbre, on cré une méthode recherche qui repose sur le même principe que
l’insertion d’une valeur dans l’arbre :

1 class ABR:
2 ...
3
4 def recherche(self, val):
5 if val < self.valeur:
6 return self.gauche.recherche(val) if self.gauche else False
7 elif val > self.valeur:
8 return self.droit.recherche(val) if self.droit else False
9 return True # la méthode renvoie True si val == self.valeur
✝ ✆

Exercice n°16 :
1. Tester la méthode en recherchant les valeurs 19 et 18.
2. Est-ce que l’intégralité de l’arbre est parcouru pour trouver la valeur ? A quoi cela fait-il penser ?
3. Entrer un nouvel arbre avec les valeurs : 5, 8, 11, 13, 15, 18. Quelle sont la taille et la hauteur de cet
arbre ? (Réutiliser les fonctions vues précédemment)
4. Entrer un nouvel arbre avec les valeurs : 13, 8, 5, 11, 15, 18. Quelle sont la taille et la hauteur de cet
arbre ? Que constatez-vous ?

6.2.2 Equilibrer un ABR


Si l’arbre de recherche est équilibré et possède n nœuds, alors cette recherche à un coût de
l’ordre du logarithme de n, c’est à dire du nombre de chiffres utilisés dans l’écriture de n. (voir
exercices)

6.2.3 Recherche d’un minimum ou d’un maximum


On ajoute dans la classe les méthodes suivantes :

1 class ABR:
2 ...
3

4 def minimum(self, val):


5 s = self
6 while s.gauche:
7 s = s.gauche
8 return s.valeur
✝ ✆

Numérique et Sciences informatiques Terminale 88 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Exercice n°17 :
1. Ecrire vous-même la méthode qui trouve le maximum.
2. Tester ces deux méthodes.

Coût d’une recherche et d’une insertion


D’une manière générale, dans un arbre binaire de recherche, les fonctions de recherche d’une valeur, d’un
minimum, d’un maximum, d’un successeur, d’un prédécesseur ont un coût de l’ordre de h où h est la hauteur
de l’arbre. Il en est de même pour l’insertion d’une valeur.

Exercice n°18 :
1. Créer un arbre rempli de 100 nombres aléatoires distincts compris entre 1 et 100.
2. Combien d’étapes sont nécessaires pour trouver la valeur 25 ?
3. Modifier le programme pour faire en sorte que l’arbre soit équilibré.
4. Combien d’étapes sont nécessaires pour trouver la valeur 25 ? Conclure.

6.3 Applications
Un exemple d’utilisation des ABR est le codage de Huffmann.
Codage de Huffman est un algorithme de compression de données sans perte. Le codage de Huff-
man utilise un code à longueur variable pour représenter un symbole de la source (par exemple un
caractère dans un fichier). Le code est déterminé à partir d’une estimation des probabilités d’appa-
rition des symboles de source, un code court étant associé aux symboles de source les plus fréquents.
Un code de Huffman est optimal au sens de la plus courte longueur pour un codage par symbole,
et une distribution de probabilité connue. Des méthodes plus complexes réalisant une modélisation
probabiliste de la source permettent d’obtenir de meilleurs ratios de compression.
(wikipédia)
Prenons un texte quelconque. Si nous utilisons le codage ASCII étendu à 256 caractères pour avoir les caractères
accentués de la langue française, chaque caractère peut être codé sur un octet. Donc un texte de 1000 caractères
nécessite 1000 octets.
Le principe du codage de Huffmann est le suivant :
Plutôt que de coder chaque caractère sur 8 bits, on utilise moins de bits pour les caractères les plus fréquents et
plus de bits pour les caractères les moins fréquents.
Considérons un exemple simple. Un texte ne contient que les quatre caractères suivants : "A", "Z", "E", "R".
Le "E" est présent 500 fois, le "R" est présent 300 fois, le "A" est présent 150 fois, le "Z" est présent 50 fois.
Nous commençons par le caractère le plus fréquent et codons le "E" par 0. Nous ne pouvons pas coder le "R"
avec la valeur 1 ou 01, en effet une suite de bits débutant par 0 est alors ambigüe : est-ce "ER" ou est-ce un autre
caractère commençant par 1 ? L’astuce est de dire que "R" est codé 10, les autres caractères devront commencer
par 11.
Finalement le codage est le suivant :
"A" "Z" "E" "R"
110 111 0 10
Considérons une suite quelconque : 100110111010 et vérifions que le codage fonctionne.

Pour un texte de 1000 caractères, nous obtenons donc : 500 ∗ 1 + 300 ∗ 2 + (150 + 50) ∗ 3 = 1700 bits au lieu
des 8000 bits standard. On a donc compressé l’information sans perte.

Numérique et Sciences informatiques Terminale 89 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Remarque : en utilisant 00, 01, 10, 11, on aurait obtenu : 1000∗2 = 2000 bits, ce qui aurait été moins performant
en terme de compression.
Remarque 2 : le rapport 8000 2000
1700 ou 1700 s’appelle le taux de compression. Dans le premier cas il est de 4,7 alors
que dans le second cas il est de 1,18.
Voici l’arbre correspondant à notre exemple :

1000
0 1

E : 500 500
0 1

R : 300 200
0 1

A : 150 Z :50

Exercice n°19 :
Dessiner un arbre binaire de recherche (sur le papier) obtenu dans les 2 cas qui suivent :
1. en insérant dans l’ordre les valeurs suivantes : 17, 25, 11, 13, 23, 5, 8, 28.
2. en insérant dans l’ordre les valeurs suivantes : 17, 5, 28, 11, 13,23, 8, 25.
Conclure.

Exercice n°20 :
On recherche le nombre 28 dans un arbre binaire de recherche.
1. La suite des nœuds parcourus est : 13, 22, 35, 31, 29, 23, 25, 28. Est-ce possible ?
2. La suite des nœuds parcourus est : 13, 23, 35, 31, 29, 22, 25, 28. Est-ce possible ?

Exercice n°21 :
On définit une méthode de recherche de la class ABR :

1 class ARB:
2 ...
3 def recherche(self, val):
4 while self.valeur is not None:
5 if val < self.valeur:
6 if not self.gauche: return False
7 self = self.gauche
8 elif val > self.valeur:
9 if not self.droit : return False
10 self = self.droit
11 else: return True
✝ ✆

Numérique et Sciences informatiques Terminale 90 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Prouver la correction de ce programme. (Rappel : pour prouver la correction d’un programme itératif, il faut
utiliser un invariant de boucle.

Exercice n°22 :
Successeur et prédécesseur.

1. On reprend la classe ABR en ajoutant l’attribut self.parent = None. Modifier les méthodes en
conséquence.
2. Un nœud est un enfant gauche s’il a un parent et qu’il est la racine du sous-arbre gauche de son parent.
Ecrire une méthode enfant_gauche qui renvoie True si le nœud concerné est un enfant gauche et
False sinon.
3. De même écrire enfant_droit.
Les valeurs des noeuds contenues dans l’arbre peuvent être rangées par ordre croissant. Une valeur non extrême
a une valeur qui lui succède et une qui lui précède. Les noeuds correspondants s’appellent respectivement
successeur et prédécesseur.
1. Ecrire une méthode successeur qui renvoie le successeur d’un nœud. Si le nœud a un fils droit, le
successeur est le nœud le plus à gauche du sous-arbre droit, sinon c’est le premier ascendant tel que le
nœud est dans son sous-arbre gauche.
2. Ecrire une méthode predecesseur qui renvoie le prédécesseur d’un nœud.

Exercice n°23 :
1. Implémenter le codage de Huffmann. (Attention, ne prenez pas un texte en UTF-8, ou alors tenez-en
compte)
2. Comment joindre l’arbre de compression ?
3. Tester le taux de compression sur quelques gros fichiers texte.
4. Pour aller plus loin : implémenter la décompression.

Numérique et Sciences informatiques Terminale 91 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Nous montrons que la propriété « la valeur cherchée appartient à l’arbre courant » est un invariant de boucle.
L’arbre courant est représenté par self. Si la valeur recherchée appratient à cet arbre, celui-ci a une racine.
Soit la valeur cherchée est égale à cette racine et le programme s’arrête ; soit elle est strictement inférieure à
cette racine et appartient donc au sous-arbre gauche ; soit elle est supérieure à cette racine et appartient donc
au sous-arbre droit. Donc si il y a un nouveau passage dans la boucle, la valeur cherchée appartient encore à
l’arbre courant.
Si la valeur cherchée n’appartient pas à l’arbre courant, elle n’est pas égale à la racine et n’appartient ni au sous-
arbre gauche, ni au sous-arbre droit. Donc elle n’appartient pas à l’arbre courant s’il y a un nouveau passage
dans la boucle, le programme s’arrête lorsque l’arbre courant n’a pas de sous-arbre gauche alors qu’on cherche
une valeur inférieure, ou pas de sous-arbre droit alors qu’on cherche une valeur supérieure à la racine courante.

Numérique et Sciences informatiques Terminale 92 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

6.4 Algorithmes sur les graphes

6.5 Méthode « diviser pour régner »


L’expression « diviser pour régner », en anglais « divide and conquer », signifie en informatique : on « divise »
en réduisant le problème en sous-problèmes, puis on « règne » en résolvant ces sous-problèmes. Il reste ensuite
à combiner les sous-problèmes pour obtenir une solution au problème initial.
Nous allons voir que dans la conception d’un algorithme, ce type de résolution se prète bien à une écriture
récursive plutôt qu’à une écriture itérative.
Cependant dans certains cas, l’écriture récursive d’un programme peut-être limitée par l’utilisation de la mé-
moire, dans le cas d’une pile par exemple.

6.5.1 Le problème du palindrome


Un palindrome est une chaîne de caractère qui peut se lire dans les deux sens.
Victor Hugo : « Et la marine va, papa, venir à Malte »
Alain Damasio : « Et si l’arôme des bottes révèle madame, le verset t’obsède, moraliste ! »
La fonction palindrome ci-dessous prend en argument une chaîne de caractères et renvoie True si la chaîne
est un palindrome et False sinon.

1 def palindrome(ch):
2 """
3 ch est de type str
4 renvoie True si ch est un palindrome, False sinon.
5 """
6 if len(ch) <= 1:
7 return True
8 else:
9 return ch[0] == ch[-1] and palindrome(ch[1:-1])
✝ ✆
Quelques tests : ressasser, radar, toto.

Exercice n°24 :
1. Décrivez ce que fait le programme.

2. Vérifiez la terminaison du programme : le programme termine-t-il dans tous les cas ?


3. Pour une chaîne de n caractères combien y a-t-il d’appels récursifs ?
4. Quel est le coût en temps ?
5. quel est le coût en mémoire ?
Voici une nouvelle version de ce programme :

1 def palindrome2(ch, g, d):
2 if d - g < 1:
3 return True
4 else:
5 return ch[g] == ch[d] and palindrome2(ch, g+1, d-1)
6

7 if __name__ == "__main__":
8 mot = "ressasser"
9 palindrome2( mot, 0, len(mot)-1)
✝ ✆

Numérique et Sciences informatiques Terminale 93 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

Exercice n°25 :
1. Décrivez ce que fait le programme, en mettant en avant les différences avec le précédent.

2. En quoi ce programme est un exemple de « diviser pour régner » ?

6.5.2 Recherche dichotomique


Le coût d’une recherche linéaire d’un élément dans un tableau de taille n est de l’ordre de n dans le pire des
cas : O(n). C’est à dire si on est obligé de tester tous les éléments du tableau.
On pourrait penser améliorer le coût dans le cas d’un tableau dont les éléments sont triés, en effet dans un
tableau trié dans l’ordre croissant la recherche s’arrête dès qu’un élément est plus grand que l’élément cherché.
Mais cela change peu la complexité.
L’intérêt d’une recherche dichotomique est d’améliorer grandement le coût d’une recherche linéaire.

1 def dichotomie(liste, x):
2 g, d = 0, len(liste)-1
3 while g <= d:
4 m = (g + d) / 2
5 if x == liste[m]:
6 return True
7 else:
8 if x > liste[m]:
9 g=m+1
10 elif x < liste[m]:
11 d = m -1
12 return False
✝ ✆

1. Etude de la terminaison de l’algorithme : est-ce que la fonction renvoie effectivement une valeur ?
Au départ, l’intervalle d’étude est l’ensemble {0, 1, ..., N-1} de longueur N. Après chaque itération
dans la boucle while la longueur de l’intervalle est divisé par 2. Supposons donc que N = 2n , c’est un
multiple de 2.
1
Notons li cette longueur à chaque itération. La suite est géométrique de raison et de premier terme N,
2
soit :
N 2n
li = i = i = 2n−i
2 2
n
(Un = U0 × q = Un0 × q 0 ) n−n

en effet, à la nième division ln = 1, et l’algorithme s’arrête.


2. Etude le la correction de l’algorithme : est-ce que la fonction renvoie la valeur attendue ? L’invariant de
boucle, c’est à dire la proposition qui est vraie à chaque itération est : q <= d , on sélectionne toujours
un intervalle tel que la borne inférieure soit plus petite que la borne supérieure, et que la valeur cherchée
soit entre les deux. A chaque fois que l’on divise l’intervalle par 2, cette proposition est toujours vraie,
donc il vient un moment où l’intervalle est {val, val+1} et val est la valeur cherchée.
3. Etude de la complexité de l’algorithme : peut-on estimer la vitesse d’exécution de cet algorithme ? Com-
bien de fois aura-t-on divisé l’intervalle par 2 avant de trouver le résultat ? On a posé que N = 2n , donc
dans le pire des cas, on aura diviser l’intervalle par deux n fois donc si : N = 2n , alors n = log2 N. La
complexité est en O(log2 N ).

6.5.3 Exemple du tri fusion récursif


Nous avons vu en classe de première le tri par fusion (merge sort) dans sa version itérative.
Principe de l’algorithme récursif :

Numérique et Sciences informatiques Terminale 94 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

❧ la liste à triée est partagée en deux parties égale à une unité près ;
❧ Un appel récursif est alors réalisé sur chacune des deux parties ;
❧ lorsque les deux parties sont triées, elles sont fusionnées en une liste triée.

Exercice n°26 :
Soit une liste d’entiers : [5, 6, 8, 9, 3, 4, 8]
1. Implémenter un tri par fusion récursif ;
2. Implémenter un tri par fusion itératif ;
3. Un tri en place se fait en modifiant les valeurs de la liste directement, un tri qui n’est pas en place trie une
liste secondaire et la renvoie. Comparez vos solutions avec celles de vos voisins, reconnaissez quel sont
les algorithmes qui sont en place et ceux qui ne le sont pas.
4. Quel est l’intérêt de réaliser un tri en place ?

Numérique et Sciences informatiques Terminale 95 sur 105 Sandrine Piquard


CHAPITRE 6. ALGORITHMIQUE

6.5.4 Exemple de la rotation d’une image d’un quart de tour

6.6 Programmation dynamique


6.6.1 L’algorithme du rendu de monnaie
6.6.2 Discussion sur le coût en mémoire

6.7 Recherche textuelle


6.7.1 Etude de l’algorithme de Boyer-Moore
6.7.2 L’intérêt du prétraitement du motif

Exos :

Numérique et Sciences informatiques Terminale 96 sur 105 Sandrine Piquard


Chapitre 7 Grand Oral

7.1 Définition et objectifs


L’épreuve du « Grand Oral » a été conçue pour permettre au candidat de montrer sa capacité à prendre la
parole en public de façon claire et convaincante. Elle lui permettra aussi d’utiliser les connaissances liées à ses
spécialités pour démontrer ses capacités argumentatives et la maturité de son projet de poursuite d’études, voire
professionnel.

7.2 Épreuve orale


❧ Durée : 20 minutes
❧ Préparation : 20 minutes
❧ Coefficient : 10

7.3 Évaluation de l’épreuve


L’épreuve est notée sur 20 points.
Le jury valorise la solidité des connaissances du candidat, sa capacité à argumenter et à relier les savoirs, son
esprit critique, la précision de son expression, la clarté de son propos, son engagement dans sa parole, sa force
de conviction et la manière d’exprimer une réflexion personnelle, ainsi que ses motivations.

7.4 Format et déroulement de l’épreuve


Le Grand oral dure 20 minutes avec 20 minutes de préparation.
Le candidat présente au jury deux questions préparées avec ses professeurs et éventuellement avec d’autres
élèves, qui portent sur ses deux spécialités, soit prises isolément, soit abordées de manière transversale.
Le jury choisit une de ces deux questions. Le candidat a ensuite 20 minutes de préparation pour mettre en
ordre ses idées et créer s’il le souhaite un support (qui ne sera pas évalué) à donner au jury.
L’épreuve se déroule en 3 temps :
❧ Pendant 5 minutes, le candidat présente la question choisie et y répond. Le jury évalue son argumenta-
tion et ses qualités de présentation. L’exposé se déroule sans note et debout, sauf aménagements pour les
candidats à besoins spécifiques.
❧ Ensuite, pendant 10 minutes, le jury échange avec le candidat et évalue la solidité de ses connaissances et
ses compétences argumentatives. Ce temps d’échange permet à l’élève de mettre en valeur ses connais-
sances, liées au programme des spécialités suivies en classe de première et terminale.
❧ Les 5 dernières minutes d’échanges avec le jury portent sur le projet d’orientation du candidat. Le
candidat montre que la question traitée a participé à la maturation de son projet de poursuite d’études, et
même pour son projet professionnel.

7.5 Composition du jury


Le jury est composé de deux professeurs de disciplines différentes :
❧ l’un représente l’un des enseignements de spécialité du candidat,
❧ l’autre représente l’autre enseignement de spécialité, ou l’un des enseignements communs, ou est professeur-
documentaliste.

97
Chapitre 8 Programme officiel

8.1 Préambule

L’enseignement de spécialité de numérique et sciences informatiques du cycle terminal de la voie générale
vise l’appropriation des fondements de l’informatique pour préparer les élèves à une poursuite d’études en
les formant à la pratique d’une démarche scientifique et en développant leur appétence pour des activités de
recherche.
L’objectif de cet enseignement général est l’appropriation des concepts et des méthodes qui fondent l’informa-
tique, dans ses dimensions scientifiques et techniques. Il s’appuie sur l’universalité de quatre concepts fonda-
mentaux et la variété de leurs interactions :
les données , qui représentent sous une forme numérique unifiée des informations très diverses : textes, images,
sons, mesures physiques, sommes d’argent, etc. ;
les algorithmes , qui spécifient de façon abstraite et précise des traitements à effectuer sur les
les langages , qui permettent de traduire les algorithmes abstraits en programmes textuels ou graphiques de
façon à ce qu’ils soient exécutables par les machines ;
les machines , et leurs systèmes d’exploitation, qui permettent d’exécuter des programmes en enchaînant un
grand nombre d’instructions simples, assurent la persistance des données par leur stockage et gèrent les
communications. Y sont inclus les objets connectés et les réseaux.
À ces concepts s’ajoute un élément transversal : les interfaces qui permettent la communication, la collecte des
données et la commande des systèmes.
Cet enseignement prolonge les enseignements d’informatique dispensés à l’école primaire, au collège en ma-
thématiques et en technologie et, en classe de seconde, l’enseignement commun Sciences numériques et tech-
nologie. Il s’appuie aussi sur l’algorithmique pratiquée en mathématiques en classe de seconde. Il approfondit
les notions étudiées et les compétences travaillées en classe de première dans l’enseignement de spécialité. Il
autorise tous les choix de couplage avec les autres spécialités.
L’enseignement de spécialité de numérique et sciences informatiques permet de développer les compétences
suivantes, constitutives de la pensée informatique :
❧ analyser et modéliser un problème en termes de flux et de traitement d’informations ;
❧ décomposer un problème en sous-problèmes, reconnaître des situations déjà analysées et réutiliser des
solutions ;
❧ concevoir des solutions algorithmiques ;
❧ traduire un algorithme dans un langage de programmation, en spécifier les interfaces et les interactions,
comprendre et réutiliser des codes sources existants, développer des processus de mise au point et de
validation de programmes ;
❧ mobiliser les concepts et les technologies utiles pour assurer les fonctions d’acquisition, de mémorisation,
de traitement et de diffusion des informations ;
❧ développer des capacités d’abstraction et de généralisation.
Cet enseignement se déploie en mettant en activité les élèves, sous des formes variées qui permettent de déve-
lopper des compétences transversales :
❧ faire preuve d’autonomie, d’initiative et de créativité ;
❧ présenter un problème ou sa solution, développer une argumentation dans le cadre d’un débat ;
❧ coopérer au sein d’une équipe dans le cadre d’un projet ;
❧ rechercher de l’information, partager des ressources ;
❧ faire un usage responsable et critique de l’informatique.

98
CHAPITRE 8. PROGRAMME OFFICIEL

La progression peut suivre un rythme annuel construit autour de périodes spécifiques favorisant une alternance
entre divers types d’activités.
L’enseignement de numériques et sciences informatiques permet l’acquisition des compétences numériques qui
font l’objet d’une certification en fin de cycle terminal. Comme tous les enseignements de spécialité, il contribue
au développement des compétences orales à travers notamment la pratique de l’argumentation. Celle-ci conduit
à préciser sa pensée et à expliciter son raisonnement de manière à convaincre. Elle permet à chacun de faire
évoluer sa pensée, jusqu’à la remettre en cause si nécessaire, pour accéder progressivement à la vérité par la
preuve.

8.1.1 Démarche de projet


Un enseignement d’informatique ne saurait se réduire à une présentation de concepts ou de méthodes sans
permettre aux élèves de se les approprier en développant des projets.
Un tiers au moins de l’horaire total de la spécialité est réservé à la conception et à l’élaboration de projets
conduits par des petits groupes d’élèves.
Les projets réalisés par les élèves, sous la conduite du professeur, constituent un apprentissage fondamental
tant pour l’appropriation des concepts informatiques que pour l’acquisition de compétences. En classe de pre-
mière comme en classe terminale, ils peuvent porter sur des problématiques issues d’autres disciplines et ont
essentiellement pour but d’imaginer des solutions répondant à un problème ; dans la mesure du possible, il
convient de laisser le choix du thème du projet aux élèves. Il peut s’agir d’un approfondissement théorique
des concepts étudiés en commun, d’une application à d’autres disciplines telle qu’une simulation d’expérience,
d’exploitation de modules liés à l’intelligence artificielle et en particulier à l’apprentissage automatique, d’un
travail sur des données socio-économiques, du développement d’un logiciel de lexicographie, d’un projet au-
tour d’un objet connecté ou d’un robot, de la conception d’une bibliothèque implémentant une structure de
données complexe, d’un problème de traitement d’image ou de son, d’une application mobile, par exemple de
réalité virtuelle ou augmentée, du développement d’un site Web associé à l’utilisation d’une base de données,
de la réalisation d’un interpréteur d’un mini-langage, de la recherche d’itinéraire sur une carte (algorithme A*),
d’un programme de jeu de stratégie, etc.
La conduite d’un projet inclut des points d’étape pour faire un bilan avec le professeur, valider des éléments,
contrôler l’avancement du projet ou en adapter les objectifs, voire le redéfinir partiellement, afin de maintenir
la motivation des élèves.
Les professeurs veillent à ce que les projets restent d’une ambition raisonnable afin de leur permettre d’aboutir.

8.1.2 Modalités de mise en œuvre


Les activités pratiques et la réalisation de projets supposent que chaque élève ait un accès individuel à un
équipement relié à internet.
Un langage de programmation est nécessaire pour l’écriture des programmes : un langage simple d’usage, inter-
prété, concis, libre et gratuit, multiplateforme, largement répandu, riche de bibliothèques adaptées et bénéficiant
d’une vaste communauté d’auteurs dans le monde éducatif est à privilégier. Au moment de la conception de ce
programme, le langage choisi est Python version 3 (ou supérieure).
L’expertise dans tel ou tel langage de programmation n’est cependant pas un objectif de formation.

8.2 Éléments de programme


Le programme, organisé en six rubriques, ne constitue pas un plan de cours. Il appartient aux professeurs de
choisir leur progression, sans faire de chaque partie un tout insécable et indépendant des autres. Les mêmes
notions peuvent être développées et éclairées dans différentes rubriques et leurs interactions mises en évidence.

8.2.1 Histoire de l’informatique


Cette rubrique transversale se décline dans chacune des cinq autres.

Numérique et Sciences informatiques Terminale 99 sur 105 Sandrine Piquard


CHAPITRE 8. PROGRAMME OFFICIEL

Comme tous les concepts scientifiques et techniques, ceux de l’informatique ont une histoire et ont été forgés
par des personnes. Les algorithmes sont présents dès l’Antiquité, les machines à calculer apparaissent pro-
gressivement au XVIIe siècle, les sciences de l’information sont fondées au XIXe siècle, mais c’est en 1936
qu’apparaît le concept de machine universelle, capable d’exécuter tous les algorithmes, et que les notions de
machine, algorithme, langage et information sont pensées comme un tout cohérent. Les premiers ordinateurs
ont été construits en 1948 et leur puissance a ensuite évolué exponentiellement. Parallèlement, les ordinateurs
se sont diversifiés dans leurs tailles, leurs formes et leurs emplois : téléphones, tablettes, montres connectées, or-
dinateurs personnels, serveurs, fermes de calcul, méga-ordinateurs. Le réseau internet, développé depuis 1969,
relie aujourd’hui ordinateurs et objets connectés.
Contenus Capacités attendues Commentaires
Événements clés de l’histoire de Situer dans le temps les principaux Ces repères viennent
l’informatique. événements de l’histoire de compléter ceux qui ont été
l’informatique et leurs introduits en première.
protagonistes. Ces repères historiques sont
construits au fur et à mesure
Identifier l’évolution des rôles de la présentation des
relatifs des logiciels et des concepts et techniques.
matériels.

8.2.2 Structures de données


L’écriture sur des exemples simples de plusieurs implémentations d’une même structure de données permet de
faire émerger les notions d’interface et d’implémentation, ou encore de structure de données abstraite. Le pa-
radigme de la programmation objet peut être utilisé pour réaliser des implémentations effectives des structures
de données, même si ce n’est pas la seule façon de procéder. Le lien est établi avec la notion de modula-
rité qui figure dans la rubrique « langages et programmation » en mettant en évidence l’intérêt d’utiliser des
bibliothèques ou des API (Application Programming Interface).

Numérique et Sciences informatiques Terminale 100 sur 105 Sandrine Piquard


CHAPITRE 8. PROGRAMME OFFICIEL

Contenus Capacités attendues Commentaires


Structures de données, interface et Spécifier une structure de don- L’abstraction des structures de
nées
implémentation. par son interface. données est introduite après
Distinguer interface et plusieurs implémentations
implémentation. d’une structure simple comme
Écrire plusieurs implémentations la file (avec un tableau ou avec
d’une même structure de don- deux piles).
nées.
Vocabulaire de la programmation Écrire la définition d’une classe. On n’aborde pas ici tous les
objet : classes, attributs, méthodes, Accéder aux attributs et aspects de la programmation
objets. méthodes d’une classe. objet comme le
polymorphisme et l’héritage.
Listes, piles, files : structures Distinguer des structures par le On distingue les modes FIFO
jeu
linéaires. des méthodes qui les caracté- (first in first out) et LIFO (last
risent.
Dictionnaires, index et clé. Choisir une structure de données in first out) des piles et des
adaptée à la situation à modéliser. files.
Distinguer la recherche d’une
valeur dans une liste et dans un Les listes n’existent pas de
dictionnaire. façon native en Python.
Arbres : structures hiérarchiques. Identifier des situations nécessi- On fait le lien avec la rubrique
tant
Arbres binaires : nœuds, racines, une structure de données « algorithmique » .
feuilles, sous-arbres gauches, sous- arborescente.
arbres droits. Évaluer quelques mesures des
arbres binaires (taille,
encadrement de la hauteur, etc.).
Graphes : structures relationnelles. Modéliser des situations sous On s’appuie sur des exemples
Sommets, arcs, arêtes, graphes forme de graphes. comme le réseau routier, le
orientés ou non orientés. Écrire les implémentations réseau électrique, internet, les
correspondantes d’un graphe : réseaux sociaux.
matrice d’adjacence, liste de Le choix de la représentation
successeurs/de prédécesseurs. dépend du traitement qu’on
Passer d’une représentation à une veut mettre en place : on fait
autre. le lien avec la rubrique
« algorithmique ».

8.2.3 Bases de données


Le développement des traitements informatiques nécessite la manipulation de données de plus en plus nom-
breuses. Leur organisation et leur stockage constituent un enjeu essentiel de performance. Le recours aux bases
de données relationnelles est aujourd’hui une solution très répandue. Ces bases de données permettent d’orga-
niser, de stocker, de mettre à jour et d’interroger des données structurées volumineuses utilisées simultanément
par différents programmes ou différents utilisateurs. Cela est impossible avec les représentations tabulaires
étudiées en classe de première. Des systèmes de gestion de bases de données (SGBD) de très grande taille (de
l’ordre du pétaoctet) sont au centre de nombreux dispositifs de collecte, de stockage et de production d’informa-
tions. L’accès aux données d’une base de données relationnelle s’effectue grâce à des requêtes d’interrogation
et de mise à jour qui peuvent par exemple être rédigées dans le langage SQL (Structured Query Language).
Les traitements peuvent conjuguer le recours au langage SQL et à un langage de programmation. Il convient de
sensibiliser les élèves à un usage critique et responsable des données.

Numérique et Sciences informatiques Terminale 101 sur 105 Sandrine Piquard


CHAPITRE 8. PROGRAMME OFFICIEL

Contenus Capacités attendues Commentaires


Modèle relationnel : relation, Identifier les concepts définissant Ces concepts permettent
attribut, domaine, clef primaire, le modèle relationnel. d’exprimer les contraintes
clef étrangère, d’intégrité (domaine, relation
schéma relationnel. et référence).
Base de données relationnelle. Savoir distinguer la structure La structure est un ensemble
d’une base de données de de schémas relationnels qui
son contenu. respecte les contraintes du
Repérer des anomalies dans le modèle relationnel.
schéma d’une base de données. Les anomalies peuvent
être des redondances de
données ou des anomalies
d’insertion, de suppression, de
mise à jour.
On privilégie la manipulation
de données nombreuses et réalistes.
Système de gestion de bases Identifier les services rendus par Il s’agit de comprendre le rôle
de données relationnelles. un système de gestion de bases de et les enjeux des différents
données relationnelles : services sans en détailler le
persistance des données, gestion fonctionnement.
des accès concurrents, efficacité
de traitement des requêtes,
sécurisation des accès.
Langage SQL : requêtes Identifier les composants d’une On peut utiliser DISTINCT,
d’interrogation et requête. ORDER BY ou les fonctions
de mise à jour Construire des requêtes d’agrégation sans utiliser les
d’une base de données. d’interrogation à l’aide des clauses GROUP BY et HAVING.
clauses du langage SQL :
SELECT, FROM, WHERE, JOIN.
Construire des requêtes
d’insertion et de mise à jour à l’aide
de :
UPDATE, INSERT, DELETE.

8.2.4 Architectures matérielles, systèmes d’exploitation et réseaux


La réduction de taille des éléments des circuits électroniques a conduit à l’avènement de systèmes sur puce
(SoCs pour Systems on Chips en anglais) qui regroupent dans un seul circuit nombre de fonctions autrefois
effectuées par des circuits séparés assemblés sur une carte électronique. Un tel système sur puce est conçu
et mis au point de façon logicielle, ses briques électroniques sont accessibles par des APIs, comme pour les
bibliothèques logicielles.
Toute machine est dotée d’un système d’exploitation qui a pour fonction de charger les programmes depuis la
mémoire de masse et de lancer leur exécution en leur créant des processus, de gérer l’ensemble des ressources,
de traiter les interruptions ainsi que les entrées-sorties et enfin d’assurer la sécurité globale du système.
Dans un réseau, les routeurs jouent un rôle essentiel dans la transmission des paquets sur internet : les paquets
sont routés individuellement par des algorithmes. Les pertes logiques peuvent être compensées par des proto-
coles reposant sur des accusés de réception ou des demandes de renvoi, comme TCP. La protection des données
sensibles échangées est au cœur d’internet. Les notions de chiffrement et de déchiffrement de paquets pour les
communications sécurisées sont explicitées.

Numérique et Sciences informatiques Terminale 102 sur 105 Sandrine Piquard


CHAPITRE 8. PROGRAMME OFFICIEL

Contenus Capacités attendues Commentaires


Composants intégrés d’un système Identifier les principaux Le circuit d’un téléphone peut
sur puce. composants sur un schéma de être pris comme un exemple :
circuit et les avantages de leur microprocesseurs, mémoires
intégration en termes de vitesse et locales, interfaces radio et
de consommation. filaires, gestion d’énergie,
contrôleurs vidéo,
accélérateur graphique,
réseaux sur puce, etc.
Gestion des processus et des Décrire la création d’un proces- À l’aide d’outils standard, il
sus,
ressources par un système l’ordonnancement de plusieurs s’agit d’observer les processus
d’exploitation. processus par le système. actifs ou en attente sur une
Mettre en évidence le risque de machine.
l’interblocage (deadlock). Une présentation débranchée
de l’interblocage peut être
proposée.
Protocoles de routage. Identifier, suivant le protocole de En mode débranché, les tables
routage utilisé, la route emprun- de routage étant données, on
tée
par un paquet. se réfère au nombre de sauts
(protocole RIP) ou au coût des
routes (protocole OSPF).
Le lien avec les algorithmes de
recherche de chemin sur un
graphe est mis en évidence.
Sécurisation des communications. Décrire les principes de Les protocoles symétriques et
chiffrement symétrique (clef asymétriques peuvent être
partagée) et asymétrique (avec illustrés en mode débranché,
clef
privée/clef publique). éventuellement avec
Décrire l’échange d’une clef description d’un chiffrement
symétrique en utilisant un particulier.
protocole asymétrique pour La négociation de la méthode
sécuriser une communication chiffrement du protocole SSL
HTTPS. (Secure Sockets Layer) n’est
pas abordée.

8.2.5 Langages et programmation


Le travail entrepris en classe de première sur les méthodes de programmation est prolongé. L’accent est mis sur
une programmation assurant une meilleure sûreté, c’est-à-dire minimisant le nombre d’erreurs. Parallèlement,
on montre l’universalité et les limites de la notion de calculabilité. La récursivité est une méthode fondamen-
tale de programmation. Son introduction permet également de diversifier les algorithmes étudiés. En classe
terminale, les élèves s’initient à différents paradigmes de programmation pour ne pas se limiter à une démarche
impérative.

Numérique et Sciences informatiques Terminale 103 sur 105 Sandrine Piquard


CHAPITRE 8. PROGRAMME OFFICIEL

Contenus Capacités attendues Commentaires


Notion de programme en tant que Comprendre que tout programme L’utilisation d’un interpréteur
donnée. est aussi une donnée. ou d’un compilateur, le
Calculabilité, décidabilité. Comprendre que la calculabilité téléchargement de logiciel, le
ne
dépend pas du langage de fonctionnement des systèmes
programmation utilisé. d’exploitation permettent de
Montrer, sans formalisme comprendre un programme
théorique, que le problème de comme donnée d’un autre
l’arrêt est indécidable. programme.
Récursivité. Écrire un programme récursif. Des exemples relevant de
Analyser le fonctionnement d’un domaines variés sont à
programme récursif. privilégier.
Modularité. Utiliser des API (Application
Programming Interface) ou des
bibliothèques.
Exploiter leur documentation.
Créer des modules simples et les
documenter.
Paradigmes de programmation. Distinguer sur des exemples les Avec un même langage de
paradigmes impératif, fonctionnel programmation, on peut
et objet. utiliser des paradigmes
Choisir le paradigme de différents. Dans un même
programmation selon le champ programme, on peut utiliser
d’application d’un programme. des paradigmes différents.
Mise au point des programmes. Dans la pratique de la On prolonge le travail
Gestion des bugs. programmation, savoir répondre entrepris en classe de
aux causes typiques de bugs : première sur l’utilisation de la
problèmes liés au typage, effets de spécification, des assertions,
bord non désirés, débordements de la documentation des
dans les tableaux, instruction programmes et de la
conditionnelle non exhaustive, construction de jeux de tests.
choix des inégalités, comparai- Les élèves apprennent
sons
et calculs entre flottants, mauvais progressivement à anticiper
nommage des variables, etc. leurs erreurs.

8.2.6 Algorithmique
Le travail de compréhension et de conception d’algorithmes se poursuit en terminale notamment via l’intro-
duction des structures d’arbres et de graphes montrant tout l’intérêt d’une approche récursive dans la résolution
algorithmique de problèmes. On continue l’étude de la notion de coût d’exécution, en temps ou en mémoire et
on montre l’intérêt du passage d’un coût quadratique en n2 à n log 2 n ou de n à log 2 n. Le logarithme en base
2 est ici manipulé comme simple outil de comptage (taille en bits d’un nombre entier).

Numérique et Sciences informatiques Terminale 104 sur 105 Sandrine Piquard


CHAPITRE 8. PROGRAMME OFFICIEL

Contenus Capacités attendues Commentaires


Algorithmes sur les arbres binaires Calculer la taille et la hauteur Une structure de données
d’un
et sur les arbres binaires de arbre. récursive adaptée est utilisée.
recherche. Parcourir un arbre de différentes L’exemple des arbres permet
façons (ordres infixe, préfixe ou d’illustrer la programmation
suffixe ; ordre en largeur par classe.
d’abord).
Rechercher une clé dans un arbre La recherche dans un arbre de
de recherche, insérer une clé. recherche équilibré est de
coût logarithmique.
Algorithmes sur les graphes. Parcourir un graphe en profon- Le parcours d’un labyrinthe et
deur
d’abord, en largeur d’abord. le routage dans internet sont
Repérer la présence d’un cycle des exemples d’algorithme sur
dans un graphe. les graphes.
Chercher un chemin dans un L’exemple des graphes permet
graphe. d’illustrer l’utilisation des
classes en programmation.
Méthode « diviser pour régner ». Écrire un algorithme utilisant la La rotation d’une image
méthode « diviser pour régner ». bitmap d’un quart de tour
avec un coût en mémoire
constant est un bon exemple.
L’exemple du tri fusion permet
également d’exploiter la
récursivité et d’exhiber un
algorithme de coût en
n log2 n dans les pires des cas.
Programmation dynamique. Utiliser la programmation Les exemples de l’alignement
dynamique pour écrire un de séquences ou du rendu de
algorithme. monnaie peuvent être
présentés.
La discussion sur le coût en
mémoire peut être
développée.
Recherche textuelle. Étudier l’algorithme de Boyer- L’intérêt du prétraitement du
Moore pour la recherche d’un motif est mis en avant.
motif dans un texte. L’étude du coût, difficile, ne
peut être exigée.

Numérique et Sciences informatiques Terminale 105 sur 105 Sandrine Piquard

Vous aimerez peut-être aussi