Académique Documents
Professionnel Documents
Culture Documents
Cours et exercices
Kamal Haïdar,
Lycée Janson-de-Sailly,
kamal.m.haidar@gmail.com
Table des Matières
I Démarrer en Python 1
iii
II Machines numériques 29
3 Machines numériques 31
3.1 Description d’une machine numérique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.1.1 L’ordinateur personnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.1.2 La tablette . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.2 Le système d’exploitation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2.1 Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2.2 Structure de fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.3 Compléments sur les différents types de mémoire . . . . . . . . . . . . . . . . . . . . . . . 33
III Algorithmique 45
5 Algorithmes classiques 47
5.1 Statistiques élémentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.1.1 Moyenne d’une série statistique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.1.2 Variance d’une série statistique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.2 Algorithmes de recherche séquentielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.2.1 Recherche du maximum dans un tableau de nombres . . . . . . . . . . . . . . . . . 48
5.2.2 Recherche d’un élément dans un tableau . . . . . . . . . . . . . . . . . . . . . . . . 49
5.2.3 Recherche dichotomique dans un tableau trié . . . . . . . . . . . . . . . . . . . . . 50
5.2.4 Recherche d’un mot dans une chaine de caractères . . . . . . . . . . . . . . . . . . 51
6 Analyse algorithmique 53
6.1 Terminaison et correction d’algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.1.1 Le problème de l’arrêt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.1.2 Terminaison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.1.3 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.2 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.2.2 Complexité asymptotique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6.2.3 Classe de complexité asymptotique d’un algorithme . . . . . . . . . . . . . . . . . . 56
6.3 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
iv
IV Bases de données relationnelles 63
V Analyse numérique 77
8 Résolution d’équations 79
8.1 Algorithme de recherche du zéro d’une fonction monotone . . . . . . . . . . . . . . . . . . 79
8.2 Méthode de Newton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
v
vi
Partie I
Démarrer en Python
1
Chapitre 1
Programmation impérative en Python
1.1 Introduction
1.1.1 Notion d’algorithme
Définition 1.1.1
Un algorithme est une séquence finie et ordonnée d’opérations qui, prenant un ensemble (fini) de
valeurs en entrée, produit un ensemble (fini) de valeurs en sortie.
Un algorithme répond alors à un problème (de calcul, de décision, etc), en ayant connaissance de l’entrée
et de la sortie.
Exemple 1.1.2
Une recette de cuisine, une notice de montage d’un meuble sont des algorithmes.
L’entrée est l’ensemble des ingrédients (resp. l’ensemble des matériaux à assembler) et la sortie est le
plat cuisiné (resp. le meuble monté).
Définition 1.1.3
Un langage de programmation est un ensemble de notations destinées à formuler des algorithmes.
Il est muni d’une orthographe, d’un vocabulaire et d’une grammaire.
• orienté-objet, tout est objet : une variable peut contenir une référence à un élément quelconque
du langage (nombre, méthode, module, ...),
• interprété, traduit ligne par ligne en langage machine, au moment de l’exécution, contrairement
aux langages compilés qui traduisent les programmes en une seule fois,
• à typage dynamique, on ne précise pas le type des variables lors de leur définition, il est inféré
par l’interpréteur,
3
1.2 Les types usuels Chapitre 1. Programmation impérative en Python
Exemple 1.1.4
Python est utilisé par Google, Industrial Light & Magic (Lucas Film), la NASA, etc.
C’est le langage de commande des logiciels Open Office, Blender ou encore Corel Paint Shop Pro.
1.1.3 Framework
Le framework choisi au lycée pour travailler en Python est Pyzo. Il contient un environnement de
développement intégré (IDE) appelé IEP (Interactive Editor for Python) ainsi qu’une version 3.* du
langage Python ainsi qu’une série de modules et est disponible à l’adresse suivante :
http://www.pyzo.org/downloads.html
Il est nécessaire de distinguer deux composantes fondamentales de cet environnement : le shell, encore
appelé console ou terminal (plusieurs peuvent tourner simultanément) et l’éditeur.
• Le shell est une interface (contenant un interpréteur) entre vous et le système d’exploitation qui
permet d’exécuter des instructions en Python à la ligne de commande,
• L’éditeur permet quant à lui d’écrire, de modifier et d’enregistrer (dans un fichier) du code (série
d’instructions en Python) avant d’être exécuté dans un shell. Tout passe donc par un terminal !
• Les opérations logiques possibles sur le type bool sont (par ordre de priorité) : la disjonction or,
la conjonction and et la négation not.
Exemple 1.2.1
4 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.2 Les types usuels
Remarque 1.2.2
• Deux objets de types différents ne peuvent pas être considérés comme égaux, à moins qu’ils
soient de types numériques.
• Les opérateurs de comparaison ont la même priorité (supérieure à celle des opérateurs booléens).
• le type int (abréviation d’integer) représentant les nombres entiers (relatifs) avec une précision
illimitée,
• le type float représentant les nombres à virgule flottante1 , généralement stockés en double précision
(64 bits),
• le type complex représentant des nombres complexes dont les parties réelle et imaginaire sont des
nombres flottants.
Le tableau ci-dessous présente les opérations usuelles sur les types numériques (dans l’ordre des priorités
croissantes).
x + y somme de x et y
x - y différence de x par y
x * y produit de x par y
x / y quotient flottant de x par y
x // y quotient de la division euclidienne de x par y (pour y de type int)
x % y reste de la division euclidienne de x par y (pour y de type int)
-x opposé de x
abs(x) valeur absolue/module de x
z.conjugate() conjugué du nombre complexe z
x ** y exponentation de x par y
1
cf. chapitre "Représentation des nombres en machine"
Informatique MPSI/PCSI 5
1.2 Les types usuels Chapitre 1. Programmation impérative en Python
Exemple 1.2.3
>>> 2 ∗ 5 // 6 − 4 ∗∗ 4 # noter l a p r i o r i t é des opérations
−255
>>> 43 / 7 # d i v i s i o n dans l e s f l o t t a n t s
6.142857142857143
>>> 43 // 7; 43 % 7 # q u o t i e n t / r e s t e de l a d i v i s i o n e u c l i d i e n n e
6
1
>>> abs(( −2) ∗∗ 7)
128
>>> (1j ) ∗∗ 2 # l a n o t a t i o n j d é s i g n e l e i mathématique
( −1+0j)
>>> (1 + 2j). real # partie réelle
1.0
>>> (1 + 2j). imag # partie imaginaire
2.0
>>> 0.1 + 0.1 + 0.1 # p r é c i s i o n d e s c a l c u l s en f l o t t a n t s !!!
0.30000000000000004
Il existe de plus des constructeurs de type - à utiliser avec parcimonie - permettant de convertir un
objet d’un type donné vers un autre (lorsque cela est possible) : les fonctions int, float et complex.
Exemple 1.2.5
>>> int (1.56)
1
>>> int( − 1.56) # partie entière ?
−1
>>> float (1)
1.0
>>> complex (3.5 ,4)
(3.5+4 j)
6 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.2 Les types usuels
• l’extraction d’une sous-chaine d’une chaine de caractères par les indices de début et fin,
On peut accéder à chacun des caractères d’une chaine par son indice. Attention, en informatique, la
numérotation commence à partir de 0 !
Exemple 1.2.7 (Accès à un caractère par son indice)
La longueur d’une chaine de caractères est calculée par la fonction len (abréviation de length).
Exemple 1.2.8 (Calcul de longueur d’une chaine)
>>> len(txt)
13
On peut extraire une sous-chaine d’une chaine de caractères par sclicing : si ch désigne une chaine de
caractères, alors ch[a:b] est sous-chaine de ch composée de tous les caractères de ch de l’indice a inclus
à l’indice b exclu.
Informatique MPSI/PCSI 7
1.2 Les types usuels Chapitre 1. Programmation impérative en Python
À l’aide de l’opérateur binaire +, on peut concaténer deux chaines de caractères, i.e. les mettre bout-à-
bout de manière à créer une seule chaine.
Exemple 1.2.11 (Concaténation de chaines de caractères)
On peut tester l’appartenance d’un caractère à une chaine grâce au mot-clé in.
Exemple 1.2.12 (Test d’appartenance à une chaine)
8 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.2 Les types usuels
• par extension (de manière explicite), en encadrant les éléments de la liste par des crochets [ et ]
et en les séparant par des virgules,
Les types séquentiels (les listes, les chaines de caractères, les tuples, etc) partagent un grand nombre de
syntaxes, comme illustré dans l’exemple suivant.
Exemple 1.2.15 (Syntaxes communes aux types séquentiels)
>>> liste4 = [1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> liste4 [0] # a c c è s à un é l é m e n t par son i n d i c e
1
>>> liste4 [6] # a c c è s à un é l é m e n t par son i n d i c e
13
>>> liste4[−1] # a c c è s à un é l é m e n t par son i n d i c e
34
>>> len( liste4 ) # l o n g u e u r d ’ une s é q u e n c e
9
>>> liste4 [1:7] # e x t r a c t i o n d ’ une sous−s é q u e n c e
[1, 2, 3, 5, 8, 13]
>>> liste4 [:4]
[1, 1, 2, 3]
>>> liste4 [6:]
[13, 21, 34]
>>> 7 not in liste4 # t e s t d ’ appartenance
True
>>> [’a’, ’b’, ’c’] + [’d’, ’e’, ’fghij ’] # c o n c a t é n a t i o n de s é q u e n c e s
[’a’, ’b’, ’c’, ’d’, ’e’, ’fghij ’]
Le type list est dynamique, sa taille est donc modifiable (sans changer d’objet), comme l’illustre l’exemple
suivant.
Informatique MPSI/PCSI 9
1.2 Les types usuels Chapitre 1. Programmation impérative en Python
• par extension, en séparant ses éléments par des parenthèses et en les encadrant par des parenthèses
( et ) (facultatif),
Les tuples ne sont pas des objets mutables contrairement aux listes avec lesquelles ils partagent de
nombreuses propriétés et syntaxes.
10 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.3 Les variables
>>> triplet = 1, 2, 3
>>> triplet [1] = 0
Traceback (most recent call last ):
File "<console >", line 1, in <module >
TypeError : ’tuple ’ object does not support item assignment
(i) le membre de droite est évalué par l’interpréteur; le résultat est stocké en mémoire, référencé par
un identifiant;
>>> x = 0
>>> x, id(x), type(x) # valeur , i d e n t i f i a n t , type
(0, 4297326592 , <class ’int ’>)
>>> x = x + 1 # i n c r é m e n t a t i o n de x
>>> x, id(x), type(x) # c a r t e d ’ i d e n t i t é de x
(1, 4297326624 , <class ’int ’>)
>>> x = x − 2 # d é c r é m e n t a t i o n de x ( de 2)
>>> x, id(x), type(x) # c a r t e d ’ i d e n t i t é de x
(−1, 4297326560 , <class ’int ’>)
>>> x += 1 # remplace x = x + 1
>>> x −= 3 # remplace x = x − 3
>>> x ∗ = 3 # remplace x = x ∗ 3
>>> x /= 2 # remplace x = x / 2
Exercice 1.3.2
Déterminer le contenu des variables à la fin de l’exécution de chacun des deux scripts suivants :
>>> a = 10 >>> a = 3
>>> b = 17 >>> b = 4
>>> c = a − b >>> c = a
>>> a = 0 >>> a = b
>>> c = b + a + 1 >>> b = c
Informatique MPSI/PCSI 11
1.3 Les variables Chapitre 1. Programmation impérative en Python
Il existe en Python une syntaxe rapide et efficace pour lier des valeurs à plusieurs variables : l’affectation
parallèle.
Exemple 1.3.3 (Affectation parallèle)
• Un nom de variable est une séquence de caractères constituée de chiffres et de lettres ordinaires
de l’alphabet (i.e. sans accent, sans cédille, sans espace, ni caractères spéciaux).
• Cette séquence doit nécessairement commencer par une lettre (et non un chiffre). Il convient
que cette lettre soit une minuscule à moins de ne définir une constante (dans ce cas, toutes les
lettres sont en majuscule).
• La casse est prise en compte. Ainsi hauteur, Hauteur et HAUTEUR sont des noms de variables
distincts.
• Il convient de limiter les noms de variables à un caractère. Il est préférable d’utiliser des noms
de variables clairs et explicites.
• Lorsque les noms de variables sont assez longs, on pourra utiliser la convention CamelCase (ou
plus précisément lowerCamelCase) qui consiste à écrire une variable comme concaténation de
mots dont la première lettre est une majuscule. Cette convention permet une meilleure lecture.
Exemple : PlayStation.
Cette convention est en concurrence avec l’insertion du symbole underscore (noté _) entre les
mots liés.
On pourra alors écrire : maxHauteur (convention lowerCamelCase) ou max_hauteur (convention
“underscore”).
2
Pour plus d’informations sur le sujet, on invite le lecteur à consulter le Style Guide for Python Code, à l’adresse http:
//www.python.org/dev/peps/pep-0008/.
12 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.3 Les variables
On peut penser que l’instruction liste2 = liste1 effectue une copie de liste1. Cette copie n’est en
réalité par indépendante de l’originale. Pire ! il s’agit d’un alias : deux noms de variables pour le même
objet en machine (cf. les identifiants de référence). En réalité, le symbole = permet de copier des références
et non des valeurs.
Pour notre exemple, on peut simplement contourner le problème. En effet, l’opération d’extraction d’une
sous-séquence crée une copie, dite superficielle.
Exemple 1.3.6
Les choses se compliquent lorsqu’on considère une liste contenant une liste !
Exemple 1.3.7
>>> liste5
[1, "a", [4 ,0 ,6]]
>>> alias
[1, "a", [4 ,0 ,6]]
>>> copieSuperficielle
[1, "a", [4 ,0 ,6]]
Informatique MPSI/PCSI 13
1.4 Les fonctions Chapitre 1. Programmation impérative en Python
La première instruction donne une information : une copie superficielle et son originale sont deux objets
distincts en mémoire.
La seconde instruction indique que, lorsqu’on réalise une copie superficielle, on crée une nouvelle
séquence dont les éléments sont identiques en mémoire. La copie superficielle n’est donc pas
indépendante de l’originale.
Il existe une manière de copier de façon à rendre la copie entièrement indépendante de l’originale : la
copie profonde. La syntaxe sera vue en TP.
Exemple 1.4.2
On définit la fonction cubique dans l’éditeur.
def cube(x):
""" r e n v o i e l e c u b e du nombre x """
return x ∗∗ 3
On exécute ensuite ce code dans un shell et on l’utilise de différentes manières.
>>> ( executing lines 1 to 3 of "<tmp 1>")
>>> cube (3)
27
>>> cube(−2)
−8
>>> help(cube)
Help on function cube in module __main__ :
cube(x)
renvoie le cube du nombre x
3
il est possible de ne rien renvoyer (ou, plus précisément, de renvoyer un argument de type NoneType).
4
Le mot-clé return n’est pas une fonction ! D’où l’absence de parenthèses...
5
Par abus de langage, on dit qu’une fonction renvoie plusieurs objets lorsqu’elle renvoie un tuple (dont les coordonnées sont
ces objets).
14 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.4 Les fonctions
Il est aussi possible de définir une fonction avec des paramètres par défaut. On précise la valeur par défaut
de ces paramètres dans la définition.
Exemple 1.4.3
• la docstring d’une fonction, propre à chaque fonction, qui explique comment utiliser la fonc-
tion, en décrivant les paramètres attendus, les valeurs retournées et les exceptions levées.
• des commentaires sur le code qui expliquent comment votre code fonctionne. Les commentaires
ne sont pas lus par l’interpréteur, ils sont uniquement destinés à faciliter la lecture du code (par
soi-même ou un tiers) ainsi que la maintenance du code. Un commentaire débute par le symbole
# et continue sur le reste de la ligne.
“Les programmes doivent être écrits pour être lus par des gens et accidentellement
exécutés par des machines.”
Informatique MPSI/PCSI 15
1.4 Les fonctions Chapitre 1. Programmation impérative en Python
Remarque 1.4.7
Vous trouverez ci-dessous les noms des erreurs classiques que vous ferez en TP :
Identifiez-les, sachez lire les messages d’erreur correctement (de bas en haut) pour débugger.
Une variable définie à l’extérieur du corps d’une fonction est appelée variable globale.
Une variable globale est accessible dans le corps d’une fonction. Le mot-clé global permet de ne pas
créer de nouvelle variable locale (dans l’espace des noms de la fonction) mais d’accéder au contenu de la
variable définie à l’extérieure du corps de la fonction.
Exemple 1.4.8
>>> x = 0
>>> def incremente ():
... """ a j o u t e 1 à l a v a r i a b l e g l o b a l e x """
... x += 1
...
>>> incremente ()
Traceback (most recent call last ):
File "<console >", line 1, in <module >
File "<console >", line 3, in incremente
UnboundLocalError : local variable ’x’ referenced before assignment
On recommence en déclarant la variable x comme globale.
>>> def incremente_bis ():
... """ a j o u t e 1 à l a v a r i a b l e g l o b a l e x """
... global x
... x += 1
...
>>> incremente_bis ()
>>> x
1
Remarquer que la fonction incremente ne renvoie rien. On dit qu’elle agit par effet de bord : elle
modifie l’état d’une variable dont la valeur n’est pas renvoyée.
16 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.5 Les test conditionnels
•
if booleen :
# instructions
Si la valeur du booléen est True alors la série d’instructions indentée est réalisée. On n’effectue
pas la série d’instructions indentée dans le cas contraire.
•
if booleen :
# instructions
else:
# instructions
# alternatives
Si la valeur du booléen est True alors on réalise (uniquement) la première série d’instructions
indentée, sinon, on réalise la série d’instructions alternatives (elle aussi indentée).
•
if booleen0 :
# instructions
elif booleen1 :
# instructions
# alternatives 1
elif booleen2 :
# instructions
# alternatives 2
.
.
.
else:
# instructions
# alternatives n
Les booléens sont évalués dans l’ordre de lecture. Dès qu’un de ces booléens a pour valeur True,
la série d’instructions indentée associée (et uniquement celle-ci) est exécutée. Dans le cas où aucun
des booléens n’est vrai (else), la dernière instruction alternative est exécutée.
Informatique MPSI/PCSI 17
1.6 Les structure de boucles Chapitre 1. Programmation impérative en Python
Exemple 1.5.1
On (re-)définit ci-dessous la fonction valeur absolue.
def absolue (x):
if x >= 0:
return x
else:
return −x
La série indentée d’instructions est répétée autant de fois qu’il y a d’éléments dans la séquence.
Une boucle itérative ne nécessite pas d’initialisation : la variable est définie à l’entrée dans la boucle par
la syntaxe elle-même.
Il existe deux manières de parcourir une séquence :
Pour illustrer ces deux types de parcours, on définit dans les deux exemples suivants des fonctions calculant
la somme des éléments d’une liste de nombres.
Exemple 1.6.1 (Parcours par élément)
def somme(liste ):
""" r e n v o i e l a somme d e s é l é m e n t s de l i s t e ( de t y p e l i s t )
A t t e n t i o n : l e s é l é m e n t s de l i s t e d o i v e n t ê t r e de t y p e numérique """
# initialisation
s = 0
# p a r c o u r s par é l é m e n t
for nb in liste:
s = s + nb
return s
Pour le parcours par indice, on calcule la longueur de la séquence (via la fonction len) et on utilise
l’itérateur range.
• L’instruction range(n) génère successivement tous les entiers de 0 inclus à n exclu, i.e. de 0 à n-1.
• L’instruction range(a,b) génère successivement tous les entiers de a inclus à b exclu (!).
• L’instruction range(a, b, p) génère successivement les entiers a, a+p, a+2p jusqu’à b exclu (!).
Le nombre p est appelé pas.
18 Informatique MPSI/PCSI
Chapitre 1. Programmation impérative en Python 1.6 Les structure de boucles
Remarque 1.6.3
On prendra l’habitude de noter en commentaire l’ensemble des valeurs prises par la variable parcourant
un itérateur.
• Lorsque la valeur de l’expression booléenne n’est jamais modifiée par la série d’instructions à
l’intérieur de la boucle, on parle de boucle infinie : le code ne permet pas de sortir de la boucle
qui répétera (théoriquement) à l’infini la série d’instructions7 .
A moins que cela soit absolument nécessaire, on évitera d’écrire des boucles infinies. Pour cela, on
veillera à ce qu’il existe au moins une itération de la boucle qui modifie la valeur de l’expression
booléenne.
• Une boucle while devra toujours être initialisée (par la définition d’une variable par exemple) avant
l’entrée dans la boucle, de façon à en permettre l’entrée (ou à en forcer l’entrée).
Exemple 1.6.4 (Division euclidienne)
7
Il est important de pouvoir vérifier qu’une boucle conditionnelle termine ; nous aborderons cette question dans un prochain
chapitre.
Informatique MPSI/PCSI 19
1.6 Les structure de boucles Chapitre 1. Programmation impérative en Python
20 Informatique MPSI/PCSI
Chapitre 2
Présentation de modules usuels
Notion de module
La distribution standard de Python contient un grand nombre de bibliothèques logicielles externes. Chaque
bibliothèque regroupe notamment un ensemble de fonctions et procédures autour d’un thème (calcul
numérique et scientifique, traitement d’images, graphisme, programmation internet et réseau, gestion de
base de données, etc).
Pour pouvoir accéder aux fonctionnalités d’une bibliothèque, il est nécessaire d’importer le fichier (au
format .py) qui la contient. La syntaxe standard est la suivante :
import <module >
Pour accéder à une fonction funct d’un module mod préalablement importé comme ci-dessus, il est néces-
saire de rappeler le nom du module via la syntaxe mod.funct.
• random() simule une variable aléatoire uniforme sur [0, 1[ et renvoie un nombre aléatoire de
l’intervalle [0, 1[.
>>> random . random () # r e n v o i e un nombre a l é a t o i r e c o m p r i s e n t r e 0 e t 1
0.49105308610499
• randint(a,b) simule une variable aléatoire uniforme sur Ja, bK et renvoie un nombre aléatoire de
l’ensemble Ja, bK.
>>> random . randint (1, 6) # s i m u l a t i o n d ’ un l a n c e r de dé
1
• choic(seq) renvoie un élément choisi (uniformément) aléatoirement dans une séquence seq.
>>> random . choice ("abcde") # r e n v o i e une l e t t r e c h o i s i e a l é a t o i r e m e n t
’b’
>>> random . choice ([k for k in range (1 ,7)]) # s i m u l a t i o n l a n c e r de dé
3
21
2.2 Module time Chapitre 2. Présentation de modules usuels
Remarque 2.1.1
Le module random contient bien-sûr bien plus que les quelques fonctions présentées ici. Il permet
notamment de simuler des variables aléatoires suivant des lois exponentielle, normale, Gamma ou de
Weibull.
Remarque 2.1.2
On peut raccourcir la syntaxe d’appel d’une fonction d’un module externe en donnant un “petit nom”
au module importé à l’aide du mot-clé as. Le code ci-dessous est équivalent à ceux présentés en
exemple ci-dessus.
import random as rd
rd. random ()
rd. randint (1, 6)
rd. choice ("abcde")
rd. choice ([k for k in range (1 ,7)])
rd. shuffle ([1, 2, 3, 4, 5, 6])
22 Informatique MPSI/PCSI
Chapitre 2. Présentation de modules usuels 2.3 Module matplotlib
import time
# mesure à l ’ i n s t a n t t 0
t0 = time.clock ()
...
# algorithme
...
# mesure à l ’ i n s t a n t t 1
t1 = time.clock ()
# a f f i c h e l e temps d ’ e x é c u t i o n e n t r e deux a p p e l s de t i m e . c l o c k ( )
print (t1 − t0)
Remarque 2.2.1
On utilisera cette fonction pour comparer des temps d’exécution d’algorithmes sur une même machine.
On fera notamment attention à ne pas lancer de programme pendant ou entre deux mesures.
• plot(x, y) prend en argument deux listes x = [x0 , . . . , xn−1 ] et y = [y0 , . . . , yn−1 ] et place succes-
sivement - en les reliant - les points de coordonnées (x0 , y0 ), . . . , (xn−1 , yn−1 ).
La fonction plot accepte de nombreux arguments optionnels permettant de personnaliser la
représentation graphique (épaisseur et couleur du trait, marquage des points, etc).
• show() affiche la représentation graphique (dans une nouvelle fenêtre) construite à l’aide de la
fonction plot.
En mode non-interactif, l’affichage est bloqué jusqu’à la fermeture effective de la fenêtre.
Le code ci-dessous
import matplotlib . pyplot as plt
# c r é a t i o n de l a l i s t e d e s a b s c i s s e s d e s p o i n t s de l a p a r a b o l e
parabole_x = [k for k in range( −4,5)]
# c r é a t i o n de l a l i s t e d e s o r d o n n é e s d e s p o i n t s de l a p a r a b o l e
parabole_y = [k ∗∗ 2 for k in parabole_x ]
# t r a c e r d i s c o n t i n u d e s p o i n t s de l a p a r a b o l e en r o u g e
plt.plot(parabole_x , parabole_y , "r−−", linewidth = 2, label = " parabole ")
# t r a c e r en p o i n t i l l é s d ’ un r e c t a n g l e b l e u (+ marqueur " diamond ")
plt.plot([ − 3,3,3, − 3, − 3],[5,5,17,17,5], "b:", label=" rectangle ", marker ="D")
# b o r n e s [ xmin , xmax , ymin , ymax ] de l a f e n ê t r e ( à t e s t e r : " e q u a l ")
plt.axis([ −5, 5, −1, 24])
# p l a c e m e n t d e s l é g e n d e s ( à t e s t e r : l o c = " upper l e f t " , " c e n t e r " , e t c )
plt. legend (loc = "best")
# affichage
plt.show ()
1
La documentation complète de la bibliothèque matplotlib est disponible à l’adresse http://matplotlib.org/contents.
html
Informatique MPSI/PCSI 23
2.4 NumPy Chapitre 2. Présentation de modules usuels
2.4 NumPy
Le module numpy2 est destiné au calcul numérique, notamment à la manipulation de tableaux multidi-
mensionnels.
• Un ndarray peut se définir par conversion d’une liste via la fonction array (le second argument -
optionnel - impose le type de tous les éléments du tableau).
>>> import numpy as np
>>> tab = np.array ([1, 2, 3, 4], float ) # t a b l e a u u n i d i m e n s i o n n e l
>>> tab
array ([ 1., 2., 3., 4.])
>>> type(tab)
<class ’numpy. ndarray ’>
2
La documentation complète du projet NumPy est disponible à l’adresse http://docs.scipy.org/doc/numpy/reference/
24 Informatique MPSI/PCSI
Chapitre 2. Présentation de modules usuels 2.4 NumPy
• Les tableaux array sont séquentiels : on accède à un élément du tableau par les indices qui le
placent dans le tableau (l’ordre est important).
>>> tab [0], tab [1], tab[−1]
(1.0 , 2.0, 4.0)
• La fonction arange permet de générer un array de nombres équirépartis dont on précise le premier
terme, le dernier terme (exclu !) et le pas (par défaut égal à 1).
>>> np. arange (2, 3, 0.2)
array ([ 2. , 2.2, 2.4, 2.6, 2.8])
• La fonction linspace permet de générer un array de nombres équirépartis dont on précise le premier
terme, le dernier (inclu !) et le nombre d’éléments du tableau.
>>> np. linspace (2, 2.8, 5)
array ([ 2. , 2.2, 2.4, 2.6, 2.8])
• La fonction zeros (resp. ones) permet de générer un array rempli de 0 (resp. 1). Si l’argument
d’entrée est un entier, le tableau est unidimensionnel, sinon l’argument est un tuple et dans ce cas
le tableau est au moins à deux dimensions.
>>> np.zeros (6)
array ([ 0., 0., 0., 0., 0., 0.])
• La fonction eye permet de générer une matrice identité de type array dont la taille est donnée en
entrée (sous la forme d’un entier).
Informatique MPSI/PCSI 25
2.4 NumPy Chapitre 2. Présentation de modules usuels
• La fonction diag permet de générer une matrice diagonale dont la diagonale est donnée en entrée
sous la forme d’une séquence. Un argument optionnel k permet de décaler la diagonale de |k| cases
vers la droite (si k > 0) ou vers le bas (si k < 0).
>>> vect = np.array ([1, 2, 3])
>>> np.diag(vect)
array ([[1 , 0, 0],
[0, 2, 0],
[0, 0, 3]])
• La fonction reshape permet de redimensionner un array. Le tableau originel n’est pas modifié : la
fonction reshape effectue d’abord une copie.
>>> tab = np. linspace (1 ,6 ,6)
>>> tab
array ([ 1., 2., 3., 4., 5., 6.])
• La fonction shape renvoie les dimensions d’un tableau array sous la forme d’un tuple.
>>> a = np.array ([[3 , 2], [4, 3]])
>>> np.shape(a)
(2, 2)
Les tableaux array ont été conçus dans le but de supporter les opérations vectorielles.
• Les opérateurs arithmétiques agissent comme des opérateurs terme-à-terme entre nombres et
tableaux ou sur des tableaux de même dimension.
26 Informatique MPSI/PCSI
Chapitre 2. Présentation de modules usuels 2.4 NumPy
• La fonction inv du sous-module linalg renvoie l’inverse d’une matrice carrée (inversible !).
>>> a = np.array ([[3 ,2] ,[4 ,3]])
>>> np. linalg .inv(a)
array ([[ 3., −2.],
[ −4., 3.]])
Informatique MPSI/PCSI 27
2.5 SciPy Chapitre 2. Présentation de modules usuels
2.5 SciPy
Le module scipy3 est destiné à l’usage scientifique. Il contient de nombreux sous-modules spécialisés
dans les domaines mathématiques suivants (liste non exhaustive) :
• l’intégration (scipy.integrate)
• l’optimisation (scipy.optimize)
• l’interpolation (scipy.interpolate)
3
La documentation complète du projet Scipy est disponible à l’adresse http://docs.scipy.org/doc/scipy/reference/
28 Informatique MPSI/PCSI
Partie II
Machines numériques
29
Chapitre 3
Machines numériques
I d’une unité arithmétique et logique (ALU : Arithmetic and Logic Unit) capable d’effectuer
des opérations (arithmétiques et logiques) de base,
I d’une unité de contrôle, organisant les autres unités du processeur,
I de registres, mémoires de petites tailles (plusieurs octets), à accès très rapide, contenant
"l’état du système" à un instant (cycle) donné (adresse de l’instruction en cours, données et
résultats intermédiaires, etc),
I d’une unité de calculs en virgule flottante (Floating Point Unit) permettant de faire des
calculs (approchés) dans (un sous-ensemble de) R,
I de mémoire cache dont la principale utilité est de réduire considérablement le temps d’accès
à la mémoire de stockage. Il s’agit d’un exemple de mémoire tampon (buffer ).
I son jeu d’instruction, i.e. l’ensemble des opérations qu’il est capable d’effectuer,
I la taille de ses registres (entre 8 et 128 bits actuellement)
I sa cadence (quelques GHz actuellement)
I son architecture physique (taille, agencement, nombre de coeurs)
31
3.1 Description d’une machine numérique Chapitre 3. Machines numériques
• la mémoire de masse
Il s’agit d’une mémoire non volatile (on dit aussi rémanente), pouvant être à la fois lue et écrite,
souvent de grande capacité.
Le dispositif physique de stockage de cette mémoire de masse est appelé disque dur. Ce terme
est parfois employé à tort puisque de nouvelles générations de "disques durs" ne contiennent plus
de disque physique. On fera donc la distinction entre les disques durs HDD (Hard Drive Disc),
contenant un support magnétique à rotation, et les "disques" durs SSD (Solid-State Disc), composés
de semi-conducteurs mais dépourvus de disque physique.
• les bus qui forment l’ensemble des systèmes de communication entre les composants.
Chaque bus est caractérisé par une fréquence propre et un nombre de bits transférables simultané-
ment.
• l’alimentation qui transforme un courant alternatif (220V efficaces) en courant continu (12V).
• l’écran, caractérisé par sa définition (nombre total de pixels à l’écran), sa résolution (densité de
pixels) fournie en ppi (pixels per inch) ainsi que par son temps de réponse.
I d’un radiateur (ou dissipateur), ayant pour rôle d’échanger de la chaleur entre le processeur
et du système,
I d’un ventilateur thermorégulé (dont la vitesse des pales varie selon les besoins du CPU).
• les cartes-filles (carte graphique, carte son, carte réseau, etc) qui permettent de compléter/améliorer
les performances de la carte-mère.
On pourra noter que les cartes graphiques actuelles contiennent un processeur, communément appelé
GPU (Graphics Processing Unit), soulageant le CPU en prenant par exemple en charge les calculs
de rendu de l’affichage, le rendu 3D, la mémoire vidéo,
• les périphériques externes tels que le clavier, la souris, les haut-parleurs, reliés au système par
différents ports.
3.1.2 La tablette
Comme l’ordinateur personnel (de bureau ou portable), une tablette contient : une carte-mère, un pro-
cesseur (soutenu généralement par un processeur graphique), de la mémoire-vive, de la mémoire de masse
sous la forme exclusive de «disque dur» SSD.
Certains composants de la tablette lui sont cependant plus spécifiques1 :
1
propos à nuancer en raison de la convergence technologique des ordinateurs portables, des tablettes et des smartphones.
32 Informatique MPSI/PCSI
Chapitre 3. Machines numériques 3.2 Le système d’exploitation
• assure la liaison entre les ressources matérielles d’une machine numérique et les applications infor-
matiques,
• fournit des points d’entrée génériques aux programmes pour les périphériques.
I la famille Microsoft Windows, avec les systèmes d’exploitation Windows XP, Vista, Seven, etc,
I la famille UNIX, avec les systèmes d’exploitation de la firme Apple (OS X, iOS), les systèmes
d’exploitation contenant un noyau Linux (Ubuntu, Android).
La structure de fichiers associe à chaque fichier des métadonnées contenant par exemple les droits d’accès
(lecture, écriture, exécution), la date du dernier accès, la taille du fichier, son propriétaire, le format du
fichier.
• les registres, inclus dans le CPU, et la mémoire cache inhérente au processeur ont des temps
d’accès très faibles grâce à des technologies de pointe. Leur coût important est le facteur limitant
leur capacité.
• A l’inverse, la mémoire de masse (HDD et dans une moindre mesure SSD) est relativement peu
coûteuse et a une très grande capacité. Son temps d’accès est cependant bien plus important que
celui des registres.
Informatique MPSI/PCSI 33
3.3 Compléments sur les différents types de mémoire Chapitre 3. Machines numériques
e
iqu
registres
log
o
chn
Tem
t te
ps
mémoire cache
coû
d’a
u&
ccè
s
ten
&c
con
apa
mémoire vive
du
cité
lité
abi
ri
Va
mémoire de masse
2
ces valeurs, données à titre indicatif, ne sont peut-être déjà plus d’actualité, selon les “lois” de Moore.
34 Informatique MPSI/PCSI
Chapitre 4
Représentation des nombres en machine
• L’entier ap est appelé bit de poids fort (ou encore MSB = “Most significant bit”).
• L’entier a0 est appelé bit de poids faible (ou encore LSB = “Least significant bit”).
Méthode 4.1.3
Il existe un algorithme pour déterminer le développement binaire d’un nombre entier.
Il suffit d’étudier les restes des divisions successives par 2, comme on le montre ci-dessous :
• 23 = 11 × 2 + 1
• 11 = 5 × 2 + 1
• 5=2×2+ 1
• 2=1×2+ 0
• 1=0×2+ 1
2
On en déduit alors le développement binaire de 23 : 10111 .
Pour de “petits” nombres, on peut utiliser ses connaissances des puissances de 2.
Par exemple : 23 = 16 + 4 + 2 + 1 = 1 × 24 + 0 × 23 + 1 × 22 + 1 × 21 + 1 × 20 .
35
4.2 Représentation des nombres entiers Chapitre 4. Représentation des nombres en machine
Exercice 4.1.4
Déterminer la représentation binaire de 1234.
• Attention à ne pas confondre bit et byte : bien qu’un byte soit généralement stocké sur 8 bits
actuellement, ce n’est pas un règle : sur certains systèmes, un byte varie entre 5 et 9 bits.
• Il n’est pas rare de voir des confusions dans les unités de mesure de l’information : 1 Kio (lire
kibi-octets et non kilo-octets) ne désigne pas 1000 octets mais 210 = 1024 octets.
Les entreprises de télécommunication entretiennent d’ailleurs cette confusion.
Les notations Mi, Gi, Ti sont les préfixes respectifs des ordres de grandeur 220 , 230 et 240 .
• Les entiers signés (entiers relatifs) sont eux-aussi représentables exactement en mémoire :
– le bit de poids fort est dédié au signe de l’entier : 0 pour un entier positif, 1 pour un entier
strictement négatif,
– le reste des bits est dédié à la représentation binaire de la valeur absolue de l’entier.
Exemple 4.2.1
Sur 8 bits, l’entier −115 est enregistré en machine sous la forme de l’entier signé suivant :
1 111 0011
En effet :
115 = 64 + 32 + 16 + 2 + 1 = 26 + 25 + 24 + 21 + 20 .
36 Informatique MPSI/PCSI
Chapitre 4. Représentation des nombres en machine 4.2 Représentation des nombres entiers
Exercice 4.2.2
(i) Sur 8 bits, quel est le plus grand entier non signé qu’on peut représenter ?
(ii) Sur 8 bits, quels sont les plus petit et plus grand entiers signés qu’on peut représenter ?
(i) Sur N bits, on peut représenter tous les entiers de 0 à 2N − 1 sous la forme d’entiers non signés.
(ii) Sur N bits, on peut représenter tous les entiers −2N −1 + 1 à 2N −1 − 1 sous la forme d’entiers
signés.
ii) la compatibilité avec les opérations arithmétiques de base, illustrée par l’exemple ci-dessous.
Les représentations respectives de 7 et −5 sous forme d’entiers signés, codés sur 8 bits, sont :
Exemple 4.2.4
(i) La représentation en complément à un d’un entier négatif x sur N bits coïncide avec la représen-
tation sur N bits de l’entier positif 2N − 1 − |x| sous la forme d’entier (non signé).
(ii) La représentation en complément à deux d’un entier négatif x sur N bits coïncide avec la
représentation sur N bits de l’entier positif 2N − |x| sous la forme d’entier (non signé).
Informatique MPSI/PCSI 37
4.2 Représentation des nombres entiers Chapitre 4. Représentation des nombres en machine
Démonstration :
(i) Soit x un entier négatif. En sommant les représentations en complément à un des nombres x et |x|, on obtient
11 . . . 11 i.e. la représentation binaire de l’entier 2N − 1.
| {z }
N bits
(ii) Triviale.
Exemple 4.2.6
Additionnons les nombres 7 et −5, codés sur 8 bits, à l’aide de leur représentation en complément à
deux.
(ii) Puisque :
0000 011112 + 1111 10112 = 1 0000 00102 ,
la somme 7 + (−5) est alors représentée en machine sous la forme :
0 000 0010
Proposition 4.2.7
Sur N bits, on peut représenter en complément à deux tous les entiers entre −2N −1 et 2N −1 − 1.
Démonstration :
• On dispose de N − 1 bits pour les entiers positifs (le bit de poids fort est nul), ce qui permet de représenter tous les
entiers entre 0 et 2N −1 − 1.
• Tout nombre négatif x représentable possède une représentation en complément à deux dont le bit de poids fort est
1. Cette représentation coïncide avec la représentation binaire de l’entier positif (non signé) 2N + x compris entre :
2 2
2N −1 = 1 00
| .{z
. . 00} et 2N − 1 = 1 11
| .{z
. . 11}
N −1 bits N −1 bits
2N −1 − 2N ≤ x ≤ −1 i.e. − 2N −1 ≤ x ≤ −1.
• On en déduit alors que l’ensemble des entiers représentables sur N bits en complément de deux est :
J−2N −1 , 2N −1 − 1K.
38 Informatique MPSI/PCSI
Chapitre 4. Représentation des nombres en machine 4.3 Représentation de nombres réels
Cette notation constitue une représentation d’un nombre décimal (positif ) en binaire.
Remarque 4.3.2
Les nombres réels admettant une représentation binaire à virgule sont nécessairement décimaux, mais
cette condition n’est cependant pas suffisante : 0,1 n’admet pas de représentation binaire à virgule.
Exemple 4.3.3
Le réel 3, 8125 est codé (en virgule fixe), sur 2 × 8 bits (8 pour la partie entière et 8 pour la partie
fractionnaire ) sous la forme :
2
0000 0011, 1101 0000
10 2
En effet : 3 = 0011 .
De plus, on pourra remarquer que :
D’où la représentation en virgule fixe de 0, 8125 sur 8 bits (+1 pour le zéro avant la virgule) :
2
0, 1101 0000 .
La représentation en virgule fixe était utilisée dans les années 1970, avant l’apparition de la représentation
en virgule flottante. Elle est parfois utilisée dans les systèmes ne disposant pas d’une unité de calcul
en virgule flottante. On l’utilise actuellement pour améliorer la précision de certains calculs ou d’en
augmenter la vitesse.
Cette représentation pose cependant quelques problèmes dont :
• le gaspillage des bits à gauche de la virgule pour les petits nombres en valeur absolue,
• le gaspillage des bits à droite de la virgule pour les nombres dont la partie décimale est petite,
Exercice 4.3.4
Informatique MPSI/PCSI 39
4.3 Représentation de nombres réels Chapitre 4. Représentation des nombres en machine
Définition 4.3.5
On dit qu’un réel x non nul est représentable en virgule flottante suivant la norme IEEE 754 par le
triplet (s, e, m) s’il admet l’écriture scientifique en base 2 suivante :
x = (−1)s × m × 2e .
• e, codé sur 8 bits en simple précision et sur 11 en double précision est appelé l’exposant de x.
Afin d’éviter la comparaison d’entiers signés représentés en compléments de deux, on ne stocke
pas directement l’exposant e en machine : on le décale d’un entier d de façon à ce que l’entier
e + d soit strictement positif.
Les tableaux suivants présentent les principales conventions en simple et double précisions :
40 Informatique MPSI/PCSI
Chapitre 4. Représentation des nombres en machine 4.3 Représentation de nombres réels
Remarque 4.3.7
En plus de ces conventions, la norme IEE 754 définit quatre modes d’arrondi pour les calculs en virgule
flottante:
• l’arrondi vers 0 : arrondi par excès (resp. par défaut) pour les flottants négatifs (resp. positifs)
Exemple 4.3.8
Représentons le nombre −6, 625 en simple précision selon la norme IEEE 754 :
2
(i) La représentation binaire de 6, 625 est 110, 101 .
2 2
(ii) On procède alors à la normalisation de la mantisse : 110, 101 = 1, 10101 × 22 .
2
(iv) On décale l’exposant et on le stocke sur 8 bits : 2 + d = 1000 0001 .
(v) Conclusion : le nombre −6, 625 est stocké en machine en simple précision sous la forme :
L’erreur absolue est définie comme le produit de l’erreur relative (définie ci-dessus) par 2e où e
désigne l’exposant dans l’écriture en virgule flottante.
Informatique MPSI/PCSI 41
4.3 Représentation de nombres réels Chapitre 4. Représentation des nombres en machine
où :
• machine désigne l’epsilon machine, i.e. la distance entre 1. et le flottant qui le suit.
• dmin désigne la plus petite distance entre deux flottants consécutifs strictement positifs.
• dmax désigne la plus grande distance entre deux flottants consécutifs strictement positifs.
Exercice 4.3.9
1. Déterminer les représentations en machine (en simple précision) des nombres suivants : 0, 1, le
successeur de 1, le prédécesseur de 1, le plus petit flottant strictement positif, le plus grand flottant
positif et les infinis.
2. Retrouver par le calcul tous les résultats en simple précision du paragraphe ci-dessus.
42 Informatique MPSI/PCSI
Chapitre 4. Représentation des nombres en machine 4.4 Exercices
4.4 Exercices
Exercice 4.4.1
(iii) Déterminer la représentation binaire des nombres suivants : 12, 24, 48, 37, 142 et 1000.
Exercice 4.4.2
Effectuer les calculs suivants dans N :
2 2 2 2 2 2 2 2
a. 110101001 + 1111 . b. 1111111 + 101010 . c. 1111 × 1111 . d. 110101001 × 1010 .
Exercice 4.4.3
Quels sont tous les entiers relatifs représentables en compléments à deux sur 16 bits ? Quel est leur
nombre ?
Exercice 4.4.4
1. Quelle structure algébrique (groupe, anneau, corps) l’ensemble des entiers naturels écrits sur 8 bits
forme-t-il ?
2. Même question pour l’ensemble des entiers relatifs écrits sur 8 bits en complément à deux.
Exercice 4.4.5
1. Ecrire en Python une fonction decimal_to_binary prenant en entrée un entier naturel n (en base
10, de type int) qui renvoie la représentation binaire de n sous la forme d’une chaine de caractères
(type str).
2. Ecrire en Python une fonction binary_to_decimal prenant en entrée une chaine de caractères
représentant un entier naturel en base 2 et renvoyant sa représentation en base 10 (type int).
Exercice 4.4.6
Ecrire un script Python (pas une fonction) permettant de déterminer quel est le plus grand entier
relatif (type int) représentable en machine.
Attention : ce script ne fonctionnera que sur les versions antérieures à la version 3.0 de Python. En effet, la nouvelle
version de Python permet de représenter des entiers de taille illimitée, ou plus précisément, limitée uniquement par
l’espace mémoire disponible.
Exercice 4.4.7
1. Déterminer le flottant représenté selon la norme IEEE 754 par le mot binaire :
1100010001101001001111000011100000000000000000000000000000000000
2. Déterminer la représentation en double précision des réels suivants selon la norme IEEE 754 :
7, 0 121, 125 98, 625 1, 046875.
Informatique MPSI/PCSI 43
4.4 Exercices Chapitre 4. Représentation des nombres en machine
Exercice 4.4.8
Déterminer un nombre réel qui ne puisse pas être représentable en virgule flottante, quelle que soit la
précision.
Exercice 4.4.9
2. Déterminer l’erreur relative obtenue d’un calcul formé d’un million de multiplications.
3. Déterminer les plus grande et plus petite erreurs absolues (strictement positives) obtenues en som-
mant ou multipliant deux flottants (sans dépassement de capacité).
Exercice 4.4.10
1. Prévoir les résultats des trois derniers calculs suivants effectués dans un shell Python :
2. Proposer une fonction permettant de décider si deux flottants sont suffisamment proches pour être
considérés comme égaux.
Exercice 4.4.11 (Illustration de phénomènes pathologiques sur une petite base de flottants)
On désigne par la notation F(2, 3, −2, 1) l’ensemble des flottants écrits en base 2, dont le signe est
codé sur 1 bit, la mantisse (normalisée) est codée sur 2 bits et dont l’exposant minimal est −2 et
l’exposant maximal est 1.
1. Représenter sur l’axe des réels l’ensemble des valeurs de F(2, 3, −2, 1).
On précisera :
• Le cardinal de cet ensemble (dont le nombre de flottants strictement positifs),
• le plus petit strictement positif, le plus grand flottant strictement positif,
• l’epsilon machine (i.e. la distance entre 1 et son successeur),
• la plus petite et la plus grande distance entre deux flottants consécutifs strictement positifs.
2. On note ⊕ et ⊗ les opérations de somme et produit en machine. On fixe le mode d’arrondi des
résultats sur F(2, 3, −2, 1) comme celui par défaut pour la norme IEEE 754 : l’arrondi au flottant
le plus proche.
Effectuer les opérations suivantes dans F(2, 3, −2, 1) illustrant les principaux écueils des calculs en
virgule flottante (notés entre parenthèses) :
5 5
(i) ⊕ (overflow)
2 2
1 3
(ii) ⊗ (underflow)
4 8
1
(iii) 2 ⊕ (absorption)
4
5 3 5 3
(iv) 3 ⊕ ⊕ − puis 3 ⊕ ⊕ − (non associativité des opérations de base).
16 2 16 2
44 Informatique MPSI/PCSI
Partie III
Algorithmique
45
Chapitre 5
Algorithmes classiques
5.1 Statistiques élémentaires
5.1.1 Moyenne d’une série statistique
n
1X
La moyenne arithmétique des réels x1 , x2 , . . . , xn , notée x, est définie par x = xk .
n
k=1
Lorsque la série statistique est donnée sous la forme d’un tableau, on peut calculer la moyenne de cette
série en n’effectuant un seul parcours, comme illustrer ci-dessous.
def moyenne (tab ):
""" r e n v o i e l a moyenne d e s v a l e u r s du t a b l e a u de nombres t a b """
sigma = 0
for nb in tab: # l a v a r i a b l e nb d é c r i t t o u s l e s é l é m e n t s de t a b
sigma += nb
return sigma/len(tab)
Moyenne des valeurs d’un tableau de nombres
On peut remarquer que l’opération de division (par la taille du tableau) n’est effectuée qu’après le calcul de la somme
des termes du tableau de façon à réduire le nombre d’opérations dites élémentaires.
On peut alors optimiser le calcul de la variance d’une série statistique en couplant "à la volée" le calcul de
la moyenne à celui de la variance, i.e. en un seul parcours de tableau, comme l’illustre le code ci-suivant.
def variance (tab ):
""" r e n v o i e l a v a r i a n c e d e s v a l e u r s du t a b l e a u de nombres t a b """
n = len(tab)
sigma , sigma_carre = 0, 0
for nb in tab: # l a v a r i a b l e nb d é c r i t t o u s l e s é l é m e n t s de t a b
sigma += nb
sigma_carre += nb ∗∗ 2
return ( sigma_carre /n) − (sigma/n ) ∗∗ 2
Variance des valeurs d’un tableau de nombres
Remarque : l’écart-type σ d’une série statistique est défini comme la racine de la variance.
47
5.2 Algorithmes de recherche séquentielle Chapitre 5. Algorithmes classiques
• si cette valeur est strictement supérieure à celle de max_actuel, elle devient la nouvelle valeur de la
variable max_actuel,
• sinon, le maximum des valeurs parcourues reste inchangé (on ne modifie pas la variable max_actuel),
# Pa rc our s du t a b l e a u
for k in range (1,n):
if tab[k] > max_actuel :
max_actuel = tab[k]
Une fois le tableau entièrement parcouru, on renvoie la valeur de max_actuel : cette variable contient le
maximum des valeurs du tableau.
La fonction ci-dessous illustre l’algorithme de recherche du maximum d’un tableau de nombres. On
pourra remarquer que ce code "lève" une erreur (à l’aide de l’instruction raise) lorsque le tableau est
vide : l’erreur très (trop !) classique IndexError.
# Initialisation
max_actuel = tab [0]
# P a r c o u r s du t a b l e a u
for k in range (1,n):
if tab[k] > max_actuel :
max_actuel = tab[k]
return max_actuel
48 Informatique MPSI/PCSI
Chapitre 5. Algorithmes classiques 5.2 Algorithmes de recherche séquentielle
• À chaque étape, on compare l’élément recherché x avec un élément d’indice k du tableau tab.
• Ne connaissant pas le nombre d’itérations nécessaires à trouver l’élément, on utilise une boucle
conditionnelle while.
while flag == False and k < n:
if tab[k] == x: La recherche continue tant que l’élément x n’a pas
flag = True été trouvé et qu’il reste à "tester" des éléments du
else: tableau tab.
k += 1
# Pa rc o u r s du t a b l e a u # P a r c o u r s du t a b l e a u
while flag == False and k < n: while k < n:
if tab[k] == x: if tab[k] == x:
flag = True return True
else: else:
k += 1 k += 1
return flag return False
La variante de l’algorithme utilise une propriété inhérente au langage Python voulant qu’une fonction ter-
mine après l’exécution de l’instruction return, ce qui a pour conséquence que les lignes suivant l’instruction
return ne sont pas lues par l’interpréteur. Cela permet en particulier de pouvoir sortir d’une boucle
(qu’elle soit conditionnelle ou non).
Informatique MPSI/PCSI 49
5.2 Algorithmes de recherche séquentielle Chapitre 5. Algorithmes classiques
m = (g+d)//2
if x == tab[m]: S’il y a égalité, on renvoie True pour signifier que l’élément x appartient
return True bien au tableau tab.
elif x < tab[m]: Sinon, on réduit "l’intervalle de recherche" au sous-tableau tab[g:m] ou
d = m − 1 tab[m+1:d+1], en modifiant la valeur des variables g et d (indices des
else: bornes gauche et droite de l’intervalle de recherche).
g = m + 1
• Cette recherche continue tant que l’élément recherché n’a pas été trouvé et qu’il reste des éléments
à comparer, d’où la nécessité d’utiliser une boucle conditionnelle.
Dans le cas où g=d, il ne reste plus qu’un élément à tester et on a m=g=d. S’il ne correspond pas
à celui recherché, la recherche doit terminer et l’une des alternatives du test conditionnel entraine
g>d.
Puisqu’on peut sortir d’une boucle à l’aide de l’instruction return, la condition (suffisante) de
sortie de boucle correspond au cas où il ne reste plus d’élément à comparer. On continue donc la
recherche tant que g<d (ce qui peut paraitre anecdotique voire paradoxal au premier abord, mais
qui se comprend parfaitement après réflexion).
• A la sortie de boucle, on renvoie False : en effet, si l’algorithme n’a pas terminé mais qu’on est
sorti de la boucle, c’est que l’élément recherché n’a pas été trouvé.
Le code ci-dessus présente une fonction booléenne de recherche dichotomique d’un élément x dans un
tableau trié tab.
# Initialisation
g, d = 0, len(tab) − 1
# Parcours dichotomique
while g <= d:
m = (g+d)//2
if x == tab[m]:
return True
elif x < tab[m]:
d = m − 1
else:
g = m + 1
return False
Algorithme de recherche dichotomique
50 Informatique MPSI/PCSI
Chapitre 5. Algorithmes classiques 5.2 Algorithmes de recherche séquentielle
• l’autre, noté j, indiquant l’indice du caractère du mot actuellement testé dans la chaine.
L’idée est de comparer, pour une valeur de i donnée, les caractères mot[j] et chaine[i+j], dans
l’hypothèse que le mot "commence" à l’indice i de la chaine, pour tout j variant entre 0 et
long_mot - 1 ( = len(mot) - 1).
Cette comparaison est effectuée tant :
• ou bien deux caractères sont distincts (et dans ce cas, on incrémente la variable i de façon à décaler
l’indice de départ du mot dans la chaine),
• ou bien tous les caractères testés correspondent deux-à-deux. Dans ce cas, puisque la variable j a
été incrémentée à chaque test "réussi", sa valeur en sortie de boucle est long_mot.
Informatique MPSI/PCSI 51
5.2 Algorithmes de recherche séquentielle Chapitre 5. Algorithmes classiques
52 Informatique MPSI/PCSI
Chapitre 6
Analyse algorithmique
6.1 Terminaison et correction d’algorithmes
6.1.1 Le problème de l’arrêt
Le problème de l’arrêt, qui consiste à savoir si un algorithme (un calcul de fonction) termine ou non,
est un problème non trivial : Alan Turing a prouvé en 1936 qu’il s’agit d’un problème algorithmique-
ment indécidable. En d’autres termes, il n’existe aucun moyen algorithmique de déterminer si un calcul
quelconque termine.
6.1.2 Terminaison
Définition 6.1.1
Prouver la terminaison d’un (segment d’) algorithme consiste à montrer qu’il s’exécute en temps
fini, i.e. qu’il réalise un nombre fini d’opérations.
Remarque 6.1.2
La seule structure itérative nécessitant une preuve de terminaison est la boucle conditionnelle (boucle
while).
Pour prouver la terminaison d’une telle boucle, on utilise la propriété mathématique suivante : “toute
suite strictement décroissante et minorée d’entiers est finie”.
Exemple 6.1.3
Montrons la terminaison de mystere(a,b) pour tout (a, b) ∈ N × N∗ .
def mystere (a,b):
r, q = a, 0
while r >= b:
r = r − b
q += 1
return q, r
La suite des valeurs de r est strictement décroissante (car b > 0), à valeurs entières et minorée par b.
La suite des valeurs de r est donc finie, ce qui assure la terminaison de l’algorithme.
Exemple 6.1.4 (Le problème de l’arrêt d’un algorithme est non trivial )
On ne sait pas prouver à l’heure actuelle la terminaison de syracuse(a) pour tout a ∈ N∗ .
def syracuse (x0):
x = x0
while x != 1:
if x%2 == 0:
x = x//2
else:
x = 3 ∗ x+1
return x
53
6.2 Complexité Chapitre 6. Analyse algorithmique
6.1.3 Correction
Définition 6.1.5
Prouver la correction d’un (segment d’) algorithme consiste à prouver qu’il fournit bien une solution
valide pour répondre au problème donné.
Pour prouver la correction d’un algorithme itératif, on utilise la notion d’invariant de boucle.
Définition 6.1.6
Un invariant de boucle est une assertion vraie quelque soit l’itération d’une boucle.
Elle est en particulier vraie à l’entrée et à la sortie d’une boucle.
Exemple 6.1.7
Montrons la correction de l’algorithme de l’exemple 6.1.3 en exhibant un invariant de boucle.
6.2 Complexité
6.2.1 Introduction
Pour comparer l’efficacité d’algorithmes associés à un même problème, on définit des outils de mesure de
leurs performances qui ne dépendent pas :
• de la machine sur lequel il est implémenté,
• des performances du processeur,
• des accès à la mémoire vive et de stockage,
• du langage de programmation dans lequel il est écrit,
• du compilateur/interpréteur utilisé.
On se place pour cela sur une machine idéalisée dont la mémoire est considérée comme infinie et l’accès
aux données se fait en temps constant. Les algorithmes seront souvent écrits en pseudo-code.
54 Informatique MPSI/PCSI
Chapitre 6. Analyse algorithmique 6.2 Complexité
Définition 6.2.1
On définit plusieurs notions de complexité algorithmique :
Pour évaluer la complexité temporelle d’un algorithme, on compte le nombre “d’opérations élémentaires”
effectuées par l’algorithme. C’est la nature du problème qui rend certaines opérations fonda-
mentales.
Exemple 6.2.2
Voici quelques exemples d’opérations considérées comme élémentaires :
Le temps d’exécution d’un algorithme dépend essentiellement de la taille de l’entrée, qu’on assimile à un
entier naturel.
Exemple 6.2.3
Il s’agit par exemple :
• du nombre n ou du nombre de bits nécessaire à écrire n dans le cas où l’entrée est un entier n,
Remarque 6.2.4
Le choix d’une structure de données est fondamental dans la conception d’un algorithme quant à son
efficacité.
Informatique MPSI/PCSI 55
6.2 Complexité Chapitre 6. Analyse algorithmique
un = Θ(vn ),
n→+∞
Remarque 6.2.7
• Nous n’utiliserons en informatique que des suites à valeurs positives, nous pouvons donc nous
passer des valeurs absolues dans la définition précédente.
• Si la suite (vn )n∈N ne s’annule pas, la suite u est dominée par v si la suite u
v est majorée.
Proposition 6.2.8
n3 = O(2n ) 2n = O(n!)
n→+∞ n→+∞
Pour évaluer la complexité asymptotique d’un algorithme, on compare sa complexité aux classes de com-
plexité asymptotique usuelles, de façon à en avoir un ordre de grandeur.
Définition 6.2.10
On dit qu’un algorithme de complexité T (n) s’exécute :
56 Informatique MPSI/PCSI
Chapitre 6. Analyse algorithmique 6.2 Complexité
Pour se donner une idée des différentes classes de complexité, calculons les temps d’exécutions
d’algorithmes ne réalisant que des opérations numériques sur un ordinateur personnel standard, capa-
ble de réaliser 1011 FLOPS 1 .
T (n)
Temps
log n n n log n n2 n3 2n
102 0.7 ns 1 ns 5 ns 0.1 µs 10 µs 4 × 1011
années
103 1 ns 10 ns 0.7 µs 10 µs 10 ms 10290
années
n
On présente ci-dessous (un ordre de grandeur de) la valeur de n atteinte en une minute par des algorithmes
de chacune des classes de complexité.
Exemple 6.2.11
Pour répondre à un problème donné (un problème de tri par exemple), on implémente deux algorithmes
distincts :
• un algorithme en n log2 n opérations (complexité quasi-linéaire) sur une première machine, ca-
pable d’effectuer 229 opérations par secondes (typiquement Pentium III 450),
• Le temps d’exécution de l’algorithme en n log2 n opérations, sur la machine la plus lente est de
0,625 secondes.
Remarque 6.2.12
Il ne suffit donc pas de travailler sur une machine puissante, encore faut-il développer de bons algo-
rithmes !
1
FLOPS est l’acronyme de floating point operations per second.
Informatique MPSI/PCSI 57
6.2 Complexité Chapitre 6. Analyse algorithmique
Exemple 6.2.13
On considère l’algorithme ci-dessous, écrit en pseudo-code, permettant de déterminer le plus grand
élément d’un tableau de nombres :
ALGORITHME : MAX_TAB
ENTREE : Tableau T
m <- T[0]
n <- nb d’élément de T
POUR k de 1 à n-1 FAIRE :
SI T[k] > m FAIRE :
m <- T[k]
FIN SI
k <- k+1
FIN POUR
SORTIE : Élément maximal m de T
• Déterminons la complexité (en temps) de cet algorithme.
Ici, l’opération fondamentale est la comparaison d’éléments. A chaque itération, on effectue une
comparaison. On effectue ainsi n − 1 comparaisons.
Cet algorithme s’exécute donc en Θ(n) opérations, i.e. sa complexité est linéaire.
58 Informatique MPSI/PCSI
Chapitre 6. Analyse algorithmique 6.3 Exercices
6.3 Exercices
Exercice 6.3.1
1. Écrire en Python, à l’aide d’une boucle while une fonction somme qui a pour argument d’entrée un
entier n et qui renvoie en sortie la somme des entiers de 1 à n.
Exercice 6.3.2
On considère l’algorithme suivant (où les arguments d’entrée x et n sont de type entier positifs) :
def square_and_multiply (x,n):
y, p = x, n
r = 1
while p > 0:
if p%2 == 0:
y = y ∗∗ 2
p = p//2
else:
r ∗= y
p −= 1
return r
Exercice 6.3.3
On considère l’algorithme suivant (où l’argument d’entrée x est de type entier) :
def floored_root (x):
a = 0
b = 1
c = 1
while c <= x:
a += 1
b += 2
c += b
return a
Informatique MPSI/PCSI 59
6.3 Exercices Chapitre 6. Analyse algorithmique
1. Évaluer la complexité, dans le meilleur et le pire des cas, de l’algorithme de recherche séquentielle
d’un élément dans un tableau.
1. Écrire en Python une fonction tri_bulle triant sur place, i.e. en modifiant le tableau fourni en
entrée, à l’aide de l’algorithme du tri à bulle.
3. Déterminer, dans le meilleur et le pire des cas, le nombre de comparaisons du tri à bulle.
4. Optimiser le code écrit à la question 1 de manière à ce que l’algorithme s’arrête dès lors que le
tableau est trié.
60 Informatique MPSI/PCSI
Chapitre 6. Analyse algorithmique 6.3 Exercices
Exercice 6.3.7
Le problème est de déterminer à partir de quel étage d’un immeuble, sauter par une fenêtre est fatal.
Vous êtes dans un immeuble à n étages (numérotés de 1 à n) et on dispose de k étudiants. Il n’y a
qu’une opération possible pour tester si la hauteur d’un étage est fatal : faire sauter un étudiant par
la fenêtre. S’il survit, vous pouvez le réutiliser ensuite.
Le but du problème est de proposer un algorithme pour trouver la hauteur à partir de laquelle un saut
est fatal (on renverra n + 1 si on survit encore en sautant du n-ème étage) en faisant le minimum de
sauts.
2. Écrire le meilleur algorithme que vous pouvez en déterminer sa complexité (en nombre de questions).
On peut y arriver en O(n) questions.
Informatique MPSI/PCSI 61
6.3 Exercices Chapitre 6. Analyse algorithmique
62 Informatique MPSI/PCSI
Partie IV
63
Chapitre 7
Bases de données relationnelles
7.1 Introduction
Le développement et la démocratisation rapide des ordinateurs ont très vite posé la problématique du
stockage, de la consultation (lecture) et de la modification de données.
• sur chaque ligne, on renseigne les informations idEx, titre, auteur, éditeur séparés par des tabula-
tions,
• lorsqu’un livre est emprunté pour la première fois, on complète la ligne du livre en question par les
champs nom, prénom, téléphone, adresse, date_emprunt,
• lorsqu’un livre est retourné, on ajoute, après tabulation, un dernier champ date_retour au bout de
la ligne correspondante,
• lorsqu’un livre est emprunté une nouvelle fois, on ajoute une nouvelle ligne en copiant toutes les
informations sur le livre qu’on complète par les informations de l’empruntant.
On suppose que l’application fonctionne “correctement” depuis 20 ans, que le nombre de personnes inscrites
à la bibliothèque est de 5000 personnes par an en moyenne et qu’un abonné emprunte en moyenne 2
ouvrages par mois.
2. On suppose que chaque caractère occupe 1 octet et qu’une ligne contient, en moyenne, 200 caractères.
Quelle est la taille approximative du fichier ?
réponse :
3. On suppose qu’un accès au fichier coûte 8 ms (temps moyen d’accès au disque dur), qu’une lecture
de ligne coûte 0,1 ms (temps mis pour lire les 200 caractères), et qu’une recherche sur la ligne pour
trouver le numéro de l’exemplaire ou le nom et prénom de l’abonné coûte 0,01ms.
65
7.2 Modèle de données relationnelle - vocabulaire Chapitre 7. Bases de données relationnelles
4. On suppose qu’une personne est abonnée depuis l’origine de l’application. Elle prévient le bibliothécaire
que son nom est mal orthographié. Quel est le nombre moyen de lignes à modifier dans tout le fichier
pour corriger cette erreur.
réponse :
• recherche d’information,
• taille du fichier
• Les données sont manipulées par les opérateurs de l’algèbre relationnelle (cf. paragraphe 7.3).
• La cohérence des données de la base est définie par des contraintes d’intégrité.
Définition 7.2.1
Un attribut est un identifiant (un nom) décrivant une information stockée dans une base.
66 Informatique MPSI/PCSI
Chapitre 7. Bases de données relationnelles 7.2 Modèle de données relationnelle - vocabulaire
Exemple 7.2.2
Le nom de famille d’une personne, son âge, le code postal d’une ville sont des attributs.
Définition 7.2.3
Le domaine d’un attribut est l’ensemble (fini ou non) de ses valeurs possibles.
Exemple 7.2.4
Le domaine de l’attribut code postal est l’ensemble des nombres à 5 chiffres dont les deux premiers
varient entre 01 et 99.
Définition 7.2.5
Une relation est un sous-ensemble du produit cartésien de n domaines d’attributs.
• Elle est représentée sous forme de table à deux dimensions dans laquelle les n attributs corre-
spondent aux titres des n colonnes.
Un schéma de relation précise le nom de la relation ainsi que la liste des attributs avec leurs
domaines respectifs.
Exemple 7.2.6
La table suivante
personne
idSecu nom prenom
1 62 05 99 152 424 31 Banner Bruce
1 62 08 99 268 011 22 Parker Peter
2 65 12 99 280 042 18 Stacy Gwen
Définition 7.2.7
On appelle degré d’une relation son nombre d’attributs.
Définition 7.2.8
On appelle occurrence (ou encore n-uplet ou tuple) un élément de l’ensemble défini par une relation.
• Une occurrence est représentée par une ligne de la table représentant la relation.
Définition 7.2.9
On appelle cardinalité d’une relation son nombre d’occurrences.
• La cardinalité d’une relation est alors le nombre de lignes de la table représentant la relation.
Définition 7.2.10
On appelle clé candidate d’une relation un ensemble (minimal) d’attributs de la relation permettant
de distinguer deux occurrences.
• À deux occurrences distinctes d’une relation correspondent deux valeurs distinctes de clé candi-
date.
Informatique MPSI/PCSI 67
7.3 Algèbre relationnelle et instructions MySQL Chapitre 7. Bases de données relationnelles
Définition 7.2.12
La clé primaire d’une relation est une clé candidate choisie pour caractériser chaque occurrence
d’une relation.
• Pour signaler la clé primaire d’une relation, on soulignera ses attributs dans le schéma de relation.
Définition 7.2.14
Dans une relation, une clé étrangère est un ensemble d’attributs (clé) qui constitue une clé candidate
pour une autre relation.
Définition 7.2.16
On appelle schéma relationnel l’ensemble des schémas de relation (en précisant les clés étrangères)
et base de données relationnelle l’ensemble des occurrences des différentes relations du schéma
relationnel.
Exercice 7.2.17
Définir un schéma relationnel à partir la figure suivante (modèle entité-association) :
ETUDIANT
COURS
idEtudiant 1,n SUIVRE 0,n
idCours
nom note
intitule
prenom
68 Informatique MPSI/PCSI
Chapitre 7. Bases de données relationnelles 7.3 Algèbre relationnelle et instructions MySQL
Exemple 7.3.2
On considère la relation suivante :
personne
idPers nom prenom
1 Euler Leonhard
2 Gauss Carl Friedrich
3 Serre Jean-Pierre
4 Grothendieck Alexander
5 Curie Marie
6 Curie Pierre
On obtient la relation suivante, après sélection de toutes les occurrences ayant un identifiant (idPers)
strictement supérieur à 3 :
σidPers>3 personne
idPers nom prenom
4 Grothendieck Alexander
5 Curie Marie
6 Curie Pierre
7.3.2 La projection
Définition 7.3.3
La projection est une opération qui, à une relation R et une liste d’attributs A1 , . . . , An , associe
(génère) une relation, notée ΠA1 ,...,An R, regroupant toutes les occurrences de R mais où tous les
attributs de R autres que A1 , . . . , An ont été supprimés.
La requête MySQL associée est :
SELECT A_1 , ..., A_n FROM R
Pour supprimer les doublons dans la nouvelle relation, on place le mot-clé DISTINCT avant la liste
d’attributs sur laquelle s’effectue la projection.
Exemple 7.3.4
Les projections respectivement sur les attributs nom et prenom, et sur l’attribut nom de la relation
personne fournissent les relation suivantes :
SELECT nom , prenom FROM personne SELECT DISTINCT nom FROM personne
Informatique MPSI/PCSI 69
7.3 Algèbre relationnelle et instructions MySQL Chapitre 7. Bases de données relationnelles
Exemple 7.3.6
On considère les relations suivantes :
personne
nom peuple
Gandalf Maiar
Elendil Hommes
cadeau
artefact type créateur
Narsil épée Telchar
Narya anneau Celebrimbor
personne × cadeau
nom peuple artefact type créateur
Gandalf Maiar Narsil épée Telchar
Gandalf Maiar Narya anneau Celebrimbor
Elendil Hommes Narsil épée Telchar
Elendil Hommes Narya anneau Celebrimbor
70 Informatique MPSI/PCSI
Chapitre 7. Bases de données relationnelles 7.3 Algèbre relationnelle et instructions MySQL
Exemple 7.3.9
On considère les relations suivantes :
mathematicien physicien
nom prenom nom prenom
Newton Isaac Newton Isaac
Leibnitz Gottfried Hawking Stephen
Abel Niels Leibnitz Gottfried
mathematicien ∪ physicien
nom prenom
Newton Isaac
Leibnitz Gottfried
Abel Niels
Hawking Stephen
7.3.5 La jointure
Définition 7.3.11
La jointure est une opération qui, à deux relations R1 et R2 et un prédicat E, associe une relation,
notée R1 ./ R2 , dont les occurrences correspondent à celles de R1 × R2 vérifiant le prédicat E.
E
La requête MySQL associée est :
SELECT ∗ FROM
R_1 JOIN R_2 ON E
Remarque 7.3.12
La jointure correspond simplement à un produit cartésien suivi d’une sélection :
R1 ./ R2 = σE (R1 × R2 ) .
E
Cette opération de jointure est cependant optimisée en machine, en comparaison avec la requête
effectuant une sélection sur un produit cartésien.
Informatique MPSI/PCSI 71
7.3 Algèbre relationnelle et instructions MySQL Chapitre 7. Bases de données relationnelles
Exemple 7.3.13
On considère les relations suivantes :
personne cadeau
nom prenom age article age prix
Amaury Olympe 113 montre 24 239
Le Goff Maureen 24 peluche 2 39
El Rez William 2 livre 113 28
Highmore Freddie 24
On obtient la relation suivante après jointure entre les relations précédentes sur l’expression logique
“personne.age = cadeau.age”.
personne ./ cadeau
personne.age = cadeau.age
nom prenom age article prix
Amaury Olympe 113 livre 28
Le Goff Maureen 23 montre 239
El Rez William 1 peluche 39
Highmore Freddie 23 montre 239
La requête MySQL associée est :
SELECT ∗ FROM
personne JOIN cadeau ON personne .age = cadeau .age
L’erreur à éviter serait d’écrire la requête suivante (non optimisée) :
SELECT ∗ FROM personne , cadeau
WHERE personne .age = cadeau .age
7.3.6 La division
Définition 7.3.14
La division est une opération qui, à une relation R1 et une relation R2 tel que le schéma de R2
soit strictement inclus dans celui de R1 , associe (génère) une relation, notée R1 /R2 , dont les occur-
rences sont formées des parties d’occurrences de R1 qui, associées à toutes les occurrences de R2 ,
appartiennent dans R1 .
Exemple 7.3.15
On considère les relations suivantes :
enseignement etudiant
enseignant nom nom
Euler Laplace Laplace
d’Alembert Galois Galois
Gauss Laplace
Euler Galois
d’Alembert Laplace
Euler Poincarré
Gauss Poincarré
Les occurrences de la relation “enseignement/etudiant” correspondent aux enseignants ayant enseigné
à tous les étudiants de la relation etudiant :
enseignement/etudiant
enseignant
Euler
d’Alembert
72 Informatique MPSI/PCSI
Chapitre 7. Bases de données relationnelles 7.3 Algèbre relationnelle et instructions MySQL
Proposition 7.3.16
La division est caractérisée par la propriété suivante : R1 /R2 est la plus grande relation R (au sens
de l’inclusion) vérifiant R × R2 ⊂ R1 .
Remarque 7.3.17
Il n’existe pas de traduction directe en SQL ou MySQL de l’opérateur de division. Il est alors nécessaire
de reformuler grâce à l’équivalence suivante :
∀x, P(x) ⇔ ¬ ∃x, ¬P(x) .
“quels sont les enseignants qui ont enseigné à tous les étudiants ?”
en la requête :
“quels sont les enseignants qui ne vérifient pas qu’il existe un étudiant qui n’ait pas suivi leur cours ?”.
Informatique MPSI/PCSI 73
7.4 Systèmes de gestion de base de données (SGBD) Chapitre 7. Bases de données relationnelles
Exemple 7.3.19
• La clause GROUP BY permet de filtrer les lignes sur lesquelles s’effectuent le calcul.
Par exemple, si on considère la relation suivante :
devoir
nom prenom n_devoir note
Cooper Sheldon 1 20
Wolowitz Howard 1 12
Cooper Sheldon 2 20
Wolowitz Howard 2 14
• Remarque : une sélection sur des agrégats ne s’effectue pas avec le mot-clé WHERE mais avec
HAVING, qu’on placera, lorsqu’un regroupement est nécessaire, après le mot-clé GROUP BY.
• Les clients n’ont pas un accès direct à la base de données ; ils la consultent par des requêtes.
74 Informatique MPSI/PCSI
Chapitre 7. Bases de données relationnelles 7.5 Exercices
• Le serveur traite la base de données en la modifiant éventuellement et renvoie les résultats des
requêtes au client.
• plusieurs clients peuvent effectuer simultanément des requêtes ; le système de gestion de base de
données (SGBD) gère la cohérence des données (le client n’a pas accès aux fichiers contenant les
données).
requêtes
réponse
Dans une architecture client/serveur, le client est dit lourd : il partage du calcul (processing)
avec le serveur.
requêtes
système de gestion
client serveur SQL de base de données
navigateur applicatif relationnelles
(SGBDR)
réponse
7.5 Exercices
Exercice 7.5.1
ENSEIGNANT
COURS
idEnseignant 0,n DISPENSER 0,n
idCours
nom
intitule
prenom
Informatique MPSI/PCSI 75
7.5 Exercices Chapitre 7. Bases de données relationnelles
Exercice 7.5.2
On considère la base de donnée AirlineDB dont on présente le schéma relationnel (incomplet) :
pilot (numP, nameP, address, salary)
airplane (numAP, nameAP, capacity, localisation)
flight (numF, numP, numAP, depT, arrT, depH, arrH)
Exercice 7.5.3
On considère la base de donnée CarRentalDB dont on présente le schéma relationnel (incomplet) :
voiture (id_voiture, marque, modele, état)
client (id_client, nom, prenom, adresse, num_permis)
location (id_loc, id_voiture, id_client, date_debut, date_fin)
a. Déterminer le nombre de voitures louées par l’entreprise depuis la création de la base de données.
b. Déterminer les modèles qui n’ont jamais été loués.
c. Déterminer la marque de voiture la plus louée.
d. Déterminer le nombre de locations de voitures par marque. On triera le résultat par ordre
décroissant.
e. Déterminer le nombre de voitures louées par client.
f. Déterminer les clients (nom, prénom) qui n’ont pas encore rendu leur voiture de location.
La vacuité d’un champ se teste avec la syntaxe IS NULL.
g. Quels sont les véhicules qui ont été loués sur la durée la plus longue. On classera le résultat par
ordre décroissant de l’état.
76 Informatique MPSI/PCSI
Partie V
Analyse numérique
77
Chapitre 8
Résolution d’équations
8.1 Algorithme de recherche du zéro d’une fonction monotone
Le but de cet algorithme est de rechercher une valeur approchée du zéro d’une fonction continue
monotone sur un intervalle donné, lorsqu’il existe. La fonction f, les bornes a et b de l’intervalle ainsi
que l’erreur d’approximation epsilon sont fournis par l’utilisateur comme arguments d’entrée.
Le principe est qu’à chaque itération, on divise par deux la longueur de l’intervalle dans lequel on recherche
u+v
le zéro éventuel : si on le recherche dans l’intervalle [u, v] et si on note m = , il se situe dans l’intervalle
2
[u, m] ou dans l’intervalle [m, v], par monotonie de la fonction f. Il suffit donc, selon les cas, d’affecter à
la variable u ou la variable v, la valeur de m de façon à réduire l’intervalle de recherche à l’étape suivante.
m = (u+v)/2
if f(u) ∗ f(m) <= 0:
v = m
else:
u = m
La recherche dichotomique continue tant que la longueur de l’intervalle de recherche est supérieure au
double de l’erreur d’approximation epsilon. Une fois cette condition fausse, on renvoie en sortie de boucle
la valeur médiane du dernier intervalle [u, v] : la distance de ce réel à tout entier de l’intervalle [u, v] est
bien inférieure à epsilon.
Enfin, à l’aide de l’instruction assert, on s’assure de se trouver bien dans les conditions de réalisation de
l’algorithme :
• le zéro existe bien (et est unique) par le théorème de la bijection (f(a)*f(b) <= 0),
Les hypothèses de continuité et de monotonie ne sont quant à elles pas vérifiables : l’ensemble des flottants
(quelque soit la norme et la précision) est fini, il ne peut avoir la puissance du continu.
def recherche_zero_dichotomie (f,a,b, epsilon ):
""" Détermine , s ’ i l e x i s t e , une v a l e u r a p p r o c h é e à e p s i l o n p r è s
du z é r o d ’ une f o n c t i o n monotone s u r [ a , b ] , par d i c h o t o m i e """
assert a < b and f(a) ∗ f(b) <= 0 and epsilon > 0, \
"On ne peut pas conclure si f s’annule sur [a,b]"
u, v = a, b
while abs(v−u) > 2 ∗ epsilon :
m = (u+v)/2
if f(u) ∗ f(m) <=0:
v = m
else:
u = m
return (u+v)/2
79
8.2 Méthode de Newton Chapitre 8. Résolution d’équations
(ii) xn étant construit, on construit xn+1 comme étant l’abscisse du point d’intersection de l’axe des
abscisses avec la tangente à la courbe Cf (qu’on espère non verticale !) au point d’abscisse xn .
On illustre ci-dessous un cas où la suite définie par méthode de Newton converge un zéro de fonction.
x3 x2 x1 x0
La suite des approximations successives est définie par la relation de récurrence suivante :
f (xn )
xn+1 = xn − .
f 0 (xn )
En effet, la tangente à Cf au point d’abscisse xn a pour équation y = f 0 (xn )(x−xn )+f (xn ). L’abscisse du
point d’intersection de cette tangente avec l’axe des abscisses vérifie l’équation f 0 (xn )(x−xn )+f (xn ) = 0.
On obtient alors la relation de récurrence en résolvant l’équation.
On peut alors implémenter la méthode de Newton. Il faut noter que cette méthode nécessite la con-
naissance de la dérivée de la fonction dont on cherche à approximer un zéro (ce qui n’est pas toujours
possible).
Au lieu d’écrire un programme qui calculerait le n-ème terme de la suite, on propose ci-dessous la condition
d’arrêt suivante : lorsque la différence entre deux termes consécutifs est inférieure à une valeur ε fixée en
entrée, la méthode s’arrête et renvoie le dernier terme calculé.
def newton (f, fprime , x_0 , epsilon ):
""" Méthode de Newton : r e n v o i e une a p p r o x i m a t i o n , p r o c h e de x_0 ,
d ’ un z é r o de l a f o n c t i o n f , en c o n n a i s s a n t sa d é r i v é e f p r i m e """
x = x_0
while abs(f(x) / fprime (x)) > epsilon :
x −= (f(x) / fprime (x))
return x
Méthode de Newton
• La méthode de Newton ne converge que sous certaines conditions, comme par exemple que
l’abscisse du point de départ soit suffisamment proche du zéro recherché.
• La condition d’arrêt ne garantit pas une bonne précision mais fonctionne assez bien en pratique.
80 Informatique MPSI/PCSI
Chapitre 9
Méthodes d’intégration numérique
9.1 Méthode des rectangles et sommes de Riemann
La méthode des rectangles est une méthode d’analyse numérique permettant d’approximer l’intégrale
Z b
f (x) dx
a
d’une fonction f sur le segment [a, b] par la somme de Riemann
n−1
X
(xk+1 − xk )f (xk ),
k=0
où (xk )0≤k≤n est une subdivision de [a, b], i.e. a = x0 < x1 < · · · < xn = b.
a = x0 x1 x2 xn−2 xn−1 xn = b
On illustre ci-dessous la méthode des rectangles dans le cas particulier où le pas h est constant (on dit
b−a
aussi que la subdivision est équirépartie) : h = et pour tout k ∈ J0, n − 1K, xk = a + kh.
n
La somme de Riemann devient alors :
n−1
b−aX b−a
Sn = f a+k .
n n
k=0
81
9.2 Méthode des trapèzes Chapitre 9. Méthodes d’intégration numérique
n−1
b − a X f (xk ) + f (xk+1 )
Tn =
n 2
k=0
a = x0 x1 x2 xn−2 xn−1 xn = b
On peut remarquer que chacun des termes de cette somme est appelé deux fois, à l’exception de f (x0 ) et
f (xn ). En réindexant cette somme, on obtient une formule qui nous permet de ne calculer qu’une seule
fois chaque image :
n−1
" #
b − a f (x0 ) + f (xn ) X
Tn = + f (xk ) .
n 2
k=1
82 Informatique MPSI/PCSI
Chapitre 10
Équations différentielles - Méthode d’Euler
On rappelle que le théorème de Cauchy-Lipschitz assure que, sous certaines conditions (vérifiées par
F ), pour tout y0 ∈ R, il existe une seule et unique application y de classe C 1 sur [a, b] vérifiant :
(
y(a) = y0
∀t ∈ [a, b], y 0 (t) = F (t, y(t)).
La preuve de ce théorème n’étant pas constructiviste, nombreux sont les exemples d’équations différen-
tielles ordinaires qu’on ne sait pas résoudre de façon exacte.
Le but des schémas numériques est alors de fournir une méthode d’approximation de ces solutions
(dont l’existence est assurée par le théorème de Cauchy-Lipschitz). Les calculs étant destinés au machines
numériques, on tentera d’approximer une solution y (donnée avec condition initiale) en un nombre fini de
points.
On construit ainsi la suite (yk )0≤k≤n des approximations de (y(tk ))0≤k≤n grâce à la formule de récurrence
suivante : (
y0 = y(t0 ) = y(a) seule valeur exacte connue (condition initiale)
83
10.3 Implémentation de la méthode scalaire Chapitre 10. Équations différentielles - Méthode d’Euler
Graphiquement, cette méthode consiste à approximer y(tk+1 ) par l’ordonnée du point d’abscisse tk+1 de
la tangente à la courbe intégrale passant par le point de coordonnées (tk , y(tk )), comme l’illustre la figure
10.1 ci-dessous.
tangente
courbe intégrale
yk+1
y(tk+1 )
hF (tk , y(tk ))
y(tk )
tk tk+1
84 Informatique MPSI/PCSI
Chapitre 10. Équations différentielles - Méthode d’Euler 10.3 Implémentation de la méthode scalaire
Exemple 10.3.1
On peut par exemple l’utiliser pour résoudre sur [0, 1] par la méthode d’Euler l’équation différentielle
avec condition initiale : (
y(0) = 1
∀t ∈ [0, 1], y 0 (t) = y(t).
On commence par définir la fonction F : (t, y) 7→ y à l’aide de l’instruction lambda :
F = lambda t, y : y
On peut alors récupérer les listes des temps et des valeurs et comparer le second avec les valeurs
“exactes” :
>>> t_list , y_list = euler(F, 0., 1., 1., 0.25)
>>> t_list
[0, 0.25 , 0.5, 0.75 , 1.0]
>>> y_list
[1, 1.25 , 1.5625 , 1.953125 , 2.44140625]
>>> np.exp( t_list )
array ([ 1., 1.28402542 , 1.64872127 , 2.11700002 , 2.71828183])
Remarque : les fonctions du module numpy sont vectorielles, i.e. on peut leur appliquer un tableau de
valeur (ou une liste), elles renverront le tableau des images. Cela rend souvent service !
# préparatifs
plt.clf () # clear figure
plt.grid () # t r a c e r des axes
# r e p r é s e n t a t i o n de y _ l i s t en f o n c t i o n de t _ l i s t
plt.plot(t_list , y_list )
plt.show () # affichage
# remarquer l ’ i n t e r v e r s i o n de l ’ o r d r e d e s v a r i a b l e s dans F :
sol_scipy = odeint ( lambda y, t : y, 1., np. arange (0. ,1.25 ,0.25))
print ( sol_scipy )
on obtient :
solutions approchées par la bibliothèque scipy :
[[ 1. ]
[ 1.28402541]
[ 1.64872127]
[ 2.11700009]
[ 2.7182819 ]]
Informatique MPSI/PCSI 85
10.4 Implémentation de la méthode vectorielle Chapitre 10. Équations différentielles - Méthode d’Euler
0 2 !
x (t) x(t) (1 − 2y(t))
(S) ⇔ = 3
y 0 (t) −y(t) (1 − x(t))
⇔ U 0 (t) = F (t, U (t))
où F est la fonction :
2 !
x x (1 − 2y)
F : t, U = 7→ 3
y −y (1 − x)
86 Informatique MPSI/PCSI
Chapitre 10. Équations différentielles - Méthode d’Euler 10.4 Implémentation de la méthode vectorielle
Informatique MPSI/PCSI 87
10.5 Propriétés et limites Chapitre 10. Équations différentielles - Méthode d’Euler
Définition 10.5.2
Définition-Proposition 10.5.3
Remarque 10.5.4
Le choix du pas doit être une décision réfléchie et non juste un travail d’ajustement jusqu’à obtention
d’une approximation “acceptable” :
• S’il est trop petit, cela peut générer des erreurs d’approximation liées à la manipulation de
flottants.
Comme nous le verrons en TP, le choix du pas doit être réalisé en établissant un compromis entre la
précision souhaitée et les erreurs d’approximations liées à l’utilisation d’une machine numérique.
Si on ne trouve pas de compromis acceptable, on choisit une méthode d’ordre supérieur.
88 Informatique MPSI/PCSI
Chapitre 10. Équations différentielles - Méthode d’Euler 10.6 Exercices
10.6 Exercices
En général, il ne suffit pas qu’un schéma numérique soit convergent pour qu’il donne de bons résultats
sur n’importe quelle équation différentielle ordinaire.
Exercice 10.6.1 (Problème mal posé)
On considère le problème de Cauchy suivant, qu’on cherche à résoudre numériquement sur [1, 5] :
y(1) = 1
3 5
y 0 (t) = y(t) − .
t t3
1. Quelle est la forme des solutions de l’équation différentielle associée (sans la condition initiale
y(1) = 1) ?
3. Implémenter la méthode d’Euler pour résoudre cette équation différentielle de manière à représenter
graphiquement la solution exacte et la solution approchée comme illustré ci-après.
4. Déduire des questions 1 et 2 que le problème est mal posé pour être résolu par la méthode d’Euler.
Informatique MPSI/PCSI 89
10.6 Exercices Chapitre 10. Équations différentielles - Méthode d’Euler
1
1. Vérifier que la fonction φ : t 7→ −100 cos t + 10000 sin t + 100e−100t est l’unique solution
10001
du problème. Remarquer que cette fonction est bornée sur [1, +∞[.
3. Pour tout k ∈ J0, nK, k , on note l’erreur commise sur le calcul de yk . Exprimer k+1 en fonction
de k .
4. En déduire que pour h > 0, 02, la solution approchée oscille en “s’éloignant” de plus en plus de la
solution exacte.
1. Montrer qu’on peut appliquer la méthode d’Euler aux équations différentielles d’ordre 2 de la forme
y 00 (t) + a(t)y 0 (t) + b(t)y(t) = c(t).
2. On chercher à résoudre de manière approchée par la méthode d’Euler sur l’intervalle [1; 10] de
l’équation de Bessel ci-dessous. Écrire deux scripts Python afin de représenter graphiquement la
solution approchée et son portrait de phase.
y(1) = 1, y 0 (1) =
0
2 00 0 2 1
t y (t) + ty (t) + t − y(t) = 0.
4
90 Informatique MPSI/PCSI
Chapitre 11
Résolution de systèmes linéaires - Pivot de Gauss
11.1 Opérations élémentaires
Les opérations élémentaires réalisables sur une matrice a (représentée par un array) associée à un
système linéaire sont :
Remarque 11.1.1
• Chacune des ces opérations se traduit matriciellement par la multiplication à gauche par une
matrice inversible (matrice de permutation, de dilatation, de transvection).
• la première étape, dite phase de descente, consiste à transformer la matrice A en une matrice
triangulaire supérieure, en appliquant les mêmes opérations au vecteur Y qu’à la matrice A.
• la deuxième étape, dite phase de remontée1 , consiste à résoudre le système linéaire en déterminant
la valeur de chaque inconnue, de la dernière à la première.
1
La phase de remontée est parfois divisée en deux : une première étape qui transforme la matrice triangulaire en une matrice
diagonale, puis une seconde qui résout le système devenu trivial.
91
11.2 Algorithme du pivot de Gauss Chapitre 11. Résolution de systèmes linéaires - Pivot de Gauss
À l’entrée dans la j-itération, on commence par déterminer un pivot non nul2 ai,j parmi les coefficients
aj,j , aj+1,j , . . . , an,j . On permute alors la ligne j avec la ligne i. On élimine ensuite tous les coefficients
aj+1,j , . . . , an,j de la colonne j, à partir de la ligne j + 1.
D’un point de vue mathématique, tous les pivots potentiels se valent. D’un point de vue informatique, ce
n’est pas le cas : les calculs s’effectuant sur des flottants (même si les coefficients sont complexes), il faut
limiter les erreurs générées par ces calculs. c’est pourquoi on utilise la méthode du pivot partiel : on
choisit, parmi aj,j , aj+1,j , . . . , an,j , le plus grand coefficient en valeur absolue (ou module).
On propose alors une fonction qui détermine le pivot pivot partiel à la j-ème itération3 , puis qui permute
la ligne j avec la ligne où l’on a trouvé le pivot. On réalise bien-sûr la permutation des mêmes lignes sur
le vecteur colonne Y .
def choix_pivot (a, y, j):
i_pivot = j
for i in range(j+1, a.shape [0]):
if abs(a[i,j]) > abs(a[i_pivot , j]):
i_pivot = i
if i_pivot != j:
permute_lignes (y, j, i_pivot )
permute_lignes (a, j, i_pivot )
Élimination
Une fois le pivot partiel placé à la ligne j - on conserve la notation aj,j - on retrouve l’invariant en
ai,j
appliquant, pour tout i ∈ Jj + 1, nK, l’opération élémentaire Li ← Li − Lj .
aj,j
On propose alors une fonction qui élimine tous les coefficients de la colonne j situés sous la ligne j. Les
opérations élémentaires décrites ci-dessus sont aussi appliquées au vecteur colonne Y .
def elimination (a, y, j):
for i in range(j+1, a.shape [0]):
y[i] = y[i] − (a[i,j] / a[j,j ]) ∗ y[j]
a[i] = a[i] − (a[i,j] / a[j,j ]) ∗ a[j]
Remarque 11.2.1
On fera attention à ne pas intervertir les deux dernières lignes de la fonction précédente. En effet, en
permutant ces deux lignes, on modifie la ligne (i + 1) de la matrice, et ainsi le coefficient a[i,j]. On
ne réaliserait alors pas les mêmes opérations sur les deux membres de la (i + 1)-ème équation.
2
L’inversibilité supposée de la matrice A nous assure de l’existence d’un pivot non nul à chaque itération.
3
Attention au décalage de numération en mathématique et en informatique : ce pivot est sur la (j + 1)-ème colonne.
92 Informatique MPSI/PCSI
Chapitre 11. Résolution de systèmes linéaires - Pivot de Gauss 11.2 Algorithme du pivot de Gauss
Phase de descente
Après n − 1 itérations, la matrice A - supposée inversible - est transformée en une matrice triangulaire
(dont les coefficients de la diagonale sont tous non nuls).
def descente (a, y):
for j in range(a.shape [0] − 1):
choix_pivot (a, y, j)
elimination (a, y, j)
Il est aisé de résoudre le système résolvant successivement chacune des équations de la dernière ligne à la
première. Il suffit de remarquer que, si les valeurs de xi+1 , . . . , xn sont connues, alors on peut trouver xi :
n
1 X
xi = yi − ai,j xj .
ai,i
j=i+1
On implémente cette idée dans la fonction suivante qui résout un système linéaire dans le cas où la
matrice est triangulaire inversible. Remarquez qu’au lieu de construire un tableau des solutions, on utilise
le tableau des membres de droite : une fois la valeur xi calculée, on la stocke à la place de y[i].
def remontee (a, y):
n = a.shape [0]
for i in range(n −1, −1, −1):
# c a l c u l de x_i
for j in range(i+1, n):
y[i] = y[i] − a[i,j] ∗ y[j]
y[i] = y[i] / a[i,i]
return y
Informatique MPSI/PCSI 93
11.3 Complexité asymptotique Chapitre 11. Résolution de systèmes linéaires - Pivot de Gauss
fonction complexité
permute_lignes
choix_pivot
elimination
descente
remontee
pivot_Gauss
94 Informatique MPSI/PCSI
Chapitre 11. Résolution de systèmes linéaires - Pivot de Gauss 11.4 Annexes
11.4 Annexes
import numpy as np
Informatique MPSI/PCSI 95