Vous êtes sur la page 1sur 55

Classes Préparatoires aux Grandes Ecoles (CPGE)

BDR
Cours de
Programmation
Avancée en
Python

Base de
Données
Relationnelles

Enseignant-Chercheur en Informatique à l’INP-HB


Laboratoire de Recherche en Informatique et Télécommunication (LARIT)
DFR : Maths – Info
kpo.loua@inphb.ci / 00225 40 28 31 08

05 Septembre 2019

TS STIC 1
2018 - 2019
A Celui qui peut tout

Et qui me rend capable de tout,

Mon bien-aimé Père !


Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Table des matières


PARTIE 1 : NOTION DE BASE DE DONNEES ................................................................................................. 4
Chapitre 1 : Bases de données relationnelles (6H) ................................................................................ 6
1.1. Principes ................................................................................................................................. 6
1.1.2. Enjeux ................................................................................................................................. 6
1.2. L’architecture d’une base de données ................................................................................... 7
1.3. Le modèle relationnel............................................................................................................. 8
1.4. Algèbre relationnelle ............................................................................................................ 11
TD n°1 - Algèbre relation et les requêtes ----------------------------- (2H) ............................................. 18
Chapitre 2 : Le langage SQL (8H) .......................................................................................................... 21
2.1. Définition .............................................................................................................................. 21
TP n°1 – Environnement de l’outil MySQL -------------------------------(1H) .......................................... 31
TP n°2 – Manipulation de requêtes SQL, interface avec Python ------------- (1H) ............................. 31
PARTIE 2 : ALGORITHME AVANCE ............................................................................................................ 32
Chapitre 3 : Complexité algorithmique (8H) ........................................................................................ 34
3.1. Introduction .......................................................................................................................... 34
3.2. Motivation ............................................................................................................................ 34
3.3. Complexité et notation O ..................................................................................................... 35
3.3.2. Notation de Landau .......................................................................................................... 36
3.4. Comment mesurer la complexité d'un algorithme .............................................................. 36
3.5. Différentes nuances de complexité ...................................................................................... 38
TDn°2 – Manipulation des ordres de grandeurs, performance d’un algorithme, calcul de
complexité ------------------------------------ (4H) ................................................................................... 38
Chapitre 4 : Notion de pile (2H)............................................................................................................ 39
4.1. Rappels et compléments sur les listes.................................................................................. 39
4.2. Manipulation des piles ......................................................................................................... 39
4.3. Création d’une pile ............................................................................................................... 39
4.4. Applications .......................................................................................................................... 41
Chapitre 5 : Récursivité (8H)................................................................................................................. 42
5.1. Introduction .......................................................................................................................... 42
5.2. Fonction récursive ................................................................................................................ 42
5.3. Complexité d’un algorithme récursif.................................................................................... 43
TDn°3 – Manipulation des piles, notions de récursivité, complexité----------- (4H).......................... 45
5.4. Résolution des récurrences .................................................................................................. 45

1
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

TPn°3 – Simulation de piles ----------------------------------------- (2H) ................................................... 45


Chapitre 6 : Algorithmes de tri (10H) ................................................................................................... 46
6.1. Introduction .......................................................................................................................... 46
6.2. Algorithmes de tri par insertion ........................................................................................... 46
6.3. Algorithme de tri rapide ....................................................................................................... 48
6.4. Algorithme du Tri par fusion ................................................................................................ 50
TDn°4 – Tri rapide, tri fusion, complexité de tri avancé ------------------ (4H) .................................... 53
TPn°5 – Implémentation de Tris avancés : rapide, tri fusion ------------- (2H) ................................... 53

2
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Objectifs
▪ Gérer des données de bases structurées ou non
▪ Approfondir les connaissances en langage de programmation évolué et moderne

3
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

PARTIE 1 : NOTION DE BASE DE


DONNEES

4
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Savoir-faire :
▪ Comprendre et décrire les rôles des différents éléments d'une architecture trois
tiers
▪ Recourir aux concepts des bases de données relationnelles ;
▪ Traduire les questions posées dans un langage de requête en respectant sa syntaxe
▪ Prototyper et créer une base de données simple, à l’aide d’un outil interactif
▪ Consulter une base de données à travers des requêtes de type SQL ;

5
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Chapitre 1 : Bases de données relationnelles (6H)

Durée : CM --------------------------------------------------------------------------------- (4H)

1.1. Principes

1.1.1. Définition

Il est difficile de donner une définition exacte de la notion de base de données. Une
définition très générale pourrait être :

Une Base de données est un ensemble organisé d'informations avec un objectif commun.

Peu importe le support utilisé pour rassembler et stocker les données (papier, fichiers,
etc.), dès lors que des données sont rassemblées et stockées d'une manière organisée dans
un but spécifique, on parle de base de données.

Plus précisément, on appelle base de données un ensemble structuré et organisé permettant


le stockage de grandes quantités d'informations afin d'en faciliter l'exploitation (ajout,
mise à jour, recherche de données). Bien entendu, dans le cadre de ce cours, nous nous
intéressons aux bases de données informatisées.

1.1.2. Enjeux

Les bases de données ont pris une place importante en informatique, et particulièrement
dans le domaine de la gestion. L'étude des bases de données a conduit au développement
de concepts, méthodes et algorithmes spécifiques, notamment pour gérer les données en
mémoire secondaire (i.e. disques durs). En effet, dès l'origine de la discipline, les
informaticiens ont observé que la taille de la RAM ne permettait pas de charger l'ensemble
d'une base de données en mémoire. Cette hypothèse est toujours vérifiée, car le volume
des données ne cesse de s'accroître sous la poussée des nouvelles technologies du WEB.

Ainsi, les bases de données de demain devront être capables de gérer plusieurs dizaines de
Téra-Octets de données, géographiquement distribuées à l'échelle d'Internet, par
plusieurs dizaines de milliers d'utilisateurs dans un contexte d'exploitation changeant (on
ne sait pas très bien maîtriser ou prédire les débits de communication entre sites) voire
sur des nœuds volatiles. En physique des hautes énergies, on prédit qu'une seule expérience
produira de l'ordre du pétaoctet de données par an.
Comme il est peu probable de disposer d'une technologie de disque permettant de stocker
sur un unique disque cette quantité d'informations, les bases de données se sont orientées
vers des architectures distribuées ce qui permet, par exemple, d'exécuter potentiellement

6
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

plusieurs instructions d'entrée/sortie en même temps sur des disques différents et donc
de diviser le temps total d'exécution par un ordre de grandeur.

1.2. L’architecture d’une base de données

En règle générale, une application peut aisément se diviser en trois niveaux distincts : les
données, le traitement de ces données et leur affichage.

La couche de donnée regroupe le stockage et les mécanismes d’accès des données de façon
qu’elle soit utilisable par l’application au niveau traitement.
La couche de traitement concerne à la fois les tâches à réaliser par l’application sur les
données et les traitements nécessaires pour donner suite à une action de l’utilisateur.
Enfin, la couche de présentation qui permet l’interaction entre l’utilisateur et l’application.
Ces trois niveaux pouvant être imbriqués ou repartis, leur découpage et leur répartition
permettent de distinguer les architectures applicatives suivantes :
– L’architecture un tiers,
– L’architecture deux tiers,
– L’architecture trois tiers.

1.2.1. L’architecture client-serveur (deux tiers)

Dans une architecture deux tiers ou client-serveur de données, le poste client se contente
de déléguer la gestion des données à un service spécialisé. Ce type d’application permet de
tirer parti de la puissance des ordinateurs déployés en réseau pour fournir à l'utilisateur
une interface riche, tout en garantissant la cohérence des données, qui restent gérées de
façon centralisée.

1.2.2. Architecture trois tiers

L’architecture trois tiers applique les principes suivants :


– Les données sont toujours gérées de façon centralisée ;

7
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

– La présentation est toujours prise en charge par le poste client ;


– La logique applicative est prise en charge par un serveur intermédiaire.
Par conséquent, cette architecture appelée encore client-serveur de deuxième génération
ou client-serveur distribué, sépare l’application en trois niveaux de service distinct :

▪ Premier niveau (Couche présentation) : Elle correspond à la partie de l'application


visible et interactive avec les utilisateurs. On parle d'interface homme-machine. La
couche présentation relaie les requêtes de l'utilisateur à destination de la couche
métier, et en retour lui présente les informations renvoyées par les traitements de
cette couche.

▪ Deuxième niveau (Couche métier ou business) : Elle correspond à la partie


fonctionnelle de l'application, celle qui implémente la « logique », et qui décrit les
opérations que l'application opère sur les données en fonction des requêtes des
utilisateurs effectuées au travers de la couche présentation. Les différentes règles
de gestion et de contrôle du système sont mises en œuvre dans cette couche.

▪ Troisième niveau (Couche accès aux données) : Elle consiste en la partie gérant
l’accès aux données qui sont destinées à être conservées sur la durée, voire de
manière définitive.

1.3. Le modèle relationnel

Une base de données relationnelle est une base de données structurée suivant les principes
de l'algèbre relationnelle.

Le père des bases de données relationnelles est Edgar Frank Codd. Chercheur chez IBM à
la fin des années 1960, il étudiait alors de nouvelles méthodes pour gérer de grandes
quantités de données, car les modèles et les logiciels de l'époque ne le satisfaisaient pas.
Mathématicien de formation, il était persuadé qu'il pourrait utiliser des branches
spécifiques des mathématiques (la théorie des ensembles et la logique des prédicats du
premier ordre) pour résoudre des difficultés telles que la redondance des données,
l'intégrité des données ou l'indépendance de la structure de la base de données avec sa
mise en œuvre physique.

8
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

En 1970, il publia un article où il proposait de stocker des données hétérogènes dans des
tables, permettant d'établir des relations entre elles. De nos jours, ce modèle est
extrêmement répandu, mais en 1970, cette idée était considérée comme une curiosité
intellectuelle. On doutait que les tables puissent être jamais gérées de manière efficace
par un ordinateur.

Ce scepticisme n'a cependant pas empêché Codd de poursuivre ses recherches. Un premier
prototype de Système de gestion de bases de données relationnelles (SGBDR) a été
construit dans les laboratoires d'IBM. Depuis les années 80, cette technologie a mûri et a
été adoptée par l'industrie. En 1987, le langage SQL, qui étend l'algèbre relationnelle, a
été standardisé.
C'est dans ce type de modèle que se situe ce cours de base de données.

1.3.1. Exemple introductif : Gestion de bibliothèque

Le responsable de la bibliothèque des CPGE peut être tenté de gérer les emprunts par un
simple fichier sur un tableur, par exemple Excel. Les premières lignes de ce fichier peuvent
ressembler à ce qui suit :

On peut tout de suite relever les différentes difficultés avec cette organisation. Elles sont
les suivantes :
– l'orthographe des différents noms de famille peut manquer de cohérence;
– l'orthographe des différents titres des ouvrages peut manquer de cohérence : Mme ou
Madame Bovary ?
– la recopie des numéros de téléphone est fastidieuse et comporte des risques d'erreur ;
– même l'entrée de la date peut poser problème (les autres emprunts du même
emprunteur).
– personne différente portant le même nom
– mise à jour d’une donnée de l’emprunteur
– et si le fichier comportait des milliers de lignes !

9
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

La séparation d’objets reliés permet une avancée importante : Les modifications sont
maintenant plus simples à gérer.

Dans le modèle relationnel, les données sont organisées en tableaux à deux dimensions qui
s’appellent relations.

La table des Livres comporte une colonne identifiant le livre, puis une autre identifiant
l'auteur par son numéro, et de même pour le genre. Les titres ne sont pas répétés, ce qui
garantit la cohérence des données.

Table des emprunteurs : ici, la non-répétition des


abonnés de la bibliothèque permet de garantir la
cohérence des données La table des Emprunts permet de
trouver le numéro de l'emprunteur
et celui de l'ouvrage emprunté. La
date de rendu est éventuellement
NULL si l'ouvrage est encore
dehors.

10
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

1.3.2. Le système de gestion de base de données relationnelles

Les bases de données (BD) sont généralement gérées par un système appelle Système de
gestion de base de données relationnelles (SGBDR). C’est un système qui permet de gérer
une BD partagée par plusieurs utilisateurs simultanément

Le SGBD permet de :
Décrire les données indépendamment des applications (de manière intrinsèque) avec un
langage de définition des données DATA DEFINITION LANGUAGE (DDL)
Manipuler les données : interroger et mettre à jour les données sans préciser
d'algorithme d'accès. Dire QUOI sans dire COMMENT à l’aide d’un langage de requêtes
déclaratif.
Exemple : quels sont les noms des produits de prix inférieur à 100 F ?
On parle de langage de manipulation des données DATA MANIPULATION
LANGUAGE (DML)
Contrôler les données (intégrité) : vérification de contraintes d'intégrité
Exemple : personne dont le salaire compris entre 400F et 20000 F

1.4. Algèbre relationnelle

1.4.1. Définition

L’algèbre relationnelle est le langage de manipulation qu’utilise le SGBDR pour effectuer


des opérations sur les relations (tables).

Les requêtes SQL (Strutured Query Language) soumisent par l’utilisateur sont traduites
par le SGBD en opérations de l’algèbre relationnelle.

SQL Algèbre relationnelle

Utilisateur BD
SGBDR

L’algèbre relationnelle est un ensemble d’opérateurs qui prennent en entrée des relations
et qui produisent en sortie des relations.

Objectif : localiser des données dans la base qui répondent à un certain critère ou aux
besoins de l’utilisateur.

11
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

1.4.2. Terminaison structurelle

Les objets de base souvent référencés dans la modélisation relationnelle sont les domaines,
les relations, les attributs, les degrés et les tuples.

Domaine :
Le domaine représente un ensemble fini de valeur possible pour un attribut donné auquel
on définit aussi un ensemble d’opérateurs pouvant être appliqués aux valeurs du domaine.

Tuple
Grossièrement, un tuple est un enregistrement (une ligne) dans la base de données.
Plus formellement, un tuple est un élément atomique comportant un entête et un corps.
L'entête est un ensemble des noms d’attributs et de leurs domaines et le corps est un
ensemble de triplets <Nom du domaine, Nom d’attribut, Valeur>

Attribut :
Un attribut est simplement la valeur associée à un des triplets d’un tuple.

Clé candidate :
Une clé candidate est un ensemble des données (attribut) permettant d’indexer chaque
ligne d’une table donnée de manière différenciée. On utilise souvent une ou plusieurs clés
candidates comme clé primaire.

Une clé primaire :


Une clé primaire est une clé candidate qui identifie de manière unique un tuple. Elle est
généralement soulignée.

12
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Une clé étrangère :


Une clé étrangère est un attribut qui fait référence à une clé primaire d’une autre relation.
Elle est précédée d’un dièse (#).

Relation
Une relation (ou table) est un élément constitué d’un entête et d’un corps. L’entête est un
ensemble des noms d’attributs et de leurs domaines et le corps est un ensemble de tuples
ayant le même entête que la relation.

Soient : R : une relation ; Att1, Att2, … , Attn : des attributs ; D1, D2, … , Dn : les domaines
des attributs respectifs.

Alors on note R(Att1 : D1, Att2 : D2, … , Attn : Dn) la relation R ;

Ainsi la relation voiture sera notée Voiture(marque, couleur, plaque)


Par soucis de présentation nous retenons la notation suivante :
Voiture(plaque, couleur, marque)
– Voiture est le nom de la relation
– Plaque, couleur, marque sont les attributs de la relation
– Plaque est la clé primaire car le numéro de la plaque identifie de manière unique une
voiture.

Attention à ne pas confondre avec le concept de relation entre les tables.

1.4.3. Les opérations de l’algèbre relationnelle

L’algèbre relationnelle possède huit opérateurs : certains opérateurs sont ensemblistes


(selon la théorie des ensembles), d’autres sont relationnels (spécifiques à l’algèbre
relationnelle). On peut aussi classer les opérateurs selon qu’ils s’appliquent à une ou à
plusieurs relations (tables).

Opérations à un seul opérande : Opérations à deux opérandes :


– Sélection (opérateur relationnel) – Produit cartésien (opérateurs ensemblistes)
– Projection (opérateur relationnel) – Jointure (opérateur relationnel)
– Union (opérateurs ensemblistes)
– Intersection (opérateurs ensemblistes)
– Différence (opérateurs ensemblistes)
– Division (opérateur relationnel)

L'algèbre relationnelle est un langage d’interrogation des bases de données relationnelles.


Parfois, pour parvenir à extraire les données voulues, il faut effectuer plusieurs opérations.
Dans ce cas, le résultat de la première opération est utilisé dans la deuxième opération, et
le résultat de la deuxième opération peut être utilisé dans la troisième opération, …

13
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

1.4.4. Les opérateurs relationnels

Restriction (Sélection) σ
La sélection consiste à extraire d’une relation les occurrences (lignes) satisfaisant au(x)
critère(s) de sélection.
 Formalisme : R2 = SELECTION (R1/ C(Att)) où C(Att) est le critère de sélection sur
le(s) attribut(s) Att.
Critères de sélection :
▪ Opérateurs de comparaison : <, <=, =, >, >=, ? (Entre un champ ou attribut et une
valeur)
▪ Opérateurs logiques : ET, OU (entre deux comparaison)
▪ NON (pour renverser la comparaison)

Soit la table FILM qui permet de gérer une vidéothèque.

FILM(no-film, titre, duree, production, #code-categorie)

Requête : On aimerait avoir les films de durée supérieure à 115.

Réponse : R = σduree > 115(FILM) Ou


R = SELECTION (FILM/ duree > 115) (c’est mieux ainsi !)

Projection Π
La projection d'une relation consiste en la mise en place d'une nouvelle relation en ne
retenant que certaines colonnes (attributs) et en supprimant les occurrences en double.
 Formalisme : R2 = PROJECTION (R1/colonne 1, colonne 2, …)
R2 est la table résultat, R1 est la table utilisée par la projection. Pas de duplication des
occurrences.

On aimerait avoir les titres et productions de tous les films.


▪ Π titre, production (FILM)
▪ Ou Projection (FILM/ titre, production)

14
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Division /
La division permet de trouver les occurrences d’une table qui sont associées à toutes les
occurrences d’une autre table (qui le plus souvent est le résultat d’une sélection).
 Formalisme : R = DIVISION (dividende, diviseur) ou R = dividende / diviseur
Attention au sens, chers Prépas !!!

DIVISION(PARTICIPER, EPREUVE)

Le nom des athlètes qui ont participé aux épreuves de 200 m et de 400 m et de 110 m H.

Jointure
La jointure consiste à créer une nouvelle table à partir de deux tables ayant un champ
commun (attribut) et vérifiant un critère de jointure.
 Formalisme :
R3 = JOINTURE(R1, R2/R1.attr_jointure op_comparaison R2.attr_jointure)

Cours Notes

nom_Cours Professeur coefficient Etudiant nom_Cours note


Gestion de projet Gnaba Carine 2 Kouadah Gestion de projet 12
Base de données KPO Loua 4 Bruken Base de données 14
Analyse Wakablet 2 Ichmed Analyse 12
Data mining Adingra 3 Toto Data mining 13

La jointure entre les deux tables Cours et Notes se fait à travers l’attribut Nom_cours
▪ JOINTURE(Cours, Notes/ Cours.nom_cours = Notes.nom_cours)

Cours.nom_Cours Professeur coefficient Etudiant Notes.nom_Cours note


Gestion de projet Gnaba Carine 2 Kouadah Gestion de projet 12
Base de données KPO Loua 4 Bruken Base de données 14
Analyse Wakablet 2 Ichmed Analyse 12
Data mining Adingra 3 Toto Data mining 13

15
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

1.4.5. Les opérateurs assemblâtes

Les opérateurs ensemblistes sont les mêmes qu’en mathématiques, dans la théorie des
ensembles. Vous avez certainement vu ce cours en MPSI.

Union (U)
L’union de deux tables est l'ensemble des occurrences qui appartiennent soit à la première
table, soit à la deuxième, soit aux deux tables. C’est la traduction du OU logique.
Remarque : pas de duplication des n-uplets.
Formalisme : Soient R1 et R2 deux relations. On a : R = R1 U R2 ou R = UNION (R1 , R2)

Intersection (∩ )
L'intersection de deux relations est l'ensemble des occurrences qui sont présentes dans
les deux relations. C’est la traduction du ET logique.
 Formalisme : R = R1 ∩ R2 ou R = INTERSECTION (R1, R2)

16
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Différence –
La différence entre deux tables est l'ensemble des occurrences qui appartiennent à une
table sans appartenir à la seconde. Le but est d’obtenir l’ensemble des tuples d’une relation
qui ne figurent pas dans une autre. Attention, cette opération a un sens.
 Formalisme : R = R1 - R2 ou R = DIFFERENCE (R1, R2). Opération non commutative.

S-R

Noms des élèves qui ne portent pas


le nom d’un prof : Résu = Eleve2-Prof

Produit cartésien x
Le produit cartésien de deux tables consiste à combiner toutes les possibilités
d’associations d’occurrences des deux tables. Chaque ligne de R1 sera concaténée à chaque
ligne de R2.
 Formalisme : R = R1 x R2 ou R = PRODUIT (R1, R2)

Soit la base de données suivante :


Eleve(Num, Nom, Adresse, Age)
UV(Code, Nbh, Coord)

17
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

TD n°1 - Algèbre relation et les requêtes ----------------------------- (2H)

On considère la base de données BD AIRBASE suivante :

PILOTE (numpil : d_numpil, nompil: d_nompil, adr : d_ville, sal : d_sal)


AVION (numav : d_numav, nomav : d_nomav, cap : d_cap, loc : d_ville)
VOL (numvol : d_numvol, #numpil : d_numpil, #numav : d_numav, ville_dep : d_ville, ville_arr
: d_ville, h_dep : d_heure, h_arr : d_heure)

On pourra l’écrire plus simplement de la manière suivante :


PILOTE (numpil, nompil, adr, sal )
AVION (numav, nomav, cap, loc )
VOL (numvol, #numpil, #numav, ville_dep, ville_arr, h_dep, h_arr)

Convention : Les clés primaires sont soulignées et les clés étrangères sont précédées d’un
dièse (#).

TAF : Donner les réponses aux requêtes ci-dessous en algèbre relationnelle.

Exercice 1 : Expression des projections et sélections

Q1 : Donnez la liste des avions dont la capacité est supérieure à 350 passagers.
R1 = ……………………………………………………………………………………………………………………………………………………

Q2 : Quels sont les numéros et noms des avions localisés à Nice ?


R2.1 = ……………………………………………………………………………………………………………………………………………………
R2 = ……………………………………………………………………………………………………………………………………………………

Q3 : Quels sont les numéros des pilotes en service et les villes de départ de leurs vols ?
R3 = ……………………………………………………………………………………………………………………………………………………

Q4 : Donnez toutes les informations sur les pilotes de la compagnie.


R4 = ……………………………………………………………………………………………………………………………………………………

Q5 : Quel est le nom des pilotes domiciliés à Paris dont le salaire est supérieur à 15000 F
?

18
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

R5.1 = ……………………………………………………………………………………………………………………………………………………
R5.2 = ……………………………………………………………………………………………………………………………………………………
R5 = ……………………………………………………………………………………………………………………………………………………

Exercice 2 : Utilisation des opérateurs ensemblistes

Q6 : Quels sont les avions (numéro et nom) localisés à Nice ou dont la capacité est
inférieure à 350 passagers ?
R6.1 = ……………………………………………………………………………………………………………………………………………………
R6.2 = ……………………………………………………………………………………………………………………………………………………
R6 = ……………………………………………………………………………………...………………………………………………………………

Q7 : Liste des vols au départ de Nice allant à Paris après 18 heures ?


R7.1 = ……………………………………………………………………………………………………………………………………………………
R7.2 = ……………………………………………………………………………………………………………………………………………………
R7.3 = ……………………………………………………………………………………………………………………………………………………
R7.4 = ……………………………………………………………………………………………………………………………………………………
R7 = ……………………..………………………………………………………………………………………………………………………………

Q8 : Quels sont les numéros des pilotes qui ne sont pas en service ?
R8.1 = ……………………………………………………………………………………………………………………………………………………
R8.2 = ……………………………………………………………………………………………………………………………………………………
R8 = ………………..……………………………………………………………………………………………………………………………………

Q9 : Quels sont les vols (numéro, ville de départ) effectués par les pilotes de numéro 100
et 204 ?
R9.1 = ……………………………………………………………………………………………………………………………………………………
R9.2 = ……………………………………………………………………………………………………………………………………………………
R9.3 = ……………………………………………………………………………………………………………………………………………………
R9 = …………..…………………………………………………………………………………………………………………………………………

Exercice 3 : Expression des jointures

Q10 : Donnez le numéro des vols effectués au départ de Nice par des pilotes Niçois ?
R10.1 = ……………………………………………………………………………………………………………………………………………………
R10.2 = ……………………………………………………………………………………………………………………………………………………
R10 = …………………..…………………………………………………………………………………………………………………………………

Q11 : Quels sont les vols effectués par un avion qui n'est pas localisé à Nice ?
R11.1 = ……………………………………………………………………………………………………………………………………………………
R11.2 = ……………………………………………………………………………………………………………………………………………………
R11 = ………..……………………………………………………………………………………………………………………………………………

Q12 : Quels sont les pilotes (numéro et nom) assurant au moins un vol au départ de Nice
avec un avion de capacité supérieure à 300 places ?
R12.1 = ……………………………………………………………………………………………………………………………………………………
R12.2 = ……………………………………………………………………………………………………………………………………………………
R12.3 = ……………………………………………………………………………………………………………………………………………………
R12 = ………………….……………………………………………………………………………………………………………………………………

Q13 : Quels sont les noms des pilotes domiciliés à Paris assurant un vol au départ de Nice
avec un Airbus ?

19
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

R13.1 = ……………………………………………………………………………………………………………………………………………………
R13.2 = ……………………………………………………………………………………………………………………………………………………
R13.3 = ……………………………………………………………………………………………………………………………………………………
R13 = ……………………………………….………………………………………………………………………………………………………………

Q14 : Quels sont les numéros des vols effectués par un pilote Niçois au départ ou à
l'arrivée de Nice avec un avion localisé à Paris ?
R14.1 = ……………………………………………………………………………………………………………………………………………………
R14.2 = ……………………………………………………………………………………………………………………………………………………
R14.3 = ……………………………………………………………………………………………………………………………………………………
R14.4 = ……………………………………………………………………………………………………………………………………………………
R14.5 = ……………………………………………………………………………………………………………………………………………………
R14 = ……………………………………………………………………………………………………………………………………………………

Q15 : Quels sont les pilotes (numéro et nom) habitant dans la même ville que le pilote Dupont
?
Q16 : Quels sont les numéros des pilotes en service différents de celui de Durand ?
Q17 : Quelles sont les villes desservies à partir de la ville d'arrivée d'un vol au départ de
Paris ?
Q18 : Quels sont les appareils (leur numéro) localisés dans la même ville que l'avion numéro
100 ?

Exercice 4 : Divers

Q19 : Quels sont les numéros et noms des pilotes domiciliés dans la même ville que le pilote
Dupont et dont le salaire est supérieur à celui de Dupont ?
Q20 : Quels sont les numéros et noms des pilotes qui effectuent un vol au départ de leur
ville de résidence ?
Q21 : Y a-t-il des homonymes parmi les pilotes ? Si oui, donner leur numéro et nom.
Q22 : Quels sont les pilotes bordelais ne pilotant que des avions arrivant au moins une fois
à Bordeaux ?

20
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Chapitre 2 : Le langage SQL (8H)

2.1. Définition

Le SQL (Structured Query Language, traduit en français par Langage de requêtes


structurées) est un langage permettant de communiquer avec une base de données. Ce
langage informatique est notamment très utilisé par les développeurs web pour
communiquer avec les données d’un site web. SQL.sh recense des cours de SQL et des
explications sur les principales commandes pour lire, insérer, modifier et supprimer des
données dans une base.

SQL est un langage de définition de données (LDD, ou en anglais DDL Data Definition
Language), un langage de manipulation de données (LMD, ou en anglais DML, Data
Manipulation Language), et un langage de contrôle de données (LCD, ou en anglais DCL, Data
Control Language), pour les bases de données relationnelles.

2.1.1. Les requêtes en SQL

La projection (SELECT………FROM)
L’utilisation la plus courante de SQL consiste à lire des données issues de la base de
données. Cela s’effectue grâce à la commande SELECT, qui retourne des enregistrements
dans un tableau de résultat. Cette commande peut sélectionner une ou plusieurs colonnes
d’une table.

Syntaxe :

SELECT nom_du_champ FROM nom_du_tableau ;

SQL AS (alias)
Dans le langage SQL il est possible d’utiliser des alias pour renommer temporairement une
colonne ou une table dans une requête. Cette astuce est particulièrement utile pour
faciliter la lecture des requêtes.

Alias sur une colonne Permet de renommer le nom d’une colonne dans les résultats d’une
requête SQL. C’est pratique pour avoir un nom facilement identifiable dans une application
qui doit ensuite exploiter les résultats d’une recherche

Syntaxe :

SELECT colonne1 AS c1, colonne2 FROM `table`

La selection (SELECT…….FROM…………..WHERE)

21
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

SQL WHERE La commande WHERE dans une requête SQL permet d’extraire les lignes
d’une base de données qui respectent une condition. Cela permet d’obtenir uniquement les
informations désirées.

Syntaxe : La commande WHERE s’utilise en complément à une requête utilisant SELECT.


La façon la plus simple de l’utiliser est la suivante :

SELECT nom_colonnes
FROM nom_table
WHERE condition ;

Opérateur Description

= Égale

Pas égale

!= Pas égale

> Supérieur à

< Inférieur à

>= Supérieur ou égale à

<= Inférieur ou égale à

IN Liste de plusieurs valeurs possibles

BETWEEN Valeur comprise dans un intervalle donnée (utile


pour les nombres ou dates)

LIKE Recherche en spécifiant le début, milieu ou fin


d'un mot.

IS NULL Valeur est nulle

IS NOT Valeur n'est pas nulle


NULL

SQL AND & OR


Une requête SQL peut être restreinte à l’aide de la condition WHERE. Les opérateurs
logiques AND et OR peuvent être utilisées au sein de la commande WHERE pour combiner
des conditions. Syntaxe d’utilisation des opérateurs AND et OR Les opérateurs sont à
ajoutés dans la condition WHERE. Ils peuvent être combinés à l’infini pour filtrer les

22
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

données comme souhaités. L’opérateur AND permet de s’assurer que la condition1 ET la


condition2 sont vrai :

SELECT nom_colonnes
FROM nom_table
WHERE condition1 AND condition2

L’opérateur OR vérifie quant à lui que la condition1 OU la condition2 est vrai :

SELECT nom_colonnes FROM nom_table


WHERE condition1 OR condition2

Ces opérateurs peuvent être combinés à l’infini et mélangés. L’exemple ci-dessous filtre les
résultats de la table « nom_table » si condition1 ET condition2 OU condition3 est vrai :

SELECT nom_colonnes FROM nom_table


WHERE condition1 AND (condition2 OR condition3)

Attention : il faut penser à utiliser des parenthèses lorsque c’est nécessaire. Cela permet
d’éviter les erreurs car et ça améliore la lecture d’une requête par un humain.

SQL IN
L’opérateur logique IN dans SQL s’utilise avec la commande WHERE pour vérifier si une
colonne est égale à une des valeurs comprises dans set de valeurs déterminés. C’est une
méthode simple pour vérifier si une colonne est égale à une valeur OU une autre valeur OU
une autre valeur et ainsi de suite, sans avoir à utiliser de multiple fois l’opérateur OR.
Syntaxe Pour chercher toutes les lignes où la colonne « nom_colonne » est égale à ‘valeur
1′ OU ‘valeur 2′ ou ‘valeur 3′, il est possible d’utiliser la syntaxe suivante :

SELECT nom_colonne
FROM table
WHERE nom_colonne IN ( valeur1, valeur2, valeur3, ... )

SQL BETWEEN
L’opérateur BETWEEN est utilisé dans une requête SQL pour sélectionner un intervalle de
données dans une requête utilisant WHERE. L’intervalle peut être constitué de chaînes de
caractères, de nombres ou de dates. L’exemple le plus concret consiste par exemple à
récupérer uniquement les enregistrements entre 2 dates définies.

Syntaxe L’utilisation de la commande BETWEEN s’effectue de la manière suivante :

SELECT *
FROM table
WHERE nom_colonne BETWEEN 'valeur1' AND 'valeur2'

23
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

SQL LIKE
L’opérateur LIKE est utilisé dans la clause WHERE des requêtes SQL. Ce mot-clé permet
d’effectuer une recherche sur un modèle particulier. Il est par exemple possible de
rechercher les enregistrements dont la valeur d’une colonne commence par telle ou telle
lettre. Les modèles de recherches sont multiples.
Syntaxe La syntaxe à utiliser pour utiliser l’opérateur LIKE est la suivante :

SELECT *
FROM table
WHERE colonne LIKE modele

Dans cet exemple le « modèle » n’a pas été défini, mais il ressemble très généralement à
l’un des exemples suivants : LIKE ‘%a’ : le caractère « % » est un caractère joker qui
remplace tous les autres caractères. Ainsi, ce modèle permet de rechercher toutes les
chaines de caractère qui se termine par un « a ».
o LIKE ‘a%’ : ce modèle permet de rechercher toutes les lignes de « colonne » qui
commence par un « a ».
o LIKE ‘%a%’ : ce modèle est utilisé pour rechercher tous les enregistrements qui
utilisent le caractère « a ».
o LIKE ‘pa%on’ : ce modèle permet de rechercher les chaines qui commence par « pa »
et qui se terminent par « on », comme « pantalon » ou « pardon ».
o LIKE ‘a_c’ : peu utilisé, le caractère « _ » (underscore) peut être remplacé par
n’importe quel caractère, mais un seul caractère uniquement (alors que le symbole
pourcentage « % » peut être remplacé par un nombre incalculable de caractères . Ainsi,
ce modèle permet de retourner les lignes « aac », « abc » ou même « azc ».

SQL IS NULL / IS NOT NULL


Dans le langage SQL, l’opérateur IS permet de filtrer les résultats qui contiennent la valeur
NULL. Cet opérateur est indispensable car la valeur NULL est une valeur inconnue et ne
peut par conséquent pas être filtrée par les opérateurs de comparaison (cf. égal, inférieur,
supérieur ou différent).

Syntaxe Pour filtrer les résultats où les champs d’une colonne sont à NULL il convient
d’utiliser la syntaxe suivante :

SELECT *
FROM `table`
WHERE nom_colonne IS NULL

A l’inverse, pour filtrer les résultats et obtenir uniquement les enregistrements qui ne sont
pas null, il convient d’utiliser la syntaxe suivante :

SELECT *
FROM `table`
WHERE nom_colonne IS NOT NULL

24
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

A savoir : l’opérateur IS retourne en réalité un booléen, c’est à dire une valeur TRUE si la
condition est vraie ou FALSE si la condition n’est pas respectée. Cet opérateur est souvent
utilisé avec la condition WHERE mais peut aussi trouver son utilité lorsqu’une sous-requête
est utilisée.

SQL GROUP BY
La commande GROUP BY est utilisée en SQL pour grouper plusieurs résultats et utiliser
une fonction de totaux sur un groupe de résultat. Sur une table qui contient toutes les
ventes d’un magasin, il est par exemple possible de lister, regrouper les ventes par clients
identiques et d’obtenir le coût total des achats pour chaque client.
Syntaxe d’utilisation de GROUP BY De façon générale, la commande GROUP BY s’utilise de
la façon suivante :

SELECT colonne1, fonction(colonne2)


FROM table
GROUP BY colonne1

A noter : cette commande doit toujours s’utiliser après la commande WHERE et avant la
commande HAVING.

Prenons en considération une table “achat” qui résume les ventes d’une boutique :

id client tarif date

1 Pierre 102 2012-10-23

2 Simon 47 2012-10-27

3 Marie 18 2012-11-05

4 Marie 20 2012-11-14

5 Pierre 160 2012-12-03

Ce tableau contient une colonne qui sert d’identifiant pour chaque ligne, une autre qui
contient le nom du client, le coût de la vente et la date d’achat.
Pour obtenir le coût total de chaque client en regroupant les commandes des mêmes clients,
il faut utiliser la requête suivante :

SELECT client, SUM(tarif)


FROM achat
GROUP BY client

25
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

La fonction SUM() permet d’additionner la valeur de


chaque tarif pour un même client SUM(tarif) client. Le résultat sera donc
le suivant :
Pierre 262

Simon 47

Marie 38

La manière simple de comprendre le GROUP BY c’est tout simplement d’assimiler qu’il va


éviter de présenter plusieurs fois les mêmes lignes. C’est une méthode pour éviter les
doublons.
Juste à titre informatif, voici ce qu’on obtient de la requête sans utiliser GROUP BY.
Requite :

SELECT client, SUM(tarif)


FROM achat

Résultat :

client SUM(tarif)

Pierre 262

Simon 47

Marie 38

Marie 38

Pierre 262

Utilisation d’autres fonctions de statistiques Il existe plusieurs fonctions qui peuvent être
utilisées pour manipuler plusieurs enregistrements, il s’agit des fonctions d’agrégations
statistiques, les principales sont les suivantes :
o AVG() pour calculer la moyenne d’un set de valeur. Permet de connaître le prix du
panier moyen pour de chaque client
o COUNT() pour compter le nombre de lignes concernées. Permet de savoir combien
d’achats a été effectué par chaque client
o MAX() pour récupérer la plus haute valeur. Pratique pour savoir l’achat le plus cher
o MIN() pour récupérer la plus petite valeur. Utile par exemple pour connaître la date
du premier achat d’un client
o SUM() pour calculer la somme de plusieurs lignes. Permet par exemple de connaître
le total de tous les achats d’un client Ces petites fonctions se révèlent rapidement
indispensable pour travailler sur des données.

26
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

SQL HAVING
La condition HAVING en SQL est presque similaire à WHERE à la seule différence que
HAVING permet de filtrer en utilisant des fonctions telles que SUM(), COUNT(), AVG(),
MIN() ou MAX().
Syntaxe L’utilisation de HAVING s’utilise de la manière suivante :

SELECT colonne1, SUM(colonne2)


FROM nom_table
GROUP BY colonne1
HAVING fonction(colonne2) operateur valeur

Cela permet donc de SÉLECTIONNER les colonnes DE la table « nom_table » en


GROUPANT les lignes qui ont des valeurs identiques sur la colonne « colonne1″ et que la
condition de HAVING soit respectée.

Important : HAVING est très souvent utilisé en même temps que GROUP BY bien que ce
ne soit pas obligatoire

Exemple
Pour utiliser un exemple concret, imaginons une table “achat” qui contient les achats de
différents clients avec le coût du panier pour chaque achat.

id client tarif date_achat

1 Pierre 102 2012-10-23

2 Simon 47 2012-10-27

3 Marie 18 2012-11-05

4 Marie 20 2012-11-14

5 Pierre 160 2012-12-03

Si dans cette table on souhaite récupérer la liste des clients qui ont commandé plus de 40€,
toute commandes confondues alors il est possible d’utiliser la requête suivante :

SELECT client, SUM(tarif)


FROM achat
GROUP BY client
HAVING SUM(tarif) > 40

27
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Résultat :

client SUM(tarif)

Pierre 262

Simon 47

La cliente “Marie” a cumulé 38€ d’achat (un achat de 18€ et un autre de 20€) ce qui est
inférieur à la limite de 40€ imposée par HAVING. En conséquent cette ligne n’est pas
affichée dans le résultat.

SQL ORDER BY
La commande ORDER BY permet de trier les lignes dans un résultat d’une requête SQL. Il
est possible de trier les données sur une ou plusieurs colonnes, par ordre ascendant ou
descendant.

Syntaxe Une requête où l’on souhaite filtrer l’ordre des résultats utilise la commande
ORDER BY de la sorte :

SELECT colonne1, colonne2


FROM table
ORDER BY colonne1

Par défaut les résultats sont classés par ordre ascendant, toutefois il est possible
d’inverser l’ordre en utilisant le suffixe DESC après le nom de la colonne. Par ailleurs, il est
possible de trier sur plusieurs colonnes en les séparant par une virgule. Une requête plus
élaborée ressemblerait alors cela :

SELECT colonne1, colonne2, colonne3


FROM table
ORDER BY colonne1 DESC, colonne2 ASC

A noter : il n’est pas obligé d’utiliser le suffixe « ASC » sachant que les résultats sont
toujours classés par ordre ascendant par défaut. Toutefois, c’est plus pratique pour mieux
s’y retrouver, surtout si on a oublié l’ordre par défaut.

En utilisant deux méthodes de tri, il est possible de tri la première colonne puis de trier
une deuxième colonne selon le premier tri.
La requête serait alors la suivante :

SELECT *
FROM utilisateur
ORDER BY nom, date_inscription DESC

28
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Une requête SELECT peut devenir assez longue. Juste à titre informatif, voici une requête
SELECT qui possède presque toutes les commandes possibles :

SELECT *
FROM table
WHERE condition
GROUP BY expression
HAVING condition
{ UNION | INTERSECT | EXCEPT }
ORDER BY expression

SQL UNION
La commande UNION de SQL permet de mettre bout-à-bout les résultats de plusieurs
requêtes utilisant elles-mêmes la commande SELECT. C’est donc une commande qui permet
de concaténer les résultats de 2 requêtes ou plus. Pour l’utiliser il est nécessaire que
chacune des requêtes à concaténer retournes le même nombre de colonnes, avec les mêmes
types de données et dans le même ordre.

A savoir : par défaut, les enregistrements exactement identiques ne seront pas répétés
dans les résultats. Pour effectuer une union dans laquelle même les lignes dupliquées sont
affichées il faut plutôt utiliser la commande UNION ALL.
Syntaxe La syntaxe pour unir les résultats de 2 tableaux sans afficher les doublons est la
suivante :

SELECT * FROM table1


UNION
SELECT * FROM table2

SQL INTERSECT
La commande SQL INTERSECT permet d’obtenir l’intersection des résultats de 2
requêtes. Cette commande permet donc de récupérer les enregistrements communs à 2
requêtes. Cela peut s’avérer utile lorsqu’il faut trouver s’il y a des données similaires sur 2
tables distinctes.

A savoir : pour l’utiliser convenablement il faut que les 2 requêtes retournent le même
nombre de colonnes, avec les mêmes types et dans le même ordre.
Syntaxe La syntaxe à adopter pour utiliser cette commande est la suivante :

SELECT * FROM table1


INTERSECT
SELECT * FROM table2

29
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Dans cet exemple, il faut que les 2 tables soient similaires (mêmes colonnes, mêmes types
et même ordre). Le résultat correspondra aux enregistrements qui existent dans table1 et
dans table2.

SQL EXCEPT / MINUS


Dans le langage SQL la commande EXCEPT s’utilise entre 2 instructions pour récupérer les
enregistrements de la première instruction sans inclure les résultats de la seconde requête.
Si un même enregistrement devait être présent dans les résultats des 2 syntaxes, ils ne
seront pas présent dans le résultat final.

A savoir : cette commande s’appelle différemment selon les Systèmes de Gestion de Base
de Données (SGBD) :
o EXCEPT : PostgreSQL
o MINUS : MySQL et Oracle
Dès lors, il faut remplacer tout le reste de ce cours par MINUS pour les SGBD
correspondants.

Syntaxe La syntaxe d’une requête SQL est toute simple :

SELECT * FROM table1


EXCEPT
SELECT * FROM table2

Cette requête permet de lister les résultats du table 1 sans inclure les enregistrements de
la table 1 qui sont aussi dans la table 2.

Attention : les colonnes de la première requête doivent être similaires entre la première
et la deuxième requête (même nombre, même type et même ordre).

Jointure SQL
Les jointures en SQL permettent d’associer plusieurs tables dans une même requête. Cela
permet d’exploiter la puissance des bases de données relationnelles pour obtenir des
résultats qui combinent les données de plusieurs tables de manière efficace.

Types de jointures
Il y a plusieurs méthodes pour associer 2 tables ensemble. Voici la liste des différentes
techniques qui sont utilisées :
 INNER JOIN : jointure interne pour retourner les enregistrements quand la
condition est vraie dans les 2 tables. C’est l’une des jointures les plus communes.
 CROSS JOIN : jointure croisée permettant de faire le produit cartésien de 2
tables. En d’autres mots, permet de joindre chaque ligne d’une table avec chaque
ligne d’une seconde table. Attention, le nombre de résultats est en général très
élevé.

30
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

 LEFT JOIN (ou LEFT OUTER JOIN) : jointure externe pour retourner tous les
enregistrements de la table de gauche (LEFT = gauche) même si la condition n’est
pas vérifiée dans l’autre table.
 RIGHT JOIN (ou RIGHT OUTER JOIN) : jointure externe pour retourner tous
les enregistrements de la table de droite (RIGHT = droite) même si la condition
n’est pas vérifiée dans l’autre table.
 FULL JOIN (ou FULL OUTER JOIN) : jointure externe pour retourner les
résultats quand la condition est vraie dans au moins une des 2 tables.
 SELF JOIN : permet d’effectuer une jointure d’une table avec elle-même comme
si c’était une autre table.
 NATURAL JOIN : jointure naturelle entre 2 tables s’il y a au moins une colonne
qui porte le même nom entre les 2 tables SQL
 UNION JOIN : jointure d’union

Syntaxes :

Pour aller plus loin voir cours en ligne : https://sql.sh/cours

TP n°1 – Environnement de l’outil MySQL -------------------------------(1H)


TP n°2 – Manipulation de requêtes SQL, interface avec Python ------------- (1H)

31
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

PARTIE 2 : ALGORITHME AVANCE

32
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Savoir-faire :

▪ S’interroger sur l’efficacité algorithmique temporelle d’un algorithme,


▪ Comprendre le fonctionnement d’un algorithme récursif et l’utilisation de la mémoire
lors de son exécution,
▪ Comprendre les avantages et défauts respectifs des approches récursive et
itérative,
▪ Distinguer clairement par leurs complexités deux algorithmes résolvant un même
problème,
▪ Maitriser la notion de diviser pour régner au travers de problèmes classiques tels
que les tris.

33
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Chapitre 3 : Complexité algorithmique (8H)

Durée : CM ----------------------------------------------------------------------------------(2H)

3.1. Introduction

Deux algorithmes produisant les mêmes résultats peuvent être très différents du point de
vu des méthodes utilisées, de leurs complexités apparentes et de leurs efficacités.

La complexité apparente d’un algorithme correspond à la difficulté qu’éprouve son lecteur


à le comprendre. Cette complexité dépend du nombre et de la profondeur d’imbrication
des structures de contrôle. L’efficacité d’un algorithme est liée à la bonne utilisation des
ressources temps de traitement et espace mémoire pour s’exécuter.

Retenons que l’efficacité d’un algorithme est son coût en termes de temps d’exécution et
de mémoire. On évalue l’efficacité d’un algorithme par son coût qui dépend de deux
paramètres essentiels :
- Sa durée d’exécution appelé aussi complexité temporelle qui est liée au nombre
d’opération effectué par l’algorithme
- L’espace mémoire requis appelé aussi complexité spatiale qui est l’encombrement
mémoire.
Le problème que nous nous attelons à résoudre dans ce chapitre est le suivant : de deux
algorithmes résolvant le même problème, lequel choisir ?

3.2. Motivations

Soit à calculer l’équation (E) : 𝑦(𝑥, 𝑛) = 𝑥 𝑛

Méthode 1

𝑦(𝑥, 0) = 1 et ∀ n > 0, y(x, n) = 𝑥 ∗ 𝑦(𝑥, 𝑛 − 1)

def y(x,n) :
if (x !=0 and n == 0) :
return 1
else :
return x*y(x,n-1)

Méthode 2

𝑦(𝑥, 0) = 1
𝑦(𝑥, 𝑛) = y(x, n/2) ∗ y(x, n/2) si n est paire
𝑦(𝑥, 𝑛) = y(x, (n − 1)/2) ∗ y(x, (n − 1)/2) ∗ x si n est impaire

34
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

def y(x,n) :
if (x != 0 ande n == 0) :
return 1
elif n mod 2 == 0 :
return y(x,n/2)* y(x,n/2)
else:
return x*y(x,(n-1)/2)*y(x,(n-1)/2)

Question : des deux méthodes de calcul, laquelle choisir et pourquoi ?

Réponse : la deuxième car elle permet d’effectuer moins de produit que la première.

Exemple : calculons x16

Méthode 1 : x16 = x ∗ x ∗ x ∗ x ∗ x ∗ x ∗ x ∗ … ∗ x ∶ 𝟏𝟓 produits à effectuer

Méthode 2 : x16 = (((x 2 )2 )2 )2 = (((x ∗ x)2 )2 )2 ∶ 𝟒 produits à effectuer

Dans la pratique, la durée d’exécution dépend de la taille et de la valeur des données. Par
exemple, dans le cas d’un algorithme de tri, si les valeurs sont partiellement triées ou trié
à l’envers, le temps de traitement peut varier de façon significative.
On pourra donc définir trois sortes de complexité temporelles :
- La Complexité maximale : C’est le temps d’exécution d’un algorithme dans le cas le plus
défavorable. (Exemple : lorsque le n est très grand)
- La Complexité moyenne : c’est le temps d’exécution moyenne de l’algorithme
- La Complexité dans le meilleur des cas : le temps d’exécution le plus court. On calcul
la complexité dans le meilleur des cas. (Exemple : lorsque la valeur de n vaut 0)

Remarques :
o Dans la pratique, on étudie souvent le pire des cas, ce qui donne une borne supérieure
de la complexité de l’algorithme.
o La complexité maximale et moyenne d’un algorithme sont généralement équivalents.
o Lorsqu’on évalue la complexité d’un algorithme, on ne s’intéresse pas au temps de
calcul réel mais à un ordre de grandeur.

3.3. Complexité et notation O

3.3.1. Définition 2

La complexité d’un algorithme est la mesure du nombre d’opérations fondamentales qu’il


effectue sur un jeu de données. La complexité est exprimée comme une fonction de la taille
du jeu de données.

Nous notons Dn l’ensemble des données de taille n et T(d) le coût de l’algorithme sur la
donnée d.

35
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Complexité au meilleur : Tmin(n) = mind∈Dn C(d). C’est le plus petit nombre d’opérations
qu’aura à exécuter l’algorithme sur un jeu de données de taille fixée, ici à n. C’est une borne
inférieure de la complexité de l’algorithme sur un jeu de données de taille n.

Complexité au pire : Tmax(n) = maxd∈Dn C(d). C’est le plus grand nombre d’opérations qu’aura
à exécuter l’algorithme sur un jeu de données de taille fixée, ici à n.
Avantage : il s’agit d’un maximum, et l’algorithme finira donc toujours avant d’avoir effectué
Tmax(n) opérations.
Inconvénient : cette complexité peut ne pas refléter le comportement « usuel » de
l’algorithme, le pire des cas pouvant ne se produire que très rarement, mais il n’est pas rare
que le cas moyen soit aussi mauvais que le pire des cas.

3.3.2. Notation de Landau

Quand nous calculerons la complexité d’un algorithme, nous ne calculerons généralement


pas sa complexité exacte, mais son ordre de grandeur. Pour ce faire, nous avons besoin de
notations asymptotiques. Soient f et g deux fonctions :

3.4. Comment mesurer la complexité d'un algorithme ?

Pour évaluer la durée d’exécution d’un algorithme, on va définir un modèle fictif de


calculateur dont les caractéristiques sont les suivantes :
- α : temps de lecture, écrire et affectation
- Β : durée de toute opération arithmétique
- µ : durée d’exécution de chaque opération logique

Si dans un algorithme on peut trouver p opération(s) de lecture/sortir, q opération(s)


arithmétique(s) et r opération(s) logique(s) on aura :

T = α*p + β*q + µ*r est le temps d’exécution de l’algorithme.

36
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Exemple 1 :

Coût Nombre
de fois
Procedure TriInsertion(T:Tableau)
Const n  8 : Entier #Taille du tableau
Var
i, j, Temp : Entier
Debut

01 Pour j2 A n Faire c1 n


02 Temp  T[j] c2 n-1
03 i  j – 1 c3 n-1
04 TantQue (i>0 ET T[i]>T[j]) Faire c4 ∑𝑛2 𝑇𝑗
05 T[i+1]  T[i] c5 ∑𝑛2(𝑇𝑗 − 1)
06 i  i-1 c6 ∑𝑛2(𝑇𝑗 − 1)
07 FinTantQue
08 T[i+1]  Temp c7 n-1
09 FinPour
10 Fin.

Pour chaque valeur de j (2 ≤ j ≤ n) on désigne par Tj le nombre d’exécution du test de la


boucle TantQue. On constate que (1 ≤ Tj ≤ j). Chaque quantité Tj dépend de la condition ou
l’état initial du tableau.

Soit T(n) le temps d’exécution de cet algorithme.

Complexité au meilleur : le cas le plus favorable pour l’algorithme TriInsertion est quand le
tableau est déjà trié. Dans ce cas tj = 1 pour tout j.

T(n) peut ici être écrit sous la forme T(n) = an + b, a et b étant des constantes
indépendantes des entrées.
𝐓(𝒏) = 𝑶(𝒏) est donc une fonction linéaire de n.

Le plus souvent, comme c’est le cas ici, le temps d’exécution d’un algorithme est fixé pour
une entrée donnée ; mais il existe des algorithmes « aléatoires » intéressants dont le
comportement peut varier même pour une entrée fixée. Nous verrons un algorithme de ce
style dans la suite du cours : une version « aléatoire » du tri rapide.

Complexité au pire : le cas le plus défavorable pour l’algorithme TriInsertion est quand le
tableau est déjà trié dans l’ordre inverse, comme le montre le cas j = 5 de la figure 2.1.
Dans ce cas t j = j pour tout j.

37
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

T(n) peut ici être écrit sous la forme T(n) = a*n2 + b*n + c, a, b et c étant des constantes.
𝑻(𝒏) = 𝑶(𝒏𝟐 ).T(n) est donc une fonction quadratique de n.

Complexité en moyenne : supposons que l’on applique l’algorithme de tri par insertion à n
nombres choisis au hasard. Quelle sera la valeur de t j ? C’est-à-dire, où devra-t-on insérer
A[j] dans le sous-tableau A[1.. j − 1] ?
En moyenne, pour moitié les éléments de A[1.. j − 1] sont inférieurs à A[j], et pour moitié
supérieurs.
Donc tj = j/2. Si l’on reporte cette valeur dans l’équation définissant T(n), on obtient, comme
dans le pire cas, une fonction quadratique en n.

3.5. Différentes nuances de complexité

Οrdre Type de complexité

O(1) Constant

O(log(n)) Logarithmique

O(n) Linéaire

O(n*log(n)) Quasi-linéaire

O(n2) Quadratique

Ο(n3) Cubique

Ο(2n) Exponentiel

Ο(n!) Factoriel

TDn°2 – Manipulation des ordres de grandeurs, performance d’un algorithme,


calcul de complexité ------------------------------------ (4H)

38
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Chapitre 4 : Notion de pile (2H)

4.1. Rappels et compléments sur les listes

Voir support de cours première année

4.2. Manipulation des piles

Définition
Une pile est une structure de données mettant en œuvre le principe « dernier entré,
premier sorti » (LIFO : Last-In, First-Out en anglais).
L’élément ôté de l’ensemble par l’opération SUPPRESSION est spécifié à l’avance (et donc
cette opération ne prend alors que l’ensemble comme argument) : l’élément supprimé est
celui le plus récemment inséré. L’opération INSERTION dans une pile est communément
appelée EMPILER, et l’opération SUPPRESSION, DÉPILER. La figure 5.1 montre les
conséquences des opérations EMPILER et DÉPILER sur une pile.

4.3. Création d’une pile

4.3.1. Algorithme
Il est facile d’implémenter une pile au moyen d’un tableau. La seule difficulté dans
cette implémentation est la gestion des débordements de pile qui interviennent quand on
tente d’effecteur l’opération DÉPILER sur une pile vide et l’opération EMPILER sur un
tableau codant la pile qui est déjà plein. Ce dernier problème n’apparaît pas lorsque l’on
implémente les piles au moyen d’une structure de données dont la taille n’est pas fixée a
priori (comme une liste chaînée). Les algorithmes réalisant les fonctions EMPILER et
DÉPILER, ainsi que la nécessaire fonction auxiliaire PILE-VIDE, sont présentés

Procedure PileVide(P)
Debut
Si sommet(P)==0 Alors
Retourner Vrai
Sinon
Retourner Faux
Finsi
Fin.

39
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Procedure Empiler(P, x) Procedure Depiler(P, x)


Debut Debut
Si sommet(P)==longueur(P) Alors Si PileVide(P) Alors
Ecrire("Erreur: débordement+") Ecrire("Erreur: débordement-")
Sinon Sinon
sommet(P) ← sommet(P)+1 sommet(P) ← sommet(P)-1
P[sommet(P))] ← x Retourner P[sommet(P))+1]
Finsi Finsi
Fin. Fin.

4.3.2. Pile à capacité limité

4.3.3. Les piles à capacité illimité

40
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

4.4. Applications

Dans un navigateur web, une pile sert à mémoriser les pages Web visitées. L’adresse de
chaque nouvelle page visitée est empilée et l’utilisateur désempile l’adresse de la page
précédente en cliquant sur le bouton « Afficher la page précédente ».
La fonction « annuler la frappe » (en anglais "undo") d’un traitement de texte mémorise les
modifications apportées au texte dans une pile et permet ainsi de revenir en arrière pas à
pas.

Un algorithme de recherche en profondeur utilise une pile pour mémoriser les nœuds
visités. On peut inverser les éléments contenus dans un tableau ou dans une chaîne de
caractères en utilisant une pile. Il suffit d’empiler les éléments sur une pile puis de
reconstituer le tableau (ou la chaîne) inverse en désempilant les éléments. L’évaluation des
expressions mathématiques en notation post-fixée (ou notation polonaise inverse) utilise
une pile.

41
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Chapitre 5 : Récursivité (8H)

Durée : CM ---------------------------------------------------------------------------------(2H)

5.1. Introduction

Une définition récursive est une définition dans laquelle intervient ce que l’on veut définir.
Un algorithme est dit récursif lorsqu’il est défini en fonction de lui-même.
Dans le cadre de ce cours, nous ne nous intéresserons qu’aux programmes et algorithmes
récursifs. Mais la notion de définition récursive est beaucoup plus générale : en
mathématiques on parle de récurrence alors qu’en programmation on parle de récursivité.

5.2. Fonction récursive

 Récursivité simple
Revenons à la fonction puissance x → xn. Cette fonction peut être définie récursivement :
x 0 = 1 et ∀ n > 0, x n = x ∗ x n−1

def Power_1(x,n) :
if n == 0 :
return 1
else :
return x*Power_1(x,n-1)

 Récursivité multiple
Une définition récursive peut contenir plus d’un appel récursif.
x0 = 1
x n = x n/2 ∗ x n/2 si n est paire
x n = x (n−1)/2 ∗ x (n−1)/2 ∗ x si n est impaire

def Power_2(x,n) :
if n == 0 :
return 1
elif n mod 2 == 0 :
return Power_2(x,n/2)* Power_2(x,n/2)
else:
return x*Power_2(x,(n-1)/2)*Power_2(x,(n-1)/2)

 Récursivité mutuelle
Des définitions sont dites mutuellement récursives si elles dépendent les unes des autres.
Ça peut être le cas pour la définition de la parité :

42
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

def Pair(n) :
if n == 0 :
return True
else:
return Impair(n-1)

def Impair(n):
if n == 0 :
return False
else:
return Pair(n-1)

 Récursivité imbriquée
La fonction d’Ackermann est définie comme suit :

def Ackermann(m,n):
if m == 0:
return n+1
elif n > 0 and n == 0:
return Ackermann(m-1,1)
else:
return Ackermann(m-1,Ackermann(m,n-1))

5.3. Complexité d’un algorithme récursif

Exemple 2 :

Chez les prêtes d’une certaine secte religieuse, on a résolu le problème suivant : 64 disques
posés les uns sur les autres sur un socle A, on les transférer sur un socle C en utilisant un
socle B. les seuls déplacements possibles consistent à prendre un disque sur le sommet, à
le poser sur les autres socles vides ou sur un disque plus grand que lui.
Selon la légende, la fin du monde coïncidera avec la résolution du problème !!!

Analyse du problème

Soit H le nom de la procédure récursive.


Soit Deplacer, la procédure de transfert d’un disque d’un socle à un autre.

Paramétrage
Soient :
n : Le nombre de disque
D : socle de départ
A : socle d’arrivé
I : socle intermédiaire

43
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Cas triviaux

Si n = 0 ne rien faire
Si n = 1 déplacer sur le socle A Deplace(D,A)

Décomposition du cas général

- Transférer récursivement n-1 disque du sommet de D, sur I. A sera utilisé ensuite


comme socle intermédiaire. D’où Hanoi(n-1, D, I, A)
- Déplacer le disque qui reste en D le plus grand sur A. d’où la commande Deplacer(D,A)
- Déplacer récursivement les n-1 disques de I en A en utilisant D comme intermediare.
D’où la commande Hanoi(n-1, I, A, D)

Procedure Hanoi(n: Entier,D,A,I: Caractere)


Debut
Si n>0 Alors (1)
Hanoi(n-1,D, A, I) (2)
Deplacer(D,A) (3)
Hanoi(n-1, I, A, D) (4)
Finsi
Fin.

Procedure Deplacer(D,A : Caractere)


Debut
Ecrire("Déplacer un disque de : ",D, " à ",A)
Fin.

Complexité de l’algorithme

Les instructions (1) et (3) sont de complexité O(1)


Soit T(n) la complexité de Hanoi.
Les instructions (2) et (4) sont de complexité T(n-1)

0, n=0
T(n) = {
1 + 2T(n − 1), n≥1

On démontre par la récurrence que 𝐓(𝐧) = 𝟐𝒏 -1


La complexité est donc de l’ordre O(2n)

Pour n = 64 on a : T(64) = 18 446 744 073 709 551 615 nombre de déplacements
Pour un déplacement de 1 seconde on finira en 584 942 417 355 ans voire la fin du monde !!!!

44
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Remarque : Efficacité de la récursivité

Un algorithme récursif est littéralement moins rapide qu’un algorithme équivalent. La


récursivité utilise plus de ressources mémoire pour empiler les contextes. Cependant, la
récursivité est plus élégante et les algorithmes récursifs sont souvent plus facile à écrire.

TDn°3 – Manipulation des piles, notions de récursivité, complexité----------- (4H)

5.4. Résolution des récurrences

Théorème 1 :

TPn°3 – Simulation de piles ----------------------------------------- (2H)

45
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Chapitre 6 : Algorithmes de tri (10H)

Durée : CM ---------------------------------------------------------------------------(4H)

6.1. Introduction

On désigne par "tri" l’opération consistant à ordonner un ensemble d’éléments en


fonction de critères sur lesquelles est définie une relation d’ordre.

Les algorithmes de tri ont une grande importance pratique. Ils sont fondamentaux dans
l’informatique où l’on tri de manière quasi-systématique des données avant de les utiliser.

L’étude du tri est également intéressante en elle-même car il s’agit sans doute du
domaine de l’algorithmique qui a été le plus étudié et qui a conduit à des résultats
remarquables sur la construction d’algorithmes et l’étude de leur complexité.

Les exemples de tris que nous étudierons dans ce cours vous permettrons de vous forger
une idée. Il y a bien sûr d’autres algorithmes que ceux présentés ici, mais ce document n’a
pas l’ambition de dresser une liste exhaustive de ce qui existe mais d’avoir un
aperçu de ce problème qui est plus complexe qui n’y paraît au premier abord.
Tout le monde est confronté un jour ou l’autre à trier des affaires et l’on sait que
c’est loin d’être facile.

6.2. Algorithmes de tri par insertion

6.2.1. Principe du tri par insertion

C’est le tri du joueur de cartes. On fait comme si les éléments à trier étaient donnés un
par un, le premier élément constituant, à lui tout seul, une liste triée de longueur 1. On
range ensuite le second élément pour constituer une liste triée de longueur 2, puis on range
le troisième élément pour avoir une liste triée de longueur 3 et ainsi de suite ....
À chaque itération , on place l’élément à sa place en décalant ceux qui sont plus
grands.
Le principe du tri par insertion est donc d’insérer à la nième itération le nième élément à la
bonne place.

6.2.2. Exemple de tri par insertion

Soit à trier la liste suivante : 86 - 8 - 79 - 21 - 58 - 49 - 42 - 22 - 28 - 79


On prend les deux premiers éléments : 86 - 8, comme 8 est plus petit, on décale
86 est l’on met 8 devant. Cela donne : 8 - 86. On prend l’élément suivant 79, il
est plus petit que 86 mais plus grand que 8, on décale 86 et l’on insère 79, ce qui
donne : 8 - 79 - 86. Voici alors les différentes étapes du processus.

46
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

0 86 8 79 21 58 49 42 22 28 79
1 8 86 79 21 58 49 42 22 28 79
2 8 79 86 21 58 49 42 22 28 79
3 8 21 79 86 58 49 42 22 28 79
4 8 21 58 79 86 49 42 22 28 79
5 8 21 49 58 79 86 42 22 28 79
6 8 21 42 49 58 79 86 22 28 79
7 8 21 22 42 49 58 79 86 28 79
8 8 21 22 28 42 49 58 79 86 79
9 8 21 22 28 42 49 58 79 79 86

6.2.3. Algorithme du tri par insertion

6.2.4. Exécution de l’algorithme du tri par insertion

Voici les étapes de l'exécution du tri par insertion sur le tableau T = [9,6,1,4,8]. Le tableau
est représenté au début et à la fin de chaque itération.

47
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

6.2.5. Complexité de l’algorithme de tri par insertion

La complexité du tri par insertion est Θ(n2) dans le pire cas et en moyenne, et linéaire dans
le meilleur cas. Plus précisément :
1. Dans le pire cas, atteint lorsque le tableau est trié à l'envers, l'algorithme effectue
de l'ordre de n2/2 affectations et comparaisons ;
2. Si les éléments sont distincts et que toutes leurs permutations sont équiprobables
(c’est-à-dire avec une distribution uniforme), la complexité en moyenne de
l'algorithme est de l'ordre de n2/4 affectations et comparaisons2 ;
3. Si le tableau est déjà trié, il y a n-1 comparaisons et au plus n affectations.

Preuve : A vous de jouer les MP !

Choisissons comme opération élémentaire la comparaison de deux cellules du


tableau :

Dans le pire des cas le nombre de comparaisons "TantQue (j>0 etTab[ j-1 ] > x) Faire" est
une valeur qui ne dépend que de la longueur i de la partie (T[1], T[2], …T[i]) déjà rangée. Il
y a donc au pire i comparaisons pour chaque i variant de 1 à n-1 :
La complexité au pire en nombre de comparaison est donc égale à la somme des n termes
suivants (i = 1, i = 2,.... i = n-1)
C = 1+2 + 3 + 4 +...+ n-1 = n(n-1)/2 comparaisons au maximum.
La complexité au pire en nombre de comparaison est de de l'ordre de n², que l'on écrit
O(n²).

La complexité du tri par insertion reste linéaire si le tableau est presque trié (par exemple,
chaque élément est à une distance bornée de la position où il devrait être, ou bien tous les
éléments sauf un nombre borné sont à leur place). Dans cette situation particulière, le tri
par insertion surpasse d'autres méthodes de tri. Lesquels ? A répondre à la fin du cours
sur les tris.

6.3. Algorithme de tri rapide

En informatique, le tri rapide (en anglais quicksort) est un algorithme de tri inventé
par inventé par Charles Antony R. Hoare en 1961 et amélioré par Robert Sedgewick dans
les années 70 et fondé sur la méthode de conception diviser pour régner. Il est
généralement utilisé sur des tableaux, mais peut aussi être adapté aux listes

Le tri rapide est l’un des tris les plus rapide et donc l’un des plus utilisé. Cependant ce tri
ne peut pas tirer avantage du fait que l’entrée est déjà presque triée.
Dans ce cas particulier, il est plus avantageux d’utiliser le tri par insertion.

48
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Diviser pour régner : 3 étapes

Diviser le problème en un certain nombre de sous-problèmes


Régner sur les sous-problèmes en les résolvant récursivement Si la taille d’un sous-
problème est assez réduite, on peut le résoudre directement
Combiner les solutions des sous-problèmes en une solution complète pour le problème initial

6.3.1. Principe du tri rapide

La méthode consiste à placer un élément du tableau (appelé pivot) à sa place définitive, en


permutant tous les éléments de telle sorte que tous ceux qui sont inférieurs au pivot soient
à sa gauche et que tous ceux qui sont supérieurs au pivot soient à sa droite.
Cette opération s'appelle le partitionnement. Pour chacun des sous-tableaux, on définit un
nouveau pivot et on répète l'opération de partitionnement. Ce processus est répété
récursivement, jusqu'à ce que l'ensemble des éléments soit trié.

Concrètement, pour partitionner un sous-tableau :


▪ le pivot est placé à la fin (arbitrairement), en l'échangeant avec le dernier élément
du sous-tableau ;
▪ tous les éléments inférieurs au pivot sont placés en début du sous-tableau ;
▪ le pivot est déplacé à la fin des éléments déplacés.

6.3.2. Exemple du tri rapide

Voir vidéo : https://www.youtube.com/watch?v=ywWBy6J5gz8

6.3.3. Algorithme du tri rapide

Fonction Partitionnement

49
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

Procédure de tri rapide

6.3.4. Exécution de l’algorithme du tri par insertion

Donner les étapes de l'exécution du tri rapide sur le tableau T = [9,6,1,4,8].

6.3.5. Choix du pivot et complexité

• Pivot arbitraire
Une manière simple de choisir le pivot est de prendre toujours le premier élément du sous-
tableau courant (ou le dernier). Lorsque toutes les permutations possibles des entrées
sont équiprobables, la complexité moyenne du tri rapide en sélectionnant le pivot de cette
façon est Θ(n log (n)). Cependant, la complexité dans le cas le pire est Θ(n2), et celle-ci est
atteinte lorsque l'entrée est déjà triée ou presque triée.

Si on prend comme pivot le milieu du tableau, le résultat est identique, bien que les entrées
problématiques soient différentes.

Il est possible d'appliquer une permutation aléatoire au tableau pour éviter que
l'algorithme soit systématiquement lent sur certaines entrées. Cependant, cette technique
est généralement moins efficace que de choisir le pivot aléatoirement.

• Pivot aléatoire
Si on utilise la méthode donnée dans la description de l'algorithme, c'est-à-dire choisir le
pivot aléatoirement de manière uniforme parmi tous les éléments, alors la complexité
moyenne du tri rapide est Θ(n log (n)) sur n'importe quelle entrée. Dans le pire des cas, la
complexité est Θ(n2). Néanmoins, l'écart type de la complexité est seulement Θ(n), ce qui
signifie que l'algorithme s'écarte peu du temps d'exécution moyen.
Dans le cas le meilleur, l'algorithme est en Θ(n log (n)).

Preuve de la complexité moyenne : voir fiche de TD N°4

6.4. Algorithme du Tri par fusion

À partir de deux listes triées, on peut facilement construire une liste triée comportant les
éléments issus de ces deux listes (leur *fusion*). Le principe de l'algorithme de tri fusion
repose sur cette observation : le plus petit élément de la liste à construire est soit le plus

50
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

petit élément de la première liste, soit le plus petit élément de la deuxième liste. Ainsi, on
peut construire la liste élément par élément en retirant tantôt le premier élément de la
première liste, tantôt le premier élément de la deuxième liste (en fait, le plus petit des
deux, à supposer qu'aucune des deux listes ne soit vide, sinon la réponse est immédiate).
Ce procédé est appelé fusion et est au cœur de l'algorithme de tri développé ci-après.

6.4.1. Principe du tri par fusion

L'algorithme est naturellement décrit de façon récursive.


1. Si le tableau n'a qu'un élément, il est déjà trié.
2. Sinon, séparer le tableau en deux parties à peu près égales.
3. Trier récursivement les deux parties avec l'algorithme du tri fusion.
4. Fusionner les deux tableaux triés en un seul tableau trié.

Se basant sur le principe diviser pour régner on a :

Diviser : diviser la liste de n éléments à trier en deux sous-listes de n/2 éléments


Régner : trier les deux sous-listes récursivement à l’aide du tri par fusion
Combiner : fusionner les deux sous-listes triées pour produire la réponse triée

6.4.2. Exemple de tri par insertion

Voir la vidéo en ligne : https://www.youtube.com/watch?v=XaqR3G_NVoo


En voici deux illustrations parfaites !

Tri fusion exemple 1

Tri fusion exemple 2

51
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

6.4.3. Algorithme du tri fusion

6.4.4. Exécution de l’algorithme du tri fusion

Donner les étapes de l'exécution du tri rapide sur le tableau T = [9,6,1,4,8].

52
Classes Préparatoires
aux Grandes Ecoles
Approfondissement en Python 3

6.4.5. Complexité de l’algorithme du tri fusion

Le tri fusion comporte 3 étapes : la division de l’ensemble d’éléments en deux parties, le tri
des deux sous-ensembles puis la fusion des deux sous-ensembles triés. Soit un ensemble
de n éléments, le coût en nombre d’opérations de la division des deux ensembles est
constant dans le cas du tri de tableaux et est de n dans le cas des listes. Le coût du tri des
deux sous listes est égal à 2 fois le coût du tri d’une liste de longueur n/2. Enfin, le coût de
la fusion des deux sous listes triées est proportionnel au nombre d’éléments à fusionner,
c’est à dire en O(n).
Les quelques algorithmes vus jusqu’à maintenant sont en O(n2). D’après les résultats ci-
dessus, si on note T(n) la complexité du tri fusion pour classer n éléments, on a :

Or, si la complexité du tri fusion était en O(n2), on aurait :

Ce qui est inférieur à n2 dès que n>2. Par conséquent, la complexité du tri fusion n’est pas
en O(n2) mais en une valeur bien inférieure.

Pour calculer la complexité du tri fusion, nous avons besoin du résultat du théorème 1.
Soit T(n) une suite vérifiant la relation de récurrence suivante :
➢ T(n)=aT(n/b)+O(nc).
➢ Si a < bc alors T(n)=O(nc)
➢ Si a = bc alors T(n)=O(nclogb(n))
➢ Si a > bc alors T(n)=O(nlogb(a)) avec logb le logarithme népérien en base b

Ici, on a (2 = 21), la complexité du tri fusion est donc en O(n*ln(n)) ou ln(n) est le logarithme
népérien en base 2.
Remarque : en ce qui concerne le tri de listes, le tri fusion souffre du fait que c’est un
algorithme récursif. En effet, pour se dérouler, il doit construire un grand nombre de sous
listes, ce qui demande un grand espace mémoire. La complexité en espace mémoire du tri
fusion est donc assez handicapante si l’espace mémoire disponible n’est pas assez grand.

TDn°4 – Tri rapide, tri fusion, complexité de tris avancés ---------------- (4H)

TPn°5 – Implémentation de Tris avancés : tri rapide, tri fusion ------------ (2H)

53

Vous aimerez peut-être aussi