Informatique Pour Tous en Classes Préparatoires Aux Grandes Écoles Wack Conchon Courant DeFalco Dowek Filliatre Gonnord ÉditionsEyrolles
Informatique Pour Tous en Classes Préparatoires Aux Grandes Écoles Wack Conchon Courant DeFalco Dowek Filliatre Gonnord ÉditionsEyrolles
Informatique Pour Tous en Classes Préparatoires Aux Grandes Écoles Wack Conchon Courant DeFalco Dowek Filliatre Gonnord ÉditionsEyrolles
Pour se procurer le livre en quantité contacter Sylvie Chauveau <SChauveau@Eyrolles.com> ou aller par exemple
sur :
http://www.eyrolles.com/Sciences/Livre/informatique-pour-tous-en-classes-preparatoires-aux-grandes-ecoles-9782212137002
Ref: https://wiki.inria.fr/sciencinfolycee/Informatique_pour_tous_en_classes_préparatoires_aux_grandes_écoles
Livre_silo 30 août 2013 16:32 Page 5
l es
rol
Ey
t Table des matières
gh
Table des matières . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
P
i
pyr
C
Machine, système d’exploitation et environnement de développement . . . . . . . . . . . . . . 3
1.1 Qu’est-ce qu’un ordinateur ? . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.1 Observations externes . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.2 L’ordinateur, une machine universelle . . . . . . . . . . . . . . . . . . . . 4
1.1.3 Architecture des ordinateurs . . . . . . . . . . . . . . . . . . . . . . . . 7
Co
C
Représentation des nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.1 Représentation des entiers naturels . . . . . . . . . . . . . . . . . . . . . 34
2.1.1 Représentation de l’information par des booléens . . . . . . . . . . . . . . 34
Livre_silo 30 août 2013 16:32 Page 6
es
Informatique pour tous
VI
l
rol
2.1.2 La numération à position et les bases . . . . . . . . . . . . . . . . . . . . 34
2.1.3 La base deux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.2 Représentation des entiers relatifs . . . . . . . . . . . . . . . . . . . . . . 39
2.2.1 Notation en complément à deux . . . . . . . . . . . . . . . . . . . . . . 39
2.2.2 Dépassements de capacité . . . . . . . . . . . . . . . . . . . . . . . . . 41
Ey
2.3 Représentation des nombres à virgule . . . . . . . . . . . . . . . . . . . . 45
2.3.1 L’arithmétique flottante . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.3.2 Quelques cas particuliers . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.3.3 Dépassements de capacité et problèmes de précision . . . . . . . . . . . . . 47
2.3.4 Les arrondis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
t
D
Algorithmique et programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
gh
C
Expressions : types et opérations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.1 Expressions et types simples . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.1.1 Expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
i
3.1.2 Entiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.1.3 Flottants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
pyr
60
3.1.4 Booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.2 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.2.1 Notion de variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.2.2 État et valeur d’une expression . . . . . . . . . . . . . . . . . . . . . . . 68
3.2.3 Déclaration et initialisation . . . . . . . . . . . . . . . . . . . . . . . . 70
3.2.4 Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Co
C
Instructions : langage minimal de l’algorithmique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.1 Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.1.1 Notion d’algorithme . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.1.2 Notion de programme . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.1.3 Langage minimal de l’algorithmique . . . . . . . . . . . . . . . . . . . . 85
4.1.4 Entrées/sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4.1.5 Séquence d’instructions . . . . . . . . . . . . . . . . . . . . . . . . . . 86
4.2 Instructions conditionnelles . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.2.1 Test simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Livre_silo 30 août 2013 16:32 Page 7
es
Table des matières
VII
l
rol
4.2.2 Indentation signifiante . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.2.3 Test avec alternative . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4.2.4 Tests imbriqués . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
4.3 Boucles conditionnelles . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.3.1 Nécessité des boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Ey
4.3.2 Syntaxe d’une boucle conditionnelle . . . . . . . . . . . . . . . . . . . . 96
4.3.3 Terminaison de boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.3.4 Invariant de boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.3.5 Boucle infinie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4.4 Boucles inconditionnelles . . . . . . . . . . . . . . . . . . . . . . . . . . 104
4.4.1 Boucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
4.4.2 Valeurs itérables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
t
4.4.3 L’itérable range . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.4.4 Interrompre une boucle . . . . . . . . . . . . . . . . . . . . . . . . . .
gh
108
4.4.5 Boucles imbriquées . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
4.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
C
Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
i
pyr
C
Notions de complexité et algorithmique sur les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.1 Complexité d’un algorithme . . . . . . . . . . . . . . . . . . . . . . . . . 144
6.1.1 Plusieurs algorithmes pour un même problème . . . . . . . . . . . . . . . 144
6.1.2 Complexité et notation O . . . . . . . . . . . . . . . . . . . . . . . . . 146
6.1.3 Différentes nuances de complexité . . . . . . . . . . . . . . . . . . . . . 148
Livre_silo 30 août 2013 16:32 Page 8
es
Informatique pour tous
VIII
l
rol
6.2 Structure de tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
6.2.1 Construction d’un tableau . . . . . . . . . . . . . . . . . . . . . . . . . 150
6.2.2 Accès aux éléments d’un tableau . . . . . . . . . . . . . . . . . . . . . . 151
6.2.3 Parcours de tous les éléments d’un tableau . . . . . . . . . . . . . . . . . . 153
6.3 Recherche dans un tableau . . . . . . . . . . . . . . . . . . . . . . . . . 155
Ey
6.3.1 Recherche séquentielle . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6.3.2 Recherche dichotomique dans un tableau trié . . . . . . . . . . . . . . . . 156
6.4 Recherche d’un mot dans un texte . . . . . . . . . . . . . . . . . . . . . . 158
6.5 Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
6.5.1 Création . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
6.5.2 Copie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
6.5.3 Dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
t
6.5.4 Transposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
6.5.5 Produit matriciel . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
gh
165
6.6 Mode de passage des tableaux . . . . . . . . . . . . . . . . . . . . . . . . 166
6.7 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
T
i
pyr
C
Pivot de Gauss et résolution de systèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
7.1 Résolution de AX = Y : principe du pivot . . . . . . . . . . . . . . . . . . 175
7.1.1 Le cas des systèmes triangulaires . . . . . . . . . . . . . . . . . . . . . . 175
7.1.2 Les transvections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
7.1.3 Le problème de la comparaison à zéro . . . . . . . . . . . . . . . . . . . 177
Co
Livre_silo 30 août 2013 16:32 Page 9
es
Table des matières
IX
l
rol
C
Résolution numérique d’équations sur les réels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
8.1 Méthode dichotomique . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.1.1 Principe théorique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.1.2 Terminaison, correction et complexité de l’algorithme . . . . . . . . . . . . 202
Ey
8.1.3 Mise en place, essais . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
8.2 Méthode de Newton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
8.2.1 Extraction de racine . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
8.2.2 Algorithme général, terminaison, correction et complexité . . . . . . . . . . 206
8.2.3 Évaluation de la dérivée . . . . . . . . . . . . . . . . . . . . . . . . . . 207
8.2.4 Mise en œuvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
8.3 Quelle méthode choisir ? . . . . . . . . . . . . . . . . . . . . . . . . . . 210
t
8.3.1 Bien cerner le contexte . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
8.3.2 Utiliser numpy/scipy . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
gh
8.4 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
C
Résolution numérique d’équations différentielles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
9.1 Méthode d’Euler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
i
pyr
Q
Bases de données. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
C
Algèbre relationnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
10.1 Limites des structures de données plates pour la recherche d’informations 256
10.2 Représentation dans le modèle relationnel . . . . . . . . . . . . . . . . . 257
10.3 Opérateurs sur le modèle relationnel . . . . . . . . . . . . . . . . . . . . 260
10.3.1 Description des recherches . . . . . . . . . . . . . . . . . . . . . . . . . 260
10.3.2 Opérateurs ensemblistes usuels . . . . . . . . . . . . . . . . . . . . . . . 261
Livre_silo 30 août 2013 16:32 Page 10
es
Informatique pour tous
X
l
rol
10.3.3 Projection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
10.3.4 Sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
10.3.5 Renommage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
10.3.6 Algèbre relationnelle. . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
10.4 Utilisation d’un gestionnaire de bases de données relationnelles . . . . . . 268
Ey
10.4.1 Description du langage SQL . . . . . . . . . . . . . . . . . . . . . . . . 269
10.4.2 Projection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
10.4.3 Sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
10.4.4 Opérations ensemblistes . . . . . . . . . . . . . . . . . . . . . . . . . . 270
10.4.5 Renommage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
10.5 Base de données et architecture logicielle . . . . . . . . . . . . . . . . . . 271
10.5.1 Architecture client-serveur . . . . . . . . . . . . . . . . . . . . . . . . . 271
t
10.5.2 Architecture trois-tiers . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
10.6 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
gh
273
C
Base de données relationnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
11.1 Clé primaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
11.1.1 Clé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
i
pyr
C
Algorithmique et programmation avancées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
C
Structure de pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
12.1 Opérations caractérisant une structure de pile . . . . . . . . . . . . . . . 300
12.2 Réalisation d’une structure de pile . . . . . . . . . . . . . . . . . . . . . . 302
12.2.1 Piles à capacité finie . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
12.2.2 Piles non bornées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
Livre_silo 30 août 2013 16:32 Page 11
es
Table des matières
XI
l
rol
12.3 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
12.3.1 Analyse des mots bien parenthésés . . . . . . . . . . . . . . . . . . . . . 306
12.3.2 Évaluation d’une expression arithmétique en notation polonaise inverse . . . . 308
12.3.3 Construction d’un labyrinthe parfait . . . . . . . . . . . . . . . . . . . . 310
12.4 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Ey
C
Algorithmes de tri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
13.1 Tri par insertion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
13.1.1 Réalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
13.1.2 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
13.2 Tri rapide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
t
13.2.1 Réalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
13.2.2 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
gh
13.3 Tri fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
13.3.1 Réalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
13.3.2 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
13.4 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
i
A A
Livre_silo 30 août 2013 16:32 Page 12
es
Informatique pour tous
XII
l
rol
A.8 Prise en main de phpMyAdmin . . . . . . . . . . . . . . . . . . . . . . . . 363
A.8.1 Création d’une table . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
A.8.2 Insertion de valeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
A.9 Clés primaires et clés étrangères . . . . . . . . . . . . . . . . . . . . . . . 368
A.9.1 Définition d’une clé primaire . . . . . . . . . . . . . . . . . . . . . . . . 368
Ey
A.9.2 Clé primaire auto-incrémentée . . . . . . . . . . . . . . . . . . . . . . . 371
A.9.3 Lien entre deux tables . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
A.9.4 Lancement de requêtes . . . . . . . . . . . . . . . . . . . . . . . . . . 373
A B
Compléments sur les entrées/sorties. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
B.1 Lecture et écriture dans des fichiers . . . . . . . . . . . . . . . . . . . . . 375
t
B.1.1 Lire les lignes d’un fichier . . . . . . . . . . . . . . . . . . . . . . . . . 375
B.1.2 Extraction des données dans une ligne . . . . . . . . . . . . . . . . . . . 376
gh
B.1.3 Écrire des données dans un fichier . . . . . . . . . . . . . . . . . . . . . 377
B.2 Lecture et écriture dans des images . . . . . . . . . . . . . . . . . . . . . 379
B.2.1 Lecture d’image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
B.2.2 Traitement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
B.2.3 Écriture dans une image . . . . . . . . . . . . . . . . . . . . . . . . . . 380
i
pyr
Références . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
Co
Livre_silo 30 août 2013 16:32 Page 13
l es
rol
Ey
t Avant-propos
gh
Proposer un enseignement spécifique d’informatique à tous les élèves de classes prépara-
toires aux grandes écoles scientifiques était une nécessité.
L’informatique est omniprésente dans le monde actuel. Chacun en a sa représentation
personnelle, enthousiaste ou méfiante, superficielle ou pointue. Pour comprendre en pro-
i
fondeur ce qu’on entend par informatique, il faut commencer par clarifier ce qu’elle n’est
pyr
pas.
Les anglophones la nomment souvent computer science, ce qui est un double contresens.
Premièrement, parce que l’ordinateur n’est pas qu’une machine à calculer (to compute en an-
glais), en tout cas pas au sens où on l’entend dans le langage courant. Certes, les premières
machines comme l’ENIAC ou l’EDVAC étaient utilisées exclusivement pour le calcul de
tables balistiques. Cependant, il n’y a qu’à regarder fonctionner quelques minutes un ordi-
Co
nateur de bureau pour voir la diversité des tâches qu’il peut réaliser. Même si elle n’était pas
bien exploitée par manque de ressources, cette polyvalence était déjà présente dans les tout
premiers ordinateurs. Comme leur nom français l’indique, ce sont plutôt des machines à
ordonner l’information, capables de stocker, de manipuler et de transmettre efficacement
n’importe quelles données pourvu qu’elles leur soient fournies dans un format adéquat.
Plutôt que de parler de polyvalence, on parlera donc d’universalité : un ordinateur peut
traiter les données de toutes les façons raisonnables que l’on peut imaginer.
Ensuite, parce que l’informatique n’est pas uniquement la science des ordinateurs : elle
est avant tout la science de l’information et de son traitement automatique. Elle démontre
que tout objet du monde réel, et de certains mondes abstraits comme les mathématiques,
peut se traduire par une représentation numérique, certes souvent imparfaite, mais que la
recherche ne cesse d’améliorer au fil des années. La question du codage de l’information
est bien antérieure à l’invention des ordinateurs, du dénombrement des moutons à l’aide
de petits cailloux jusqu’au code morse, en passant par les différents systèmes d’écriture et
de numération ou la programmation de motifs complexes dans les métiers à tisser.
Livre_silo 30 août 2013 16:32 Page 14
es
Informatique pour tous
XIV
l
rol
Les ordinateurs n’ont fait que confirmer a posteriori la validité et l’utilité de ces représen-
tations. Grâce à leur extraordinaire puissance de calcul, ils ont également donné en moins
de cent ans un essor inédit à des modes de raisonnement patiemment élaborés pendant
plusieurs millénaires. Cette pensée algorithmique, qui consiste à établir une méthode systé-
matique pour résoudre un problème, était déjà connue des Mésopotamiens. Elle a trouvé
Ey
une application directe lorsqu’il a fallu exprimer des façons de réaliser des processus com-
plexes pour des machines très rapides mais dénuées d’initiative et de compréhension.
À peine née, la science informatique s’est confrontée à l’un de ses plus grands défis, qui
l’occupe encore aujourd’hui : mettre au point des langages de communication communs à
l’homme et à la machine. Comme les langues naturelles, ceux-ci doivent permettre une
compréhension mutuelle entre deux entités ayant chacune leur propre représentation du
t
monde, mais à un degré bien plus important puisqu’aucune place ne peut être laissée à
l’ambiguïté.
gh
L’informatique est donc bien plus qu’une technologie de pointe, c’est la science qui réunit
ces quatre concepts de machine, d’information, d’algorithme et de langage et qui, les fai-
sant travailler ensemble, leur a donné la place qu’ils occupent aujourd’hui dans nos sociétés.
i
pyr
exemples, l’architecture globale des ordinateurs n’a pas changé depuis plus de 60 ans ; les
premiers ordinateurs représentaient déjà l’information en binaire ; les algorithmes de calcul
numérique présentés dans cet ouvrage sont tous connus depuis le siècle au moins ;
les langages de programmation reposent presque tous sur les mêmes cinq instructions
fondamentales. Il importe donc d’acquérir une culture informatique solide plutôt qu’un
vernis technologique ; cela ne dispense pas de mettre très régulièrement en pratique les
notions que l’on découvre, l’informatique ne pouvant s’apprendre qu’accompagnée d’une
expérience régulière de programmation.
Enfin, même en dehors de tout cadre professionnel, le jeune citoyen qu’est l’élève de classe
préparatoire sera fréquemment amené à rencontrer l’informatique dans sa vie quotidienne
que ce soit par le biais des réseaux de communication, de ses loisirs, de ses achats, de
ses interactions avec les administrations, etc. Comprendre comment fonctionnent ces sys-
tèmes et, à plus forte raison, en avoir soi-même programmé, même à un niveau modeste,
est une clé indispensable pour en profiter en tant qu’acteur et pas seulement en tant que
consommateur.
Livre_silo 30 août 2013 16:32 Page 15
es
Avant-propos
XV
l
rol
Structure de l’ouvrage
Le contenu de ce manuel se veut fidèle au programme officiel des deux années de classes
préparatoires aux grandes écoles scientifiques. Il aborde les différentes notions qui sont
pertinentes dans une formation scientifique, toujours avec la préoccupation de les replacer
Ey
dans le contexte plus général de la science informatique. L’ensemble du contenu a vocation
à être réutilisé pour le développement des Travaux d’Initiative Personnelle Encadrés.
• Dans la première partie Architecture matérielle et logicielle, on aborde les mécanismes
internes d’un ordinateur. On présente les modèles théoriques qui régissent son fonction-
nement, le système d’exploitation qui en permet l’usage quotidien, et les grands prin-
cipes d’un environnement de programmation (chapitre 1). On donne ensuite un premier
t
aperçu de la traduction numérique de l’information via la représentation des nombres en
machine (chapitre 2), qui aura des conséquences importantes lorsqu’on voudra effectuer
gh
du calcul numérique.
• Dans la deuxième partie Algorithmique et programmation, on présente les notions
clés de l’algorithmique (chapitres 3 et 4) en s’attachant systématiquement à démontrer
que les algorithmes que l’on écrit produisent le résultat attendu. On aborde également la
traduction de ces algorithmes sous forme de programmes. On présente ensuite la notion
i
Ce chapitre présente également les fonctions récursives, qui font partie du programme
pyr
Livre_silo 30 août 2013 16:32 Page 16
es
Informatique pour tous
XVI
l
rol
• On conclut ce manuel par une série de neuf propositions de travaux pratiques (annexe A)
et par une brève documentation pratique sur quelques fonctions utiles au traitement de
fichiers et à la production d’images (annexe B).
Chaque chapitre contient trois types de contenus :
Ey
• une partie de cours ;
• des sections intitulées « Savoir-faire », qui permettent d’acquérir les capacités essen-
tielles ;
• des exercices, avec leur corrigé lorsque nécessaire. Les exercices les plus difficiles sont
marqués d’un ou deux astérisques (*).
Trois types d’encadrés jalonnent cet ouvrage : ATTENTION signale un piège ou une erreur
fréquente chez les programmeurs débutants ; EN PRATIQUE mentionne des considérations
t
d’ordre pragmatique ; POUR ALLER PLUS LOIN propose des ouvertures vers des questions hors-
gh
programme.
Des compléments numériques à cet ouvrage sont proposés sur le site compagnon :
http://informatique-en-prepas.fr.
Avertissement
i
lequel on va programmer est incontournable, bien qu’in fine ce choix n’ait pas d’impor-
tance et que les compétences acquises dans un langage soient pour la plupart facilement
transposables à un autre.
Le programme officiel de cet enseignement donne d’emblée la réponse à cette question
puisqu’il impose le langage Python. Or ce langage existe en plusieurs versions incompa-
tibles entre elles (un programme écrit pour une version ne fonctionnera pas toujours dans
Co
l’autre). On distingue notamment les versions 2.x (celles dont le numéro commence par 2)
des versions 3.x, puisque c’est à la version 3.0 qu’ont eu lieu des changements incompa-
tibles. Dans cet ouvrage, tous les programmes sont écrits en Python 3.x ; lorsque cela est
nécessaire, un encadré précise les changements à apporter pour faire fonctionner ces pro-
grammes en Python 2.x.
Remerciements
Les auteurs tiennent à remercier Serge Abiteboul, Luc Albert, Jean-Pierre Archambault,
Bruno Arsac, Jean-Philippe Berne, Hugues Bersini, Christophe Boilley, Sylvain Delpech,
Francis Dorra, Pascal Lafourcade, Guillaume Le Blanc, Vincent Massart, Jean-Marie
Monier, Pierre-Étienne Moreau, Franz Ridde, Landry Salle, Marie-Dominique Siefert
et ierry Viéville pour leur aide précieuse au cours de la rédaction de ce livre ainsi que
l’équipe des éditions Eyrolles, Anne Bougnoux, Laurène Gibaud, Sébastien Mengin et
Muriel Shan Sei Fan.
Livre_silo 30 août 2013 16:32 Page 1
l es
rol
t Ey
Architecture matérielle
Première partie
gh
et logicielle
i
pyr
Dans cette partie, nous abordons les mécanismes internes d’un ordinateur.
Nous présentons les modèles théoriques qui régissent le fonctionnement
d’un ordinateur, le système d’exploitation qui en permet l’usage quotidien,
et les grands principes d’un environnement de programmation (chapitre 1).
Co
Livre_silo 30 août 2013 16:32 Page 2
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 3
l es
rol
1
t
Machine,
Ey
gh
système d’exploitation
et environnement de
i
pyr
développement
Co
Livre_silo 30 août 2013 16:32 Page 4
es
Informatique pour tous
4
l
rol
1.1 Qu’est-ce qu’un ordinateur ?
Contrairement aux autres machines du quotidien, rien dans l’observation d’un ordinateur
ne renseigne sur la façon dont il fonctionne. Comment savoir si telle machine est plus
intéressante qu’une autre sans en comprendre le fonctionnement ?
Ey
Pour tout informaticien, une tablette numérique et un téléphone portable intelligent
(smartphone) sont des ordinateurs, au même titre qu’un ordinateur de bureau. Pourquoi ?
Pour répondre à cette question, on observera dans un premier temps ces objets de l’exté-
rieur. On verra que ces observations sont insuffisantes pour caractériser vraiment ce qu’est
un ordinateur. En s’intéressant alors aux considérations qui motivent l’existence des or-
dinateurs, on expliquera comment les informaticiens peuvent apporter une réponse plus
t
satisfaisante à cette question et on montrera comment cela conduit à une architecture
commune à tous les ordinateurs actuels.
gh
1.1.1 Observations externes
Considérons donc une tablette numérique, un smartphone et un ordinateur de bureau dans
ce qu’ils ont de commun :
i
• Pour fonctionner, ils ont besoin d’une source d’énergie, en l’occurrence l’électricité.
pyr
• Ils reçoivent des informations de la part de l’utilisateur, par l’intermédiaire d’un clavier,
d’une souris, d’un microphone, d’un écran tactile ou d’un réseau (téléphonique, wifi ou
filaire).
• Ils émettent des informations, par l’intermédiaire de l’écran, de leur haut-parleur ou du
réseau.
Or d’autres objets partagent ces caractéristiques. Ainsi une automobile a également be-
Co
soin d’une source d’énergie pour fonctionner, reçoit des informations de son conducteur
(accélérer, freiner, clignoter) et lui renvoie des informations (vitesse, niveau d’huile, tem-
pérature du moteur). Mais à la différence d’un ordinateur, elle a pour objectif principal de
mouvoir ses utilisateurs et pas de traiter des informations.
Que penser alors d’un thermostat d’ambiance ? Ce dispositif fonctionne généralement sur
piles, reçoit des informations (température de consigne, calendrier), a pour but principal
de traiter ces dernières et transmet à la chaudière l’ordre de s’allumer ou de s’éteindre.
Pourtant, on ne peut le considérer comme un ordinateur : il est trop spécialisé.
Pour qu’on puisse parler d’ordinateur, on voit que les caractéristiques évoquées, notamment
la capacité de traiter des informations, sont nécessaires mais pas suffisantes.
Livre_silo 30 août 2013 16:32 Page 5
es
1 – Machine, système d’exploitation et environnement de développement
5
l
rol
des règles les régissant. C’est pour cela qu’ont été développés les systèmes de numération
et l’écriture.
Plus récemment, on a commencé à réaliser que cette information pouvait être traitée de
façon automatisée. Le métier à tisser mis au point par Jacquard en 1801 peut ainsi être
Ey
programmé pour tisser des motifs complexes à l’aide de cartes perforées.
t
i gh
pyr
metier-jacquard.jpg
Co
Figure 1.1
Mécanisme Jacquard au musée des arts et métiers de Paris
Livre_silo 30 août 2013 16:32 Page 6
es
Informatique pour tous
6
l
rol
Les mathématiciens ont également réalisé que de nombreux calculs pourraient être auto-
matisés. Dès 1642, Blaise Pascal avait ainsi conçu et réalisé une machine (la Pascaline) qui
effectuait les quatre opérations usuelles sur les entiers : addition, soustraction, multipli-
cation et division. Plus tard, vers le milieu du siècle, suite aux travaux de Babbage,
plusieurs machines à différences, des machines mécaniques, ont été produites pour calculer
Ey
et imprimer des tables de logarithmes.
Finalement, au siècle, on s’est demandé si l’on pouvait tout calculer. Peut-on calculer
toutes les fonctions des entiers dans les entiers ? Peut-on trouver une machine qui imprime
sur un ruban les chiffres successifs de n’importe quel réel ?
David Hilbert demande même en 1928 s’il existe une machine capable de décider si une
proposition mathématique est vraie ou fausse. Alonzo Church et Alan Turing répondent
t
indépendamment non à cette question, en 1936 et 1937 respectivement.
gh
L’article de Turing propose un modèle de machine (appelée aujourd’hui machine de Turing)
qui possède les caractéristiques suivantes :
1 Une machine de Turing possède un ruban infini sur lequel on a disposé des données.
Elle peut lire des données sur ce ruban, les traiter et en écrire d’autres. Au bout d’un
certain temps, il se peut qu’elle s’arrête, on peut alors lire le résultat du calcul sur le ruban
i
2 Pour tout procédé qui peut être calculé par un algorithme, il semble qu’il y ait une ma-
pyr
chine de Turing capable de le calculer (il est difficile de montrer que c’est effectivement le
cas si on ne sait pas définir précisément ce qu’est un algorithme ; cette dernière question
est justement une de celles auxquelles Turing tente de répondre).
3 Turing démontre qu’on peut construire une machine universelle, c’est-à-dire une ma-
chine capable de simuler toutes les autres. Pour l’utiliser, on dispose simplement sur son
ruban une description de la machine qu’on veut simuler ainsi que les données d’entrée
Co
de la machine à simuler.
4 Il démontre également qu’il existe cependant des problèmes que cette machine n’est pas
capable de résoudre : par exemple, décider si une proposition mathématique est vraie
ou même, beaucoup plus simplement, à partir de la description d’une machine et de ses
données d’entrée, décider à coup sûr si cette machine va s’arrêter ou non.
Cet article est extrêmement novateur car il considère que la description d’une machine
(ou d’un algorithme) peut en fait être considérée comme une donnée : la donnée d’entrée
d’une machine de Turing universelle.
Aujourd’hui, on considère ce point comme une caractérisation essentielle de ce qu’est un
ordinateur : un ordinateur est la réalisation concrète d’une machine de Turing universelle, c’est-
à-dire une machine traitant des informations et capable en principe de prendre comme donnée
d’entrée n’importe quel algorithme et de l’exécuter.
Livre_silo 30 août 2013 16:32 Page 7
es
1 – Machine, système d’exploitation et environnement de développement
7
l
rol
Un ordinateur de bureau, une tablette et un smartphone sont bien des ordinateurs : on
peut en effet leur faire exécuter des programmes arbitraires. Reste à expliquer comment ils
calculent.
Ey
POUR ALLER PLUS LOIN Les systèmes embarqués
Un thermostat d’ambiance est-il une machine universelle ? S’il s’agit d’un thermostat méca-
nique, certainement pas. Et dans le cas d’un thermostat électronique ? Du point de vue de
son utilisateur, ce n’est pas un ordinateur. Pourtant, il contient en son sein un véritable petit
ordinateur, qu’on appelle généralement un microcontrôleur. Cependant, il est prévu pour
exécuter le programme spécialisé qu’on lui a incorporé à la fabrication et non pour exécuter
des algorithmes arbitraires. On peut donc le considérer comme un ordinateur manquant
précisément de ce qui fait de lui un ordinateur !
t
De la même façon, un lecteur DVD de salon contient un ordinateur mais n’est pas fait non
plus pour exécuter des programmes arbitraires. Quant au boîtier d’un fournisseur d’accès à
Internet, c’est un ordinateur, dont le programme est régulièrement mis à jour. Cependant,
gh
l’utilisateur n’a pas le pouvoir de décider quel programme il exécutera.
On parle de systèmes embarqués pour désigner ces ordinateurs et programmes spécialisés.
pyr
Pour pouvoir exécuter des algorithmes arbitraires, les ordinateurs actuels sont tous bâtis
autour du même modèle architectural théorique, l’architecture de von Neumann, même si
la mise en œuvre pratique est un peu plus complexe.
(qui calculait des tables indiquant les paramètres de tir d’une batterie d’artillerie en fonc-
tion de la distance à la cible, du vent, etc.) dont la construction démarra en 1943. Son
architecture a été décrite dans un rapport de John von Neumann en 1945 et est depuis ap-
pelée « architecture de von Neumann ». Depuis près de 70 ans, à quelques variations près,
c’est l’architecture utilisée dans tous les ordinateurs, qu’il s’agisse de tablettes, smartphones,
ordinateurs portables ou de bureau.
Une machine suivant l’architecture de von Neumann est constituée :
• d’une mémoire vive ;
• d’un processeur qu’on peut conceptuellement décomposer en une unité de contrôle et
une unité de calcul arithmétique et logique (UAL) ;
• de dispositifs périphériques, appelés simplement périphériques ;
• d’un canal de communication entre la mémoire, le processeur et les périphériques, ap-
pelé le bus.
Livre_silo 30 août 2013 16:32 Page 8
es
Informatique pour tous
8
l
rol
La mémoire vive est une suite de chiffres binaires (bits), organisés en pratique en octets
(paquets de 8 bits) et en mots mémoire (64 bits sur les machines de bureau récentes). Elle
a les caractéristiques essentielles suivantes :
• Pas de sens a priori. Un mot dans la mémoire peut aussi bien représenter une instruc-
Ey
tion d’un programme, qu’un nombre entier ou la couleur d’un point d’une image. La
signification d’une valeur stockée dans la mémoire dépend donc entièrement de l’in-
terprétation qu’en fait son utilisateur, ce dernier étant en l’occurrence autant le reste de
l’ordinateur que l’être humain qui le commande.
• Inertie. La mémoire vive est inerte au sens où elle n’effectue aucun calcul.
• Accès direct. Tout mot mémoire possède une adresse (un nombre entier). On peut lire
ou écrire directement le contenu d’un mot mémoire d’adresse donnée, d’où le nom donné
t
en anglais à cette mémoire : Random Access Memory (RAM).
Les périphériques se présentent au reste de l’ordinateur sous la forme d’une mémoire sup-
gh
plémentaire : il s’agit de plages d’adresses sur lesquelles on peut écrire pour donner des
ordres au périphérique, ou lire pour en obtenir des informations. À la différence de la
mémoire vive, ils ne sont cependant pas nécessairement inertes et peuvent réagir aux ins-
tructions données : ainsi, une imprimante recevra des instructions via cette plage de mé-
moire, travaillera en conséquence et mettra certaines informations à disposition du reste
i
pyr
Le processeur est le cœur de l’ordinateur. De même qu’un chirurgien dans un bloc opé-
ratoire dirige les infirmiers pour qu’ils effectuent les tâches les plus simples pendant qu’il
effectue le travail le plus délicat, le processeur donne des ordres aux périphériques et à la
mémoire et est responsable de l’exécution du programme de l’ordinateur.
Co
Unité de contrôle
R0 R1 . . . Rn Mémoire à accès
.
Bus direct
(RAM et ROM)
UAL
Figure 1.2
Architecture de von Neumann
Livre_silo 30 août 2013 16:32 Page 9
es
1 – Machine, système d’exploitation et environnement de développement
9
l
rol
Le processeur dispose d’une toute petite mémoire, typiquement de l’ordre de quelques
mots à une centaine de mots, qu’on appelle des registres.
Grâce à son unité arithmétique et logique, il peut calculer la somme, la différence, le pro-
duit ou le rapport de nombres codés dans deux de ses registres et le stocker dans un troi-
Ey
sième. Il peut également effectuer des opérations logiques (disjonction, conjonction, né-
gation) sur des valeurs de vérité codées dans des registres.
Il peut enfin accéder à la mémoire, via le bus : pour tout couple de registres (R1 , R2 ), il
peut aller chercher dans la mémoire vive le contenu de la case mémoire dont l’adresse est
stockée dans le registre R1 et stocker le résultat dans R2 ou inversement stocker le contenu
de R2 dans la case mémoire dont l’adresse est contenue dans R1 .
C’est l’unité de contrôle du processeur qui effectue ces actions. Cependant, cette unité ne
t
contient pas le programme à exécuter : les instructions sont codées sous la forme d’une suite
gh
de bits stockée en mémoire. Plus précisément, l’unité de contrôle du processeur dispose
d’un registre particulier, généralement appelé P C (pour Program Counter) et exécute en
boucle la séquence d’actions suivante, résumée figure 1.3 :
1 Lire instruction. Aller lire le mot stocké à l’adresse mémoire donnée par le registre P C
et le stocker dans un registre spécial appelé IR (instruction register ou registre d’instruc-
i
tion).
Livre_silo 30 août 2013 16:32 Page 10
es
Informatique pour tous
10
l
rol
pour effectuer des actions de façon répétitive (« si tel registre contient une valeur non nulle,
reprendre l’exécution à l’instruction stockée à telle adresse »).
Ey
Cette présentation du processeur est en fait idéalisée.
D’une part, si sur les processeurs actuels un accès mémoire va bien chercher un mot, en
revanche les adresses mémoire sont comptées en octets et non en mots. Lors de chaque
cycle de décodage d’instructions, le P C est donc incrémenté de 4 sur un processeur 32 bits
(un mot de 32 bits étant sur 4 octets) et de 8 sur un processeur 64 bits.
D’autre part, le processeur n’attend pas qu’une instruction soit exécutée pour décoder la
suivante. On dit que les instructions passent dans un pipeline, comparable à une chaîne de
montage dans une usine.
t
Les traitements des différentes instructions se chevauchent donc temporellement, ce qui
accélère l’exécution du programme par le processeur. À l’heure actuelle, le pipeline est
gh
découpé de façon suffisamment fine pour que quelques dizaines d’instructions soient si-
multanément en cours de traitement.
Il y a cependant une différence majeure entre une chaîne de montage et le pipeline d’un
processeur. La chaîne de montage peut travailler sans arrêt, alors que les instructions de
branchement perturbent considérablement le fonctionnement du pipeline.
i
pyr
une grande souplesse. En effet puisqu’aucune structure n’est a priori imposée sur le contenu
de la mémoire, on peut stocker toute information codable numériquement. En particulier,
on peut indifféremment stocker en mémoire des programmes et des données.
Figure 1.3
Co
Incrémenter PC
Décoder instruction
Livre_silo 30 août 2013 16:32 Page 11
es
1 – Machine, système d’exploitation et environnement de développement
11
l
rol
Elle a en revanche au moins trois gros inconvénients :
• Une exécution séquentielle. Les programmes sont exécutés de façon séquentielle, ins-
truction après instruction. Une machine de von Neumann ne peut donc faire qu’une
seule chose à la fois. On pourrait cependant imaginer qu’en augmentant le nombre de
Ey
circuits électroniques du processeur, on pourrait effectuer plusieurs opérations en même
temps. Par ailleurs, on veut que les ordinateurs soient capables d’effectuer un calcul com-
plexe tout en décodant un fichier de musique et en le jouant, pendant que le navigateur
charge une page web. On verra comment les processeurs et les systèmes d’exploitation
s’affranchissent en pratique de cette limitation.
• Un goulet d’étranglement. Toutes les données et les instructions passent sur le bus mé-
moire. En pratique, les processeurs actuels sont tellement rapides que le bus a souvent
t
bien du mal à en suivre le débit. En conséquence, le processeur passe beaucoup de temps
à attendre. On exposera comment en pratique on résout ce problème.
gh
• Une faible robustesse. Les données et les programmes étant mélangés, l’architecture de
von Neumann est très fragile. Ainsi, il suffit qu’un bogue dans le programme conduise
à écrire une donnée à l’emplacement d’un programme pour que la machine se mette à
avoir un comportement inattendu, voire incompréhensible : au moment où elle lira cette
donnée, elle l’interprétera comme une instruction, alors que cela n’a aucun sens.
i
Exercice 1.1 Avec un processeur 32 bits, combien d’adresses mémoire peuvent-elles être référencées
dans un registre ? Et dans un registre 64 bits ?
pyr
En déduire pourquoi les ordinateurs de bureau actuellement en vente ont tous des processeurs 64 bits.
En 1975, Moore (un des trois fondateurs d’Intel) a prédit que le nombre de transistors des microproces-
seurs doublerait tous les 2 ans. Cette loi a été relativement bien vérifiée jusqu’ici (entre 1971 et 2000,
doublement tous les 1,96 années), même si on s’attend à se heurter à des limites physiques vers 2015.
En conséquence, on peut estimer que la quantité de mémoire d’un ordinateur double tous les deux ans.
Sous l’hypothèse (douteuse) que cette loi continue à être vérifiée, dans combien de temps les registres
64 bits seront-ils insuffisants ?
Co
Mise en œuvre
En pratique, pour réaliser une machine suivant l’architecture de von Neumann, un gros
problème se pose : comment survivre à une coupure électrique ? En effet, les mémoires
vives qu’on sait aujourd’hui construire pour un coût raisonnable ont besoin d’une alimen-
tation électrique permanente pour garder leurs données. On qualifie cette mémoire de
volatile pour signifier qu’elle perd son contenu en cas de coupure de courant. Or, on veut
que les données et programmes contenus dans un ordinateur subsistent malgré l’extinction
de la machine.
Livre_silo 30 août 2013 16:32 Page 12
es
Informatique pour tous
12
l
rol
Pour résoudre ce problème, on recourt à deux types de mémoires non volatiles :
• Mémoire morte. On sait construire des mémoires non volatiles offrant un accès en lec-
ture à vitesse raisonnable, mais pas d’accès en écriture (Read-Only Memory, ROM), ou
du moins qui ne peut être changée facilement (EEPROM). Ces mémoires sont en gé-
Ey
néral initialisées lors de la fabrication de la machine. Elles peuvent être utilisées pour
stocker des programmes mais pas les données utilisateur. Elles étaient notamment uti-
lisées dans les consoles de jeux vidéo et certains ordinateurs des années 1980, sous la
forme de cartouches enfichables dans la machine. Aujourd’hui, elles sont utilisées dans
les ordinateurs de bureau pour stocker un programme qu’on appelle le firmware (BIOS
ou UEFI). Un ordinateur de bureau possède donc à la fois une mémoire morte et une
mémoire vive. Certaines plages d’adresses correspondent à des données stockées en mé-
t
moire vive, d’autres à des adresses stockées en mémoire morte et le processeur accède
en lecture aux unes et aux autres indifféremment. Toute tentative d’écriture dans une
gh
adresse en mémoire morte se solde évidemment par un échec.
• Mémoire de masse. Pour sauvegarder les données, on ajoute un périphérique, appelé
mémoire de masse : autrefois un lecteur de bandes magnétiques, aujourd’hui un disque
dur (le plus fréquent sur les PC de bureau) ou une mémoire flash (le plus fréquent sur
les smartphones). Un disque dur est capable de stocker une grande quantité de données,
i
mais la lecture de celles-ci est environ mille fois plus lente que pour celles de la mémoire
vive. De plus, l’accès à ces données est relativement complexe : comme dans le cas de la
pyr
mémoire vive, les données qui y sont stockées ont toutes une adresse, mais le contenu
d’une adresse donnée n’est pas accessible par une simple instruction du processeur. On
stocke en général dans un disque dur non seulement les données de l’utilisateur mais
aussi tous les programmes, y compris le système d’exploitation de la machine.
Par ailleurs, les constructeurs cherchent à fabriquer des ordinateurs toujours plus rapides.
Pour cela, il s’agit de surmonter les problèmes inhérents à l’architecture de von Neumann.
Co
Aucune solution miracle n’existe mais plusieurs idées sont utilisées en conjonction :
• Déléguer l’affichage. On délègue tous les calculs concernant l’affichage à une carte gra-
phique, sur laquelle se trouve un processeur dédié à cet usage, disposant d’une mémoire
vive propre, indépendante de celle de l’ordinateur. Ce processeur est en mesure d’effec-
tuer les calculs géométriques nécessaires pour le rendu d’une scène en trois dimensions
(accélération 3D) ou pour décoder des flux vidéo, dont l’interprétation est particulière-
ment gourmande en calculs géométriques.
• Laisser les périphériques accéder à la mémoire. On permet, dans une certaine mesure,
aux périphériques de recopier des données dans et depuis la mémoire (Direct Memory
Access, DMA) pour ne pas avoir besoin de les faire passer toutes par le processeur.
En pratique, la réalisation matérielle d’un ordinateur diffère donc un peu de l’architecture
idéale de von Neumann pour tenter d’en gommer les défauts.
Livre_silo 30 août 2013 16:32 Page 13
es
1 – Machine, système d’exploitation et environnement de développement
13
l
rol
1.2 Notion de système d’exploitation
On a décrit jusqu’ici le fonctionnement du matériel constituant un ordinateur. Ce dernier
sert à exécuter un programme ; il stocke des données et les instructions du programme
dans la mémoire de masse.
Ey
Or, en pratique, on n’utilise pas un mais plusieurs programmes, souvent même simul-
tanément : navigateur web, logiciel de courrier électronique, de messagerie instantanée,
traitement de texte, tableur, outil de présentation, lecteur de vidéos, jeux… Si différents
outils doivent stocker des informations sur le disque dur, il convient qu’ils le fassent de
façon coordonnée, afin que chacun sache où aller lire et écrire les données le concernant.
Comment la machine fait-elle pour savoir où les programmes sont stockés ? Et pour les
t
lancer ? Comment ces programmes savent-ils ensuite où stocker leurs données ?
gh
Répondre à ces questions est le rôle du système d’exploitation. Il s’agit d’un programme
chargé en mémoire vive dès le démarrage de l’ordinateur et qui y reste jusqu’à l’extinction
de la machine.
EN PRATIQUE
i
pyr
1 Les systèmes d’exploitation issus d’Unix (Mac OS X, iOS, GNU/Linux, Android, FreeBSD,
NetBSD). Dans tous les domaines, sauf celui des ordinateurs personnels, les systèmes issus
d’Unix sont majoritaires. Et parmi eux, Linux se taille la part du lion : depuis sa création
comme un hobby d’étudiants, il y a 20 ans, Linux s’est imposé comme un système uni-
versel puisqu’il équipe aussi bien les téléphones portables (sous la forme d’Android) que
les boîtiers prêtés par les fournisseurs d’accès à Internet (Freebox, Livebox, Neufbox et
Bewan iBox), les ordinateurs personnels (notamment sous la forme Ubuntu/GNU Linux),
les serveurs web et les supercalculateurs (plus de 90 % des calculateurs du TOP 500).
Co
2 Les systèmes d’exploitation de la famille Microsoft Windows. Ils se sont imposés grâce
aux pratiques commerciales très agressives de Microsoft et sont en situation de quasi-
monopole sur les ordinateurs personnels depuis près de deux décennies (ils en équipent
près de 90 %).
Livre_silo 30 août 2013 16:32 Page 14
es
Informatique pour tous
14
l
rol
1.2.1 Le multitâche
On a vu qu’un ordinateur ne peut exécuter qu’une instruction à la fois. Pourtant, le système
d’exploitation permet d’exécuter plusieurs programmes en même temps. Plus exactement,
il donne l’illusion que l’on exécute plusieurs programmes en même temps.
Ey
EN PRATIQUE Multiprocesseurs et autres multicore
Certains ordinateurs exécutent réellement plusieurs instructions simultanément. Ils sont
dotés de plusieurs processeurs capables d’accéder indépendamment à la mémoire et aux
périphériques, ce qui revient effectivement à exécuter plusieurs instructions à la fois. Les
fabricants de processeurs proposent même maintenant des processeurs qui contiennent
en fait plusieurs cœurs (dualcore, quadricore), c’est-à-dire que plusieurs processeurs (unité
t
arithmétique et logique et unité de contrôle) ont été regroupés en un seul. Dans tous les
cas, d’une part cette intégration ne va pas sans problème pour coordonner les processeurs
gh
et, d’autre part, le nombre d’instructions exécutées simultanément est bien inférieur au
nombre de programmes tournant simultanément sur l’ordinateur.
Pour donner cette illusion, le système d’exploitation stocke en mémoire les différentes
applications que l’on veut exécuter. Il lance l’exécution d’une première application. Dès qu’il
i
se produit une entrée-sortie ou, à défaut, lorsqu’un certain temps est écoulé (de l’ordre de
l’exécution d’une autre application. En pratique, le temps d’exécution d’une tâche dépasse
rarement la dizaine de millisecondes.
De façon schématique, voici ce qui se passe lorsque l’on tape un texte sur un ordinateur tout
en lui faisant jouer une fugue de Bach à l’aide d’une application de lecture audio. Le noyau
du système d’exploitation commence par exemple à exécuter l’application de lecture audio,
qui envoie sur le périphérique son quelques notes de la fugue. Pendant que les données
Co
sont écrites, le noyau passe la main à une autre application. Survient alors un événement :
l’utilisateur vient de taper sur une touche du clavier. Le noyau reprend alors le contrôle.
Il sait que le traitement de texte était en attente de cet événement : il lui passe donc la
main. Celui-ci affiche la lettre tapée à l’écran et se remet en attente d’un autre caractère.
Le noyau reprend donc la main pour la passer à une autre application en attente. Après
quelques instants, survient alors un nouvel événement : le périphérique son signale que
toutes les données ont été écrites. Le noyau passe alors de nouveau la main à l’application
de lecture audio, qui envoie de nouveau quelques notes de la fugue sur le périphérique. De
nouveau le noyau passe la main à une autre application. Tout cela s’est déroulé en quelques
dizaines de millisecondes tout au plus.
Livre_silo 30 août 2013 16:32 Page 15
es
1 – Machine, système d’exploitation et environnement de développement
15
l
rol
On prendra dans la suite de ce chapitre le cas d’un utilisateur fictif Jean Dupont utili-
sant un ordinateur au sein de son entreprise. Le responsable des moyens informatiques a
créé un compte utilisateur dans le système informatique, auquel est associé un identifiant,
par exemple jdupont et un mot de passe. Il a de plus déclaré Jean Dupont comme étant
membre d’un ou plusieurs groupe(s) d’utilisateurs, ce qui lui conférera certains droits vis-
Ey
à-vis du système informatique. Par exemple, si l’entreprise de Jean a défini des groupes
utilisateur, developpeur et manager et si Jean travaille comme ingénieur informatique,
on peut imaginer qu’il est membre des deux premiers groupes, mais pas du troisième. Pour
des raisons qu’il serait un peu long d’expliciter ici, il est par ailleurs probable que le res-
ponsable informatique ait également créé un groupe privé jdupont, dont Jean sera le seul
membre.
t
Après avoir démarré, l’ordinateur de Jean Dupont présente un écran de connexion (voir fi-
gure 1.5 pour un exemple). Jean inscrit alors son identifiant, puis son mot de passe. Le sys-
gh
tème d’exploitation reconnaît jdupont comme un identifiant valide, vérifie que le mot de
passe correspond à cet identifiant et lance un programme (ou un ensemble de programmes)
qu’on appelle parfois shell, sous l’identité jdupont. Sur les systèmes d’exploitation récents,
ce shell se présente sous forme d’une interface graphique permettant à l’utilisateur de lan-
cer les applications qu’il veut utiliser (navigateur web, gestionnaire de fichiers, suite bu-
i
pyr
Il existe aussi des shells en mode texte, qu’on appelle interprètes de commandes : ces pro-
grammes attendent une commande de l’utilisateur sous forme d’une ligne de texte, l’exé-
cutent, attendent de nouveau une commande, l’exécutent, etc. Ils étaient historiquement
utilisés sur des terminaux en mode texte, c’est-à-dire une combinaison d’un clavier et d’un
écran incapable d’afficher autre chose que du texte en orange sur fond noir (ou en vert sur
fond noir). Aujourd’hui, ils ont quasiment disparu, mais tous les systèmes Unix proposent
Co
des émulateurs de terminaux. Un tel émulateur est présenté figure 1.4. Jean Dupont y a
d’abord tapé les trois commandes pwd, ls et date qui ont eu pour effet d’afficher le nom du
répertoire de travail (print working directory), de lister son contenu et d’afficher la date et
l’heure. Il a ensuite tapé une commande plus compliquée, qui permet en une ligne, certes
complexe, de savoir combien de fichiers sont présents sur l’ordinateur (on peut lire à la
suite la réponse de la machine).
Livre_silo 30 août 2013 16:32 Page 16
es
Informatique pour tous
16
l
rol
1.2.3 Système de fichiers
La mémoire de masse est généralement organisée en un système de fichiers qui permet aux
utilisateurs d’enregistrer leurs données et leurs programmes.
Pour simplifier, on se placera ici dans le cas d’un système d’exploitation de la famille Unix.
Ey
On utilisera le gestionnaire de fichiers PCManFM mais les principes énoncés restent valables
pour les autres systèmes et les autres gestionnaires de fichiers.
Le nombre de fichiers est généralement très élevé : plusieurs centaines de milliers pour
les programmes installés sur un ordinateur de bureau, près d’un demi-million de fichiers
utilisateur accumulés en 20 ans par l’auteur de ces lignes. Ils sont organisés en une structure
arborescente de répertoires. Du point de vue de l’utilisateur, un répertoire est un ensemble
t
de fichiers et de sous-répertoires, désignés par des noms.
gh
Figure 1.4
Émulateur de terminal sous un
système Unix
i
terminal.png
pyr
Figure 1.5
Écran de
Co
connexion
gdm-login-shot.png
Livre_silo 30 août 2013 16:32 Page 17
es
1 – Machine, système d’exploitation et environnement de développement
17
l
rol
Lorsque Jean Dupont lance l’application qui explore le système de fichiers, celle-ci lui
présente les fichiers d’un répertoire, en général celui où sont stockées ses propres données
(voir figure 1.6).
Sous Unix, tous les fichiers sont regroupés dans une unique arborescence. Le sommet de
Ey
l’arbre (qu’on appelle aussi sa racine car en informatique, les arbres poussent du haut vers le
bas) est un répertoire appelé /. Cette racine possède plusieurs sous-répertoires, dont géné-
ralement un appelé home, contenant les données des utilisateurs. Dans home, Jean Dupont
peut ainsi constater la présence d’un répertoire jdupont. Pour désigner un fichier (ou un
sous-répertoire) b d’un répertoire a, Unix utilise la notation a/b (sauf si a est la racine,
auquel cas on note simplement /b et non //b). Le répertoire de Jean Dupont, qui est le
sous-répertoire jdupont du sous-répertoire home de la racine, se note donc /home/jdu-
t
pont. C’est d’ailleurs ce qu’on peut lire dans la barre indiquant le répertoire en cours de
visualisation dans le gestionnaire de fichiers (figure 1.6).
gh
Figure 1.6
Répertoire
/home/jdupont
i
jdupont-dir.png
pyr
Co
Dans les systèmes Windows, il y a quelques petites différences. Les différents disques (C:,
D:, etc.) ont chacun leur propre système de fichiers, et pour séparer un nom de fichier
du répertoire qui le contient on utilise la notation a\b. À l’intérieur de chaque disque, le
principe d’organisation sous forme d’une arborescence de répertoires reste le même.
Si, dans le gestionnaire de fichiers, Jean clique sur la flèche vers le haut, il arrive dans
le répertoire immédiatement au-dessus de /home/jdupont dans la hiérarchie, c’est-à-dire
/home. Il constate alors que ce dernier contient d’autres sous-répertoires : dmartin et xdu-
rand (voir figure 1.7).
Livre_silo 30 août 2013 16:32 Page 18
es
Informatique pour tous
18
l
rol
Explorer toute la hiérarchie de ces fichiers est un vaste programme, mais on peut citer tout
de même deux autres répertoires notables :
Ey
/media Donne accès aux périphériques de données amovibles (CD-ROM, DVD, clés USB, disques durs amo-
vibles).
Rapidement, tout utilisateur est également amené à organiser ses répertoires person-
pyr
nels. Faire preuve d’un peu de méthodologie aidera à beaucoup mieux se repérer par
la suite.
1 On identifie des catégories homogènes de fichiers à classer.
2 On donne un nom significatif à chacune de ces catégories.
3 Éventuellement on les regroupe elles-mêmes en catégories homogènes.
4 On crée les répertoires correspondant à chacune de ces catégories.
Co
5 On met chaque fichier dans le répertoire approprié et on fait de même avec les
nouveaux fichiers créés ou téléchargés par la suite.
Exercice 1.2 Comment organiser le répertoire personnel d’un élève qui contient aussi bien des fichiers
de travail liés à ses études que des documents multimédia et des programmes de jeux ?
Typiquement, le répertoire personnel d’un élève peut contenir des fichiers entre autres dans les catégo-
ries suivantes, qui constitueront autant de répertoires : Anglais, Informatique, Jeux, Musique, Photos,
Sciences Physiques.
On aura ensuite intérêt à regrouper les répertoires Anglais, Informatique et Sciences Physiques dans un
répertoire Travail, et les autres dans un répertoire Loisirs.
Enfin, à l’usage, il y a des chances pour qu’un répertoire comme Informatique finisse par contenir beau-
coup de fichiers. On pourra donc y créer des sous-répertoires pour mieux organiser son contenu.
Livre_silo 30 août 2013 16:32 Page 19
es
1 – Machine, système d’exploitation et environnement de développement
19
l
rol
POUR ALLER PLUS LOIN Comment toutes ces informations sont-elles organisées sur le disque ?
Du point de vue conceptuel, un fichier est une séquence finie d’octets, sans signification
a priori : c’est le programme lisant cette séquence d’octets qui décidera de la façon de la
comprendre. Il peut contenir des données de tout type : texte, son, vidéo et même des
programmes directement exécutables par le processeur (programmes dits « binaires » ou
Ey
« en langage machine »).
Les fichiers présents sur le disque dur sont répertoriés dans des tables, stockées à un endroit
du disque conventionnellement fixé, qui donnent des informations relatives à ces fichiers,
appelées métadonnées, notamment :
1 Date de création, de dernière modification, de dernière lecture.
2 Taille du fichier.
3 Emplacement des données du fichier sur le disque.
4 Suivant les systèmes de fichiers employés, un numéro identifiant le fichier, appelé inode
t
sous Unix.
La table des métadonnées est aussi appelée table des inodes.
gh
Du point de vue du système d’exploitation, un répertoire est juste un fichier particulier,
qui contient une liste de couples (n, i) où n est un nom de fichier et i l’inode du fichier.
Une information supplémentaire est stockée dans la table des inodes pour indiquer, pour
chaque fichier, s’il contient des données ou s’il s’agit d’un répertoire.
i
pyr
Plutôt indiscret, Jean Dupont décide d’aller explorer les répertoires de ses collègues Do-
minique Martin et Xavier Durand. Cependant, lorsqu’il clique sur le répertoire xdurand,
il obtient un message d’erreur, présenté figure 1.8.
Figure 1.7
Co
Répertoire /home
slash-home.png
Figure 1.8
Refus d’accès à /home/xdurand
xdurand-perm-refusee.png
Livre_silo 30 août 2013 16:32 Page 20
es
Informatique pour tous
20
l
rol
En revanche, il parvient à lire le répertoire /home/dmartin. Celui-ci contient un fichier
Lisez-moi.odt que Jean Dupont parvient à ouvrir. Sous OpenOffice.org, il réussit à éditer
le fichier, mais lorsqu’il essaie de l’enregistrer, il obtient un message d’erreur, reproduit
figure 1.9. Qu’à cela ne tienne, Jean tente d’enregistrer ce fichier sous un nouveau nom :
nouveau-lisez-moi.odt. Il obtient de nouveau une erreur d’OpenOffice.org (figure 1.10).
Ey
Que s’est-il passé ?
Figure 1.9
Échec de l’enregistrement d’un document protégé en
refus-ecriture-oo.png
écriture
t
gh
Figure 1.10
Échec de l’enregistrement d’un document dans un tentative-ecriture-sous-un-autre-nom.png
répertoire protégé en écriture
i
pyr
d’accéder aux données d’un autre. Il convient de se renseigner et de faire preuve de discer-
nement… De même que dans la vraie vie, ce n’est pas parce que les voisins ne verrouillent
jamais leur porte que l’on est autorisé à y entrer, en informatique, ce n’est pas parce qu’il
n’y a aucune protection que l’on a le droit de faire ce que l’on veut. Passer outre une inter-
diction, voire se passer d’autorisation, en plus d’être moralement douteux, est pénalement
sanctionné (art. 323-1 et suivants du code pénal). Et les peines encourues sont particuliè-
rement lourdes : par exemple, le simple fait d’accéder à un système informatique ou à des
données en sachant qu’on n’en a pas l’autorisation, sans intention de commettre de dégâts,
sans même commettre le moindre dégât involontaire, même si aucune protection n’est en
place, est passible de 2 ans d’emprisonnement et 30 000 euros d’amende.
Chaque entrée de la table des inodes comporte, en plus des métadonnées déjà mention-
nées, les droits d’accès accordés aux utilisateurs du système. Ces droits, appelés aussi per-
missions, précisent qui a le droit de faire quoi sur le fichier ou le répertoire concerné.
Pour connaître ces permissions, il suffit, dans le gestionnaire de fichiers, de cliquer-droit
sur le répertoire, de choisir Propriétés dans le menu contextuel qui apparaît alors, puis de
cliquer sur l’onglet Permissions.
Livre_silo 30 août 2013 16:32 Page 21
es
1 – Machine, système d’exploitation et environnement de développement
21
l
rol
Lorsque Jean Dupont regarde ainsi les permissions de /home/xdurand (voir figure 1.11),
il constate que les droits sont les suivants :
• L’utilisateur propriétaire du fichier est xdurand.
• Le groupe auquel appartient le fichier est aussi xdurand.
Ey
• Le propriétaire du fichier peut accéder en lecture et en écriture au fichier.
• Les autres utilisateurs n’ont aucun droit sur le fichier.
Lorsqu’un programme, quel qu’il soit, tente d’accéder à un fichier ou à un répertoire, il
ne peut le faire directement : il doit demander les données qui l’intéressent au système
d’exploitation. Celui-ci regarde alors sous quelle identité tourne le programme et quels sont
les droits d’accès du fichier concerné. Si les permissions de l’utilisateur sont insuffisantes
pour l’opération demandée, le système refuse. D’où les messages d’erreurs constatés :
t
• Dans le cas présenté figure 1.9, Jean Dupont essaie d’écrire dans un fichier sur lequel il
gh
n’a pas le droit d’écriture, comme on peut le constater figure 1.12. OpenOffice.org l’a
constaté et a adapté son message d’erreur.
• Dans le cas présenté figure 1.10, Jean essaie d’écrire le fichier en lui donnant un nouveau
nom, nouveau-lisez-moi.odt dans un répertoire (/home/dmartin) sur lequel il n’a pas
le droit d’écriture. OpenOffice.org n’a pas cherché à vérifier si cela était possible et a
demandé au système de créer le fichier. Il n’a probablement pas vérifié la réponse du
i
pyr
que celui-ci n’existe pas, d’où le message d’erreur quelque peu sibyllin.
Figure 1.11
Droits d’accès au répertoire /home/xdurand
Co
permissions-xdurand-home.png
Livre_silo 30 août 2013 16:32 Page 22
es
Informatique pour tous
22
l
rol
Plus généralement, le système d’exploitation contrôle l’accès aux différentes ressources de
l’ordinateur :
Écran, carte son, clavier, Les utilisateurs d’un ordinateur ne sont pas nécessairement tous face à l’ordinateur.
souris Certains peuvent être connectés à distance. Les droits d’accès à la carte son et à l’écran
sont en général accordés à l’utilisateur physiquement présent devant la console (ou plus
Ey
exactement, à celui dont l’identifiant et le mot de passe ont été entrés par l’intermé-
diaire du clavier). La lecture des informations de la souris et du clavier est réservée
également à cet utilisateur.
Imprimante Selon les choix faits par l’administrateur du système, l’usage de l’imprimante peut être
réservé aux utilisateurs physiquement présents ou au contraire ouvert à tous les utili-
sateurs.
Réseau De même, l’accès au réseau peut être ouvert à tous ou filtré par utilisateur ou par
t
contenu.
gh
Exercice 1.3 Sur un PC auquel vous avez accès, en tant que simple utilisateur :
1 Essayez de copier un de vos fichiers dans un répertoire du système (par exemple, /etc sous Unix ou
C:\Program Files sous MS-Windows). Que se passe t-il ?
2 Sous Linux seulement, essayez d’ouvrir le fichier /etc/shadow. Que se passe t-il ?
i
ATTENTION
pyr
Figure 1.12
Droits d’accès à /home/dmartin/lisez-moi.odt
permissions-lisez-moi-dmartin.png
Livre_silo 30 août 2013 16:32 Page 23
es
1 – Machine, système d’exploitation et environnement de développement
23
l
rol
1.2.5 Lancement d’applications
Ey
Pour lancer une application (un programme), on dispose de deux possibilités :
• Cliquer sur un bouton, un menu ou un fichier du shell graphique. Cette méthode
est en général à réserver pour des tâches simples ; elle offre l’avantage d’être intuitive
et donc utilisable même par des utilisateurs débutants.
• Taper une commande dans un shell en mode texte. Dès que l’on veut exprimer des
commandes plus complexes, par exemple passer des arguments à un programme
ou manipuler des fichiers en masse, cette méthode s’avère quasi incontournable.
t
Son utilisation est plus ou moins facilitée selon le système d’exploitation que l’on
utilise.
gh
Le shell graphique rend transparentes pour l’utilisateur les étapes du lancement d’une ap-
plication.
Pour démarrer par exemple OpenOffice.org, l’utilisateur Jean Dupont clique sur un bou-
i
ton pour faire apparaître un menu dans lequel il choisit l’application. Le shell graphique
pyr
« sait » que le programme OpenOffice.org est installé. Plus précisément, il sait qu’un cer-
tain fichier (nommé ooffice) est exécutable par le système. Il demande donc au système
d’exploitation d’exécuter ce programme. Ensuite, tout se passe comme si l’utilisateur avait
tapé directement la commande ooffice dans un shell en mode texte.
Le système commence par vérifier que l’utilisateur a le droit d’exécuter le programme.
Sur la figure 1.12, on constate en effet la présence d’une case Rendre le fichier exécutable.
Co
Elle correspond à une permission d’exécution attachée au fichier, au même titre que les
permissions de lecture et d’écriture.
Ensuite, le système réserve un espace dans la mémoire vive de l’ordinateur pour stocker les
instructions du programme, ainsi que ses données. Il copie le contenu du fichier exécutable
en mémoire. Celui-ci n’est qu’une suite de bits qui codent les instructions dans le langage
du processeur (on dit que le programme est en langage machine), il peut donc les exécuter
en effectuant un branchement vers les premières instructions du programme.
Du point de vue de l’utilisateur, il existe d’autres façons de lancer un programme, mais
qui restent à rapprocher du shell graphique. Par exemple, si Jean Dupont clique dans son
gestionnaire de fichiers sur un fichier OpenDocument, celui-ci va lancer OpenOffice.org.
En effet, lorsqu’on clique sur le nom ou l’icône d’un fichier, le gestionnaire de fichiers
commence par en déterminer le type.
Livre_silo 30 août 2013 16:32 Page 24
es
Informatique pour tous
24
l
rol
Suivant les systèmes, il peut s’appuyer sur diverses informations :
• le nom du fichier et tout particulièrement son suffixe. Ainsi, un fichier dont le nom se
termine par .odt sera t-il identifié comme un fichier OpenDocument (le gestionnaire
consulte pour cela une table qui associe un type de fichier à chaque suffixe connu) ;
Ey
• le contenu du fichier ;
• le fait que l’utilisateur ait ou non le droit d’exécuter ce fichier ;
• sur certains systèmes (Mac OS X en particulier), les métadonnées associées au fichier
donnant son type.
Une fois le type déterminé, le gestionnaire de fichiers consulte une table (commune à tout
le système ou spécifique au gestionnaire, commune à tous les utilisateurs ou spécifique
à l’utilisateur) indiquant à quelle application est associé ce type de fichier. Il lance alors
t
l’application, en lui fournissant pour argument (c’est-à-dire comme information supplé-
mentaire) le nom du fichier à ouvrir. Là encore, la situation est exactement la même que
gh
si on avait tapé ooffice nomdufichier dans un shell en mode texte.
Tout utilisateur peut écrire ses propres programmes. Pour cela, il a trois possibilités :
• Les écrire en langage machine. C’est une tâche ardue car il s’agit d’un langage de
pyr
bas niveau dans lequel il est difficile ou au minimum fastidieux d’implanter des
idées un tant soit peu complexes. De plus un tel programme est spécifique à la
machine pour laquelle il a été écrit et risque de ne pas fonctionner sur une autre.
• Utiliser un compilateur. Un programme écrit dans un langage évolué est traduit par
le compilateur pour donner un programme en langage machine.
• Faire appel à un interpréteur d’un langage évolué. Un interpréteur est un pro-
Co
gramme exécutable qui va lire le texte d’un programme dans un langage évolué
pour l’exécuter pas à pas, sans passer par la phase intermédiaire de compilation.
La compilation demande un traitement préliminaire avant de pouvoir exécuter le
programme que l’on a écrit, mais produit en général des applications efficaces. À
l’inverse, on peut interpréter un programme immédiatement après l’avoir écrit, mais
il aura tendance à s’exécuter moins rapidement.
Le choix entre compilation et interprétation dépend très largement du langage dans
lequel on programme : certains ne proposent qu’une seule des deux possibilités,
d’autres laissent le choix. Il existe même des situations intermédiaires où un pro-
gramme peut être compilé dans un langage qui est plus proche du langage machine
mais qui doit encore être interprété (on parle de bytecode). Pour des applications écrites
en Python, à l’heure actuelle, on utilise usuellement un interpréteur. Voir en annexe
le TP sur la création de programmes autonomes, section A.1 pour la mise en œuvre
concrète de la compilation ou de l’interprétation d’un programme.
Livre_silo 30 août 2013 16:32 Page 25
es
1 – Machine, système d’exploitation et environnement de développement
25
l
rol
1.2.6 Protections
Jean Dupont, désirant toujours lire des données qui lui sont interdites, pourrait imaginer
écrire son propre programme, qui accéderait directement au disque dur pour ne pas avoir
à demander la permission au système d’exploitation. Après tout, puisqu’il est possible de
Ey
faire exécuter au processeur des instructions arbitraires, ne pourrait-il pas en tirer parti ?
Cela n’est pas si simple : lorsqu’il lance un programme utilisateur, le système d’exploitation
fait basculer le processeur dans un mode de fonctionnement particulier (appelé mode uti-
lisateur) dans lequel le programme est isolé : il ne peut accéder ni à l’espace mémoire des
autres processeurs, ni aux périphériques du système.
S’il tente malgré tout de lire ou d’écrire dans des parties de la mémoire qui lui sont inter-
t
dites, son exécution est momentanément arrêtée et le contrôle est transféré au noyau du
système d’exploitation, pour qu’il décide de la conduite à tenir. Sa décision est (normale-
gh
ment) sans appel : le programme est définitivement arrêté ².
Pour ce qui est de l’accès aux périphériques, la seule possibilité pour le programme consiste
à appeler des fonctions du noyau du système d’exploitation (on parle d’appels système). On
peut les voir comme des branchements d’un type particulier, qui transfèrent le contrôle
au système d’exploitation et remettent le processeur en mode noyau. Ensuite, le système
i
d’exploitation fait parvenir sa réponse au programme et lui rend le contrôle en mode uti-
pyr
lisateur.
Un programme lancé par Jean Dupont doit donc passer par un appel système pour accéder
à un fichier. Comme on l’a vu, le système vérifiera les droits de l’utilisateur du programme
et refusera si celui-ci n’est pas autorisé à y accéder.
Ainsi le système d’exploitation protège-t-il les différents programmes et utilisateurs les uns
des autres.
Co
2. Sous MS-Windows, on peut voir apparaître des fenêtres d’erreur indiquant « Ce programme va être arrêté
parce qu’il a effectué une opération non conforme ». Il s’agit de la réaction du système d’exploitation à une erreur
de ce type.
Livre_silo 30 août 2013 16:32 Page 26
es
Informatique pour tous
26
l
rol
On appelle environnement de développement intégré (parfois abrégé IDE) un logiciel qui
permet à la fois :
• d’écrire des programmes dans un éditeur adapté au langage ;
• d’exécuter les programmes que l’on a écrits ;
Ey
• de corriger des erreurs (déboguer) dans ces programmes ;
• éventuellement de consulter de la documentation.
Il existe de nombreux logiciels de ce type, chacun ayant ses particularités : spécifique à
un langage de programmation ou générique, léger ou complet... Cependant, tous fonc-
tionnent de façon similaire : l’essentiel est de comprendre les principes généraux et de
choisir un IDE avec lequel on travaille confortablement. Les outils cités ci-après à titre
d’exemple sont tous gratuits et fonctionnent sur le principe du logiciel libre ; il existe aussi
t
des IDE commerciaux.
gh
• IDLE. Fourni avec la distribution standard de Python, il est particulièrement sobre,
donc suffisant pour une utilisation basique de Python.
• Eclipse. Plus lourd à manipuler, il possède des plugins pour à peu près n’importe quel
langage de programmation (dont PyDev pour Python) et pourra donc intéresser les
étudiants qui souhaiteraient utiliser le même IDE dans différents contextes.
• Emacs ou Vim. Ce sont des éditeurs de texte qui peuvent être étendus pour exécuter
i
des programmes Python ou même d’autres langages. Si cette solution a l’avantage d’être
pyr
particulièrement légère, elle est évidemment très dépouillée et n’est pas forcément facile
à prendre en main.
Cet ouvrage montrera comment utiliser Spyder ³, qui est fourni avec plusieurs distribu-
tions de Python : WinPython ou Python(x,y) sous Windows, via le projet MacPorts sous
Mac OS, et enfin dans différents paquets pour la plupart des distributions Linux. Le lec-
teur n’aura cependant aucun mal à retrouver les mêmes fonctionnalités dans d’autres en-
Co
vironnements.
L’avantage le plus significatif de Spyder par rapport aux autres distributions est que les bi-
bliothèques utilisées en classes préparatoires sont fournies directement, ce qui en simplifie
largement l’installation.
La fenêtre de Spyder est divisée en trois parties (figure 1.13) :
• L’éditeur à gauche, dans lequel on écrira les programmes.
• L’explorateur en haut à droite, que nous utiliserons surtout comme débogueur, mais qui
peut également servir de documentation.
• La console interactive en bas à droite, dans laquelle s’exécuteront les programmes.
3. À l’heure où cet ouvrage est imprimé, Spyder en est à sa version 2.2 et utilise exclusivement Python 2.x.
La version 2.3, qui devrait être disponible sous peu et permettre d’utiliser Python 3.x, est déjà accessible à titre
expérimental.
Livre_silo 30 août 2013 16:32 Page 27
es
1 – Machine, système d’exploitation et environnement de développement
27
l
rol
spyder.png
t Ey
i gh
pyr
Figure 1.13
Le logiciel Spyder au démarrage
In [1]:
La dernière ligne, qui commence par In suivi d’un nombre entre crochets, attend que l’on
tape une commande : c’est le mode interactif de Python, où chaque ligne tapée est im-
Livre_silo 30 août 2013 16:32 Page 28
es
Informatique pour tous
28
l
rol
médiatement exécutée. Ainsi, si l’on tape une expression, sa valeur s’affiche (dans tout cet
ouvrage, les entrées tapées par l’utilisateur dans l’interpréteur interactif seront notées en
gras) :
In [1]: 2+2
Ey
Out[1]: 4
On appelle session de travail une suite d’instructions saisies dans une fenêtre Python in-
teractive avec les réponses correspondantes. Il est possible d’enregistrer le contenu d’une
session de travail à l’aide de la commande Enregistrer l’historique... accessible par un clic
droit dans la console.
Dans ce mode, on peut d’ores et déjà utiliser des variables pour stocker des valeurs. L’af-
t
fectation s’écrit avec le symbole = et n’affiche aucune valeur ; mais la variable mémorise la
valeur qu’on lui a donnée et peut être utilisée dans la suite de la session.
gh
In [2]: a = 2
In [3]: a+a
Out[3]: 4
Notons que si l’on utilise dans une expression une variable à laquelle on n’a jamais donné
i
pyr
In [4]: b+1
------------------------------------------------------------
La dernière ligne de ce message indique plus précisément d’où vient l’erreur, ici de la va-
riable b utilisée à tort. Dans la suite de cet ouvrage, seule cette ligne sera reproduite pour
expliquer une erreur.
In [5]: b = 1
In [6]: b+1
Out[6]: 2
Enfin, il est possible de rappeler une ligne tapée précédemment à l’aide des flèches haut et
bas, et de modifier cette ligne avant de relancer son calcul avec Entrée.
In [7]: b = b+1
In [8]: b = b+1
In [9]: b = b+1
Livre_silo 30 août 2013 16:32 Page 29
es
1 – Machine, système d’exploitation et environnement de développement
29
l
rol
In [10]: b
Out[10]: 4
À chaque nouvelle session, les valeurs des variables sont perdues ; les instructions précé-
demment saisies peuvent toujours être rappelées, mais cela reste peu pratique et on n’ima-
Ey
gine évidemment pas écrire un programme complet de la sorte. Les sessions interactives
sont donc à réserver pour tester très rapidement l’évaluation de quelques expressions que
l’on ne souhaite pas conserver par la suite.
1.3.2 Éditeur
Dès que l’on veut écrire un programme, ou même tout simplement une suite d’instructions
t
dont on veut garder une trace, on utilise l’éditeur.
gh
Voici un premier programme Python à tester :
print("Bonjour !")
x = 42
print(x)
• Les mots-clés du langage (comme print) et les nombres se colorent pour ressortir sur le
pyr
reste du texte.
• Les chaînes de caractères (entre guillemets) se colorent également.
• Lorsque l’on tape une parenthèse ouvrante, la parenthèse fermante correspondante se
crée automatiquement ; et lorsque l’on place le curseur à droite d’une parenthèse, celle-ci
se colore en rouge s’il manque la parenthèse correspondante, en vert sinon.
Ainsi, le programme écrit devient plus lisible et on évite de nombreuses fautes de frappe.
Co
Cependant, à ce stade, le programme n’est encore qu’un texte, une suite de caractères qui
n’a pas de sens pour l’ordinateur. Pour que la machine exécute (on dit aussi interprète)
les instructions que l’on a tapées, il faut le lui demander par la commande Exécution du
menu du même nom (raccourci clavier F5 ou icône run.png ). Les instructions sont alors lues et
exécutées ; le résultat, lorsqu’il y en a un, s’affiche dans l’interpréteur interactif.
On précise qu’il est possible, au moyen de la commande Configurer... du menu Exécution
( configure.png
), de choisir si on utilise une nouvelle fenêtre interactive ou bien celle déjà manipulée.
L’intérêt d’une telle option est que, tant qu’une session interactive reste ouverte, elle garde
la mémoire des variables auxquelles on a affecté des valeurs. Cela peut influer sur d’autres
instructions (interactives ou non) que l’on exécuterait par la suite ; cela permet par ailleurs
de consulter la valeur des variables à la fin de l’exécution d’un programme.
Livre_silo 30 août 2013 16:32 Page 30
es
Informatique pour tous
30
l
rol
SAVOIR-FAIRE Utiliser un environnement de développement intégré
Le minimum pour utiliser un environnement de développement intégré est de savoir :
1 Lancer l’IDE.
Ey
2 Ouvrir et enregistrer les programmes que l’on écrit.
3 Exécuter ces programmes.
L’IDE est généralement accessible au moyen d’un raccourci du système d’exploitation,
ou en tapant son nom dans un shell texte. La manipulation des programmes se fait par
le biais des menus de l’IDE ou par des raccourcis clavier, qui s’avèrent plus efficaces
lorsqu’on en a pris l’habitude.
Enfin, l’utilisation d’un IDE prend tout son sens lorsque l’on se sert de son débogueur.
t
gh
1.3.3 Débogueur
La fenêtre située en haut à droite de Spyder possède plusieurs onglets :
• L’inspecteur d’objets fournit de l’aide sur un type ou sur une fonction.
i
pyr
Livre_silo 30 août 2013 16:32 Page 31
es
1 – Machine, système d’exploitation et environnement de développement
31
l
rol
de variables. Pour exécuter cette ligne, on peut cliquer sur le bouton Pas en avant arrow-step-over.png
, ou
bien taper n (comme next) dans l’interpréteur. La ligne est alors exécutée, x prend la valeur
10 et c’est la ligne suivante (y = 7) qui est surlignée. On peut ensuite continuer à exécuter
les lignes les unes après les autres et surveiller l’évolution des différentes variables dans
l’explorateur.
Ey
Comme il est malcommode de cliquer sur un bouton pour chaque ligne alors que seules
certaines posent problème, on peut fixer des points d’arrêts dans le programme. Pour cela,
on se place sur la ligne à laquelle on désire faire une pause et on choisit Ajouter un point
d’arrêt dans le menu Exécution (raccourci F12). On peut également double-cliquer dans
la marge gauche du programme ; le symbole breakpoint.png
s’affiche alors en face de cette ligne. Par
exemple, dans le programme précédent, il peut être judicieux de placer un point d’arrêt à
t
la dernière ligne, dans laquelle risque de se produire une division par zéro.
Désormais, si au lieu d’effectuer Pas en avant, on choisit Continuer ( control.png
ou c dans l’inter-
gh
préteur interactif ), le programme s’exécute normalement jusqu’au prochain point d’arrêt,
puis attend un ordre de l’utilisateur pour continuer à s’exécuter. Dans cet exemple, on voit
dans l’explorateur de variables que x et y contiennent toutes deux la valeur 17 et qu’il y aura
donc effectivement une division par zéro.
i
pyr
les opérations arithmétiques pouvant produire des erreurs, les fins de boucles, certains
tests…
Exercice 1.4 Cet exercice a pour seul but de s’entraîner à manipuler l’environnement de développement
intégré, il n’est pas nécessaire de savoir déjà programmer en Python pour le réaliser.
1 Taper le programme suivant dans l’éditeur et l’exécuter. Que se passe-t-il ?
i = 10
while i != 0:
i = 1-i
print(i)
(Il est possible qu’à ce stade il soit nécessaire de fermer complètement l’IDE et de le rouvrir pour conti-
nuer…).
2 Exécuter ce programme pas à pas et observer les valeurs successives prises par la variable i. Expliquer
le comportement observé à la première question.
3 Placer un point d’arrêt à un endroit approprié du programme pour montrer son comportement sans
avoir besoin de détailler des étapes inutiles.
Livre_silo 30 août 2013 16:32 Page 32
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 33
l es
rol
2
t EyReprésentation
gh
des nombres
i
pyr
Livre_silo 30 août 2013 16:32 Page 34
es
Informatique pour tous
34
l
rol
2.1 Représentation des entiers naturels
2.1.1 Représentation de l’information par des booléens
La mémoire des ordinateurs est constituée d’une multitude de petits circuits électroniques
Ey
qui, chacun, ne peuvent être que dans deux états : sous tension ou hors tension. Technique-
ment, il serait possible de construire des circuits ayant plus de deux états, correspondant à
différentes valeurs de tensions, mais les risques d’erreurs dans le stockage et la transmission
de ces états deviennent vite beaucoup plus importants que les avantages qu’on pourrait en
tirer. Comme il fallait donner un nom à ces états, on a décidé de les appeler 0 et 1, mais
on aurait pu tout aussi bien les appeler A et B, froid et chaud, faux et vrai, etc. Une telle
valeur, 0 ou 1, est dite booléenne. On l’appellera booléen, chiffre binaire ou encore bit
t
(binary digit). Un tel circuit à deux états s’appelle un circuit mémoire un bit.
gh
L’état dans lequel se trouve un circuit mémoire un bit est représenté par le symbole 0 ou
par le symbole 1. L’état d’un circuit composé de plusieurs de ces circuits est représenté par
une suite finie de 0 et de 1, que l’on appelle un mot. Par exemple, le mot 100 décrit l’état
d’un circuit composé de trois circuits mémoire un bit, respectivement dans l’état 1, 0 et 0.
Exercice 2.1 Trouvez trois informations de la vie courante qui peuvent être exprimées par un booléen.
i
Exercice 2.2 On imagine un ordinateur dont la mémoire est constituée de quatre circuits mémoire un bit.
pyr
Quel est le nombre d’états possibles de la mémoire de cet ordinateur ? Même question pour un ordinateur
dont la mémoire est constituée de dix circuits mémoire un bit. Et pour un ordinateur dont la mémoire est
constituée de 32 milliards de tels circuits ?
Exercice 2.3 On veut représenter chacune des sept couleurs de l’arc-en-ciel par un mot, les sept mots
devant être distincts et de même longueur. Quelle est la longueur minimale de ces mots ?
Exercice 2.4 Un horloger excentrique a eu l’idée de fabriquer une montre sur laquelle l’heure est indiquée
par dix diodes électroluminescentes appelées 1 h, 2 h, 4 h, 8 h, 1 min, 2 min, 4 min, 8 min, 16 min et
32 min. Pour connaître l’heure, il suffit d’ajouter la valeur des diodes allumées.
Co
Quelle heure est-il quand sont allumées les diodes 1 h, 2 h, 4 h, 1 min, 2 min, 8 min, 16 min et 32 min ?
Quelles sont les diodes allumées à 5 h 55 ? Est-il possible de représenter toutes les heures ? Toutes les
configurations sont-elles la représentation d’une heure ?
Livre_silo 30 août 2013 16:32 Page 35
es
2 – Représentation des nombres
35
l
rol
Le choix de faire des paquets de dix est conventionnel : on aurait tout aussi bien pu décider
de faire des paquets de deux, de cinq, de douze, de vingt, de soixante, etc. On écrirait alors
les nombres entiers naturels en notation à position en base deux, cinq, douze, vingt ou
soixante. La notation décimale s’appelle donc aussi notation à position en base dix.
Ey
EN PRATIQUE Les bases en informatique
Comme on le verra par la suite, la base deux joue un rôle prépondérant en informatique, en
raison de l’organisation de la mémoire sous forme de suites de bits. L’autre base également
utilisée de façon fréquente est la base 16, dite hexadécimale. Elle permet de décrire ou
de manipuler une longue suite de bits sans lui donner de signification particulière. C’est
utile pour créer une clé de connexion à un réseau WiFi ou une adresse mémoire. Il devient
vite malcommode de le faire en base deux et on multiplie les risques d’erreurs alors que ce
t
sont justement des données sur lesquelles une erreur est fatale. On regroupe donc ces bits
par paquets de quatre, ce qui donne des valeurs en base 24 = 16 (voir plus loin l’exercice
corrigé 2.7 pour leur écriture). L’écriture obtenue est plus compacte (4 fois moins de chiffres
gh
pour un même nombre) mais reste manipulable. Une autre solution aurait été de regrouper
les bits par paquets de 8, comme on le fait dans les octets ; mais il aurait alors fallu 28 = 256
chiffres différents, et l’alphabet n’y suffirait plus. Un octet sera donc représenté par deux
chiffres hexadécimaux.
i
Dans ce livre, quand une suite de chiffres exprime un nombre dans une base différente de
dix, on indique la base en indice, par exemple : 11012 . On souligne aussi parfois un mot
pyr
pour indiquer qu’il exprime un nombre en base deux : 1101. Enfin, on rassemble parfois
les bits par groupes de quatre ou de huit dans les mots très longs pour qu’ils soient plus
faciles à lire : 1111111101 est écrit 11 1111 1101. Comme en base dix, ces groupes sont
formés de droite à gauche.
Co
Livre_silo 30 août 2013 16:32 Page 36
es
Informatique pour tous
36
l
rol
Exercice 2.5 Trouver la représentation en base cinq de 58.
base5_1.pdf
Ey
Donc, 58 objets se regroupent en 11 paquets et 3 unités, puis les 11 paquets se regroupent en 2 paquets
de paquets et 1 paquet.
58 = 11 × 5 + 3 = (2 × 5 + 1) × 5 + 3 = (2 × 52 ) + (1 × 51 ) + (3 × 50 )
Donc 58 = 2135 .
Exercice 2.6 Trouver la représentation en base cinq du nombre 872.
t
gh
base5_2.pdf
pyr
base16_1.pdf
Livre_silo 30 août 2013 16:32 Page 37
es
2 – Représentation des nombres
37
l
rol
Exercice 2.8 Trouver la représentation en base dix du nombre 2024135 .
2024135 = (3 × 50 ) + (1 × 51 ) + (4 × 52 ) + (2 × 53 ) + (0 × 54 ) + (2 × 55 ) = 6608
Ey
Avec l’algorithme de Horner :
son écriture en base dix, mais elle ne demande d’utiliser que deux chiffres. Le nombre
13 = 1101 est donc représenté dans la mémoire d’un ordinateur par le mot 1101, c’est-à-
pyr
1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 =
18 446 744 073 709 551 615 sur huit octets.
Lorsque l’on manipule des entiers naturels uniquement représentés de cette façon, dans
certains langages on les appelle entiers non signés. En Python, toutefois, aucun type de
données ne donne directement accès à ce genre de représentation : on ne peut manipuler
que des entiers relatifs.
Exercice 2.10 Trouver la représentation en base deux du nombre 14.
base2_1.pdf
Donc 14 = 1110.
Livre_silo 30 août 2013 16:32 Page 38
es
Informatique pour tous
38
l
rol
Exercice 2.12 Trouver la représentation en base cinq des nombres 88, 627 et 1451.
Exercice 2.13 Trouver la représentation en base dix des nombres 44135 et 2125 .
Exercice 2.14 Trouver la représentation en base deux du nombre 10 000.
Exercice 2.15 Déterminer la taille du disque dur de votre machine en Mo. Trouver la représentation en
Ey
base deux de ce nombre.
Exercice 2.16 Donner les représentations en base deux des nombres 1, 3, 7, 15, 31 et 63. Expliquer le
résultat.
Exercice 2.17 Trouver la représentation en base seize des nombres suivants : 3718, 54920 et 482919.
Exercice 2.18 Trouver la représentation en base dix du nombre 1101 1011.
Exercice 2.19 C’est en 111 1011 0001 qu’a été transmis le premier message sur Internet. Exprimer ce
t
nombre en base dix. Le système tomba en panne après seulement 10 caractères transmis...
Exercice 2.20 Trouver la représentation en base dix des nombres f edc16 et 6a5c116 .
gh
Exercice 2.21 * Chercher sur le Web ce qu’est le système de numération Shadok. Est-ce un système de
numération à position ? Si oui, en quelle base et avec quels chiffres ?
Exercice 2.22 Quelle est la représentation binaire du nombre 209 ? Et celle du nombre 46 ? Soit m un
mot de 8 bits, n l’entier naturel représenté en binaire par le mot m, m′ le mot obtenu en remplaçant
dans m chaque 0 par un 1 et chaque 1 par un 0 et n′ l’entier naturel représenté en binaire par le mot m′ .
Exprimer n et n′ comme une somme de puissances de 2, montrer que n + n′ = 255. Montrer que la
i
représentation binaire du nombre 255 − n est obtenue en remplaçant dans celle de n chaque 0 par un 1
pyr
et chaque 1 par un 0.
Exercice 2.23 Montrer que les mots de n bits permettent de représenter tous les entiers de 0 à 2n − 1.
Inversement, combien de bits faut-il au minimum pour représenter tous les entiers dont l’écriture décimale
comporte n chiffres ?
Exercice 2.24 On cherche à mettre au point une procédure de calcul du produit de deux entiers naturels
représentés en binaire. Dans tout cet exercice, on pourra supposer par commodité que les entiers sont
représentés sur 32 bits ; la généralisation à un nombre de bits quelconque ne pose cependant pas de
Co
difficulté particulière.
1 Pour multiplier par dix un entier naturel exprimé en base dix, il suffit d’ajouter un 0 à sa droite, par
exemple, 12 × 10 = 120. Quelle est l’opération équivalente pour les entiers naturels exprimés en base
deux ? Illustrer cette remarque par des exemples.
On appelle cette opération décalage à gauche, car dans les microprocesseurs, au lieu d’ajouter un 0 à
droite, on déplace tous les bits vers la gauche. Sur quels entiers cette opération n’est-elle pas possible
sans commettre d’erreur ?
2 Quelle est l’opération effectuée sur un entier naturel en base deux si on lui fait subir deux décalages à
gauche ? trois décalages ? n décalages ?
3 À l’aide des remarques précédentes, étant donnés deux entiers naturels n et m, exprimer le produit
n × m en fonction de n et de la décomposition mi . . . m1 m0 de m en base deux.
4 En déduire un algorithme pour effectuer le produit de deux entiers naturels représentés en base deux
utilisant uniquement :
• des additions ;
• des décalages à gauche ;
• des tests sur un bit d’un nombre.
Les multiplications effectuées par les microprocesseurs fonctionnent sur ce principe.
Livre_silo 30 août 2013 16:32 Page 39
es
2 – Représentation des nombres
39
l
rol
2.2 Représentation des entiers relatifs
2.2.1 Notation en complément à deux
Il faut étendre aux entiers relatifs la représentation binaire des entiers naturels.
Ey
Une solution consiste à réserver un bit pour le signe de l’entier et à utiliser les autres pour
représenter sa valeur absolue. Ainsi, avec des mots de 16 bits, en utilisant 1 bit pour
le signe et 15 bits pour la valeur absolue, on pourrait représenter les entiers relatifs de
−111 1111 1111 1111 = −(215 − 1) = −32 767 à 111 1111 1111 1111 = 215 − 1 = 32 767.
Cependant, cette méthode a plusieurs inconvénients, notamment l’existence de deux zéros,
l’un positif et l’autre négatif.
t
On applique alors une autre méthode, qui consiste à représenter un entier relatif par
un entier naturel. Si on utilise des mots de 16 bits, on peut représenter les entiers re-
gh
latifs compris entre −32 768 et 32 767 : on représente un entier relatif x positif ou nul
comme l’entier naturel x et un entier relatif x strictement négatif comme l’entier naturel
x + 216 = x + 65 536, qui est compris entre 32 768 et 65 535. Ainsi, les entiers naturels
de 0 à 32 767 correspondent aux entiers relatifs positifs ou nuls, à droite sur la figure 2.1 et
les entiers naturels de 32 768 à 65 535 représentent les entiers relatifs strictement négatifs,
i
pyr
Figure 2.1
Représentation des entiers relatifs en complément à deux
negatifs.pdf
Co
Cette manière de représenter les entiers relatifs s’appelle la notation en complément à deux.
L’entier relatif −1 est représenté comme l’entier naturel 65 535, c’est-à-dire par le mot
1111 1111 1111 1111 (on ne le souligne pas car ici ce n’est pas tout à fait la représentation
d’un nombre en base deux). On notera qu’il reste facile de déterminer le signe d’un entier
représenté sous cette forme : un entier relatif positif ou nul est représenté par un entier
naturel dont le premier bit vaut 0 ; a contrario, un entier relatif strictement négatif est
représenté par un entier naturel dont le premier bit vaut 1.
Livre_silo 30 août 2013 16:32 Page 40
es
Informatique pour tous
40
l
rol
Plus généralement, avec des mots de n bits, on peut représenter les entiers relatifs compris
entre −2n−1 et 2n−1 − 1 : on représente un entier relatif x positif ou nul comme l’entier
naturel x (compris entre 0 et 2n−1 − 1) et un entier relatif x strictement négatif comme
l’entier naturel x + 2n (compris entre 2n−1 et 2n − 1).
Ey
ATTENTION La mémoire n’a pas de sens a priori.
Rien dans la représentation binaire n’indique comment il faut interpréter un mot mémoire
donné. Le même mot peut donc être lu de plusieurs façons différentes, toutes aussi cor-
rectes les unes que les autres.
Il faut donc garder à part une trace des conventions de codage qu’on a décidé d’utiliser pour
les différents mots mémoire que l’on manipule. De nos jours, cette gestion du sens de la
mémoire n’est presque plus à la charge du programmeur : les langages de programmation
t
évolués proposent pour la plupart un système de types qui permet d’associer facilement à
chaque endroit de la mémoire une signification particulière.
gh
EN PRATIQUE Dans les versions Python 2.x
Le type int désigne en principe des entiers relatifs représentés sur 32 bits (quatre octets),
i
donc dont les valeurs vont de −2 147 483 648 à 2 147 483 647. Cependant, si l’on travaille sur
une machine 64 bits, la représentation des entiers sera également faite sur 64 bits : on dispo-
pyr
sera donc des entiers de −9 223 372 036 854 775 808 à 9 223 372 036 854 775 807. L’ensemble
des valeurs de type int dépend donc de la machine sur laquelle Python est exécuté !
Si l’entier relatif x est positif ou nul, on le représente comme l’entier naturel x. S’il est
strictement négatif, on le représente comme l’entier naturel x + 2n .
Exercice 2.25 Trouver les représentations binaires sur huit bits des entiers relatifs 0 et −128.
L’entier relatif 0 est représenté comme l’entier naturel 0 : 00000000. L’entier relatif −128 est représenté
comme l’entier naturel −128 + 256 = 128 : 10000000.
Livre_silo 30 août 2013 16:32 Page 41
es
2 – Représentation des nombres
41
l
rol
Exercice 2.26 Trouver les représentations décimales des entiers relatifs dont les représentations binaires
sur huit bits sont 00010111 et 10001100.
Le mot 00010111 représente l’entier naturel 23 et donc l’entier relatif 23. Le mot 10001100 représente
l’entier naturel 140 et donc l’entier relatif 140 − 256 = −116.
Ey
SAVOIR-FAIRE Calculer la représentation de l’opposé d’un entier relatif
On se place dans le cas où on connaît la représentation p sur huit bits de l’entier relatif
x, et on cherche à calculer la représentation p′ de son opposé.
Si l’entier relatif x est compris entre 0 et 127, alors il est représenté sur huit bits
par l’entier naturel p = x et son opposé −x est représenté par l’entier naturel
p′ = −x + 256 = 256 − p. Si l’entier relatif x est compris entre −127 et −1,
t
alors il est représenté par l’entier naturel p = x + 256 et son opposé −x est représenté
gh
par l’entier naturel p′ = −x = 256 − p. Donc, sauf quand x = −128 dont l’opposé
n’est pas représentable, si un entier relatif x est représenté par l’entier naturel p, son
opposé −x est représenté par l’entier naturel p′ = 256 − p = (255 − p) + 1.
Calculer 255 − p = 11111111 − p est facile, puisqu’il suffit, dans la représentation
binaire de p, de remplacer chaque 0 par un 1 et chaque 1 par un 0 (voir l’exercice 2.22).
i
pyr
Exercice 2.27 Calculer la représentation sur huit bits de l’entier relatif 11, puis celle de son opposé.
L’entier relatif 11 est représenté comme l’entier naturel 11 = 00001011.
Pour calculer la représentation de son opposé, on remplace les 0 par des 1 et les 1 par des 0, ce qui
donne 1111 0100 puis on ajoute 1, ce qui donne 1111 0101. On peut vérifier que ce nombre est bien la
représentation de l’entier relatif −11, c’est-à-dire de l’entier naturel −11 + 256 = 245.
Co
In [2]: a*a
Out[2]: 67600
In [3]: a*a*a
Out[3]: 17576000
In [4]: a*a*a*a
Out[4]: 4569760000
Livre_silo 30 août 2013 16:32 Page 42
es
Informatique pour tous
42
l
rol
Le dernier résultat, 4569760000,
est représenté en binaire par
1 00010000 01100001 00000001 00000000. Si l’on reste dans une représenta-
tion sur 32 bits, le bit le plus à gauche est perdu : on ne mémorise que
00010000 01100001 00000001 00000000 = 274792704, qui n’est évidemment pas le ré-
sultat attendu. On appelle ce phénomène dépassement arithmétique (overflow en anglais).
Ey
Il faut donc changer de représentation pour éviter de perdre la valeur du résultat. En Py-
thon, ce changement de représentation est fait automatiquement, mais c’est loin d’être le
cas dans tous les langages de programmation. La plupart du temps, si le résultat d’un calcul
dépasse les limites de la représentation des entiers, les bits surnuméraires sont purement
et simplement perdus.
Dans ces langages, une conséquence inattendue de la notation en complément à 2
t
dans les dépassements est la suivante : le nombre 2 000 000 000 s’écrit en binaire
01110111 00110101 10010100 00000000 et donc l’opération 2 000 000 000 + 2 000 000 000
gh
a pour résultat 11101110 01101011 00101000 00000000. Ce dernier nombre ne dépasse pas
les 32 bits, mais dans la notation en complément à deux, il représente −294 967 296 et
non pas 4 milliards. La somme de deux entiers positifs trop grands peut donc donner un
résultat négatif ! De même, la somme de deux entiers négatifs peut résulter en un entier
positif.
i
pyr
En Python, la seule limite pour la représentation des entiers, qu’ils soient naturels ou rela-
tifs, est la mémoire disponible sur la machine. Les exemples précédents de dépassements
arithmétiques ne se produisent donc pas.
suivis du marqueur L, qui explicite qu’on passe dans un autre type appelé long. La dernière
ligne de l’exemple précédent affiche donc plutôt
In [4]: a*a*a*a
Out[4]: 4569760000L
En Python 3.x, les types int et long sont fusionnés et, à toutes fins utiles, les entiers sont
toujours de taille illimitée. Le marqueur L n’est plus utilisé.
Pour représenter des entiers de taille arbitraire, il a fallu prendre une certaine distance avec
la représentation machine des entiers. Ainsi, dans l’implantation de référence de Python,
sur les machines 32 bits, lorsque l’on veut représenter un entier x, on découpe la représen-
tation binaire de x par paquets de 15 bits et on stocke les entiers correspondants dans un
tableau d’entiers naturels sur 16 bits. Ce tableau contient donc les chiffres de x en base
215 . De plus, à ce tableau on associe un entier taille qui donne le nombre de chiffres de x
en base 215 et dont le signe est le signe de x.
Livre_silo 30 août 2013 16:32 Page 43
es
2 – Représentation des nombres
43
l
rol
Par exemple, le nombre 4569760000 = 1 00010000 01100001 00000001 00000000 comporte
33 bits, que l’on découpe donc en 100 010000011000010 000000100000000. On a alors :
000000100000000 = 256
010000011000010 = 8386
Ey
100 = 4
Le nombre 4569760000 est donc représenté en mémoire par le tableau [256, 8386, 4], auquel
on associe le nombre taille=3 pour signifier qu’il y a trois chiffres et que l’entier à représenter
est positif. Le nombre -4569760000 sera représenté par le même tableau associé au nombre
-3. Enfin, cas particulier, le nombre 0 est représenté par un tableau vide associé au nombre
de chiffres 0.
t
Sur une machine 64 bits, le principe reste le même, mais on découpe x en paquets de
30 bits et on le représente donc par le tableau de ses chiffres en base 230 .
gh
Exercice 2.28 Trouver la représentation binaire sur huit bits des entiers relatifs 127 et −127, puis de 101
et de −94.
Exercice 2.29 Quels entiers relatifs peut-on représenter avec des mots de 8 bits ? Combien sont-ils ?
Mêmes questions avec des mots de 16 bits, 32 bits et 64 bits.
i
Exercice 2.30 Trouver la représentation décimale des entiers relatifs dont les représentations binaires sur
8 bits sont 01010101 et 10101010.
pyr
Exercice 2.31 Montrer que le bit le plus à gauche vaut 1 pour les entiers relatifs strictement négatifs et
0 pour les entiers relatifs positifs ou nuls.
Exercice 2.32 On considère l’algorithme suivant, où les variables sont toutes des entiers relatifs représen-
tés en complément à deux sur 32 bits.
a←x+y
a←a−z
b←x−z
Co
b←b+y
1 Lesquelles de ces opérations risquent de donner lieu à un dépassement ?
2 Montrer que, quelles que soient les valeurs initiales de x et y, à la fin de l’algorithme les variables a et b
contiennent la même valeur.
3 Que peut-on en conclure à propos de l’addition sur les entiers relatifs en machine ?
Exercice 2.33 * On considère des entiers relatifs sur 4 bits. Dessiner le cercle correspondant, sur le même
modèle que celui de la figure 2.1, en plaçant les 16 nombres : 0, 1, 2, . . . , 7, −1, −2, . . . , −8 à leur place.
Relier les nombres opposés : 1 et −1, 2 et −2, etc. Quelle est l’interprétation géométrique de la fonction
qui à chaque nombre associe son opposé ? Que penser du cas de −8 ?
Exercice 2.34 * Représenter les entiers relatifs 99 et 57 en binaire sur 8 bits. Ajouter les deux nombres
binaires obtenus. Quel est l’entier relatif obtenu ? Pourquoi est-il négatif ?
Livre_silo 30 août 2013 16:32 Page 44
es
Informatique pour tous
44
l
rol
Exercice 2.35 * Démontrer que l’addition de deux entiers relatifs en machine produit un dépassement
arithmétique si et seulement si :
• ces entiers sont de même signe ;
• et le résultat obtenu en machine est de signe opposé au signe des opérandes.
On pourra commencer par supposer que les entiers sont représentés sur n bits, puis exprimer par un
encadrement quels sont les entiers de chaque signe représentables dans ce format. Enfin, on en déduira
Ey
un encadrement du résultat de la somme de deux entiers dans chacun des cas possibles.
De même, déterminer un critère permettant de détecter un dépassement arithmétique lors d’une sous-
traction de deux entiers relatifs.
Exercice 2.36 * (À réaliser dans une version Python 2.x)
À l’aide de la calculatrice Python, déterminez le plus grand entier représentable par le type int sur votre
machine. Vérifiez si votre machine fonctionne en 32 bits ou en 64 bits et calculez la valeur théorique de
ce plus grand entier pour vérifier votre réponse.
t
Exercice 2.37 * L’ensemble des entiers naturels machine écrits sur 16 bits, avec les dépassements de
capacité expliqués dans ce chapitre (calcul sur un nombre de bits arbitraire puis troncature des bits surnu-
gh
méraires) forme-t-il un groupe pour l’addition ? Forme-t-il un anneau pour l’addition et la multiplication ?
Forme-t-il un corps ?
Exercice 2.38 ** L’ensemble des entiers relatifs machine écrits sur 16 bits, avec les dépassements de
capacité expliqués dans ce chapitre forme-t-il un groupe pour l’addition ? Un anneau pour l’addition et la
multiplication ? Un corps ?
Exercice 2.39 * L’ensemble des entiers longs de Python forme-t-il un groupe pour l’addition, en admet-
i
tant que l’on dispose d’une mémoire illimitée ? Forme-t-il un anneau pour l’addition et la multiplication ?
Forme-t-il un corps ?
pyr
Exercice 2.40 Un intérêt majeur de la notation des entiers relatifs en complément à deux est que les
additions, les soustractions et les comparaisons peuvent toutes être effectuées au moyen d’une même
opération, à quelques ajustements près.
Dans tout cet exercice, on supposera que les nombres sont représentés sur n bits.
1 Vérifier sur quelques exemples que la somme de deux entiers naturels représentés sur n bits peut se
faire d’une façon similaire à celle que l’on apprend à l’école primaire pour les nombres en base 10 :
calcul de la somme chiffre par chiffre, de droite à gauche, avec une retenue si nécessaire. Par exemple
Co
Livre_silo 30 août 2013 16:32 Page 45
es
2 – Représentation des nombres
45
l
rol
5 Étant donnés deux entiers relatifs x et y, pour évaluer le test x < y, il suffit en théorie de calculer x − y
et d’observer le signe du résultat ; cependant, les dépassements de capacité faussent cette procédure
puisqu’ils donnent à certains résultats des signes opposés à celui attendu. Il est cependant relativement
facile de corriger ce problème. On appelle N le bit de signe du résultat apparent (calculé par la machine)
de x − y : on aura donc N = 1 si ce résultat est négatif (voir exercice 2.31). On appelle V un bit égal
à 1 si et seulement si le calcul de x − y a donné lieu à un dépassement (voir exercice 2.35 pour la
Ey
détermination de V ). Déterminer une expression en fonction de N et de V qui vaut 1 si et seulement
si x < y.
comme le nombre d’Avogadro ou la constante de Planck. On utilise donc une autre re-
pyr
présentation similaire à la « notation scientifique » des calculatrices, sauf qu’elle est en base
deux plutôt qu’en base dix et définie dans la norme IEEE 754. Un nombre est représenté
sous la forme sm2n où s est le signe du nombre, n son exposant et m sa mantisse. Le signe
est + ou -, l’exposant est un entier relatif et la mantisse est un nombre à virgule, compris
entre 1 inclus et 2 exclu.
Quand on utilise 64 bits pour représenter un nombre à virgule, on utilise 1 bit pour le
Co
Livre_silo 30 août 2013 16:32 Page 46
es
Informatique pour tous
46
l
rol
SAVOIR-FAIRE Représenter en base dix un nombre à virgule flottante
donné en binaire
On identifie le signe s, la mantisse m et l’exposant n. On interprète chacun comme
Ey
un nombre décimal en n’oubliant pas de tenir compte du décalage de 1 023 pour
l’exposant. On calcule enfin la quantité sm2n .
Si l’on note s le bit de signe, e1 . . . e11 les bits d’exposant et m1 . . . m52 les bits de la
mantisse, on peut également donner l’expression directe suivante du nombre repré-
senté :
( )
∑
52
1
e1 ...e11 −1023
(−1) × 2
s
×
t
1+ mi i
i=1
2
gh
Exercice 2.41 Trouver le nombre à virgule flottante représenté par le mot
1100010001101001001111000011100000000000000000000000000000000000.
pyr
Le signe du nombre recherché est −. Le nombre 10001000110 est égal à 1 094 et l’exposant est donc
n = 1 094 − 1 023 = 71. La mantisse est
m = 1, 1001001111000011100000000000000000000000000000000000
= 1 + 1/2 + 1/24 + 1/27 + 1/28 + 1/29 + 1/210 + 1/215 + 1/216 + 1/217
= (217 + 216 + 213 + 210 + 29 + 28 + 27 + 22 + 2 + 1)/217
206 727
Co
=
131 072
Livre_silo 30 août 2013 16:32 Page 47
es
2 – Représentation des nombres
47
l
rol
chiffre de la mantisse. Ils permettent d’obtenir une répartition plus uniforme des valeurs
infinitésimales représentables (voir exercice 2.51).
Ey
Le codage prend également en compte des valeurs exceptionnelles : +∞, −∞ et enfin
les NaN (Not a Number) qui signalent en général une erreur de calcul, par exemple une
division par zéro ou la racine carrée d’un nombre négatif. Ces valeurs non numériques sont
représentées respectivement par les mots de 64 bits suivants :
0 11111111111 0000000000000000000000000000000000000000000000000000
1 11111111111 0000000000000000000000000000000000000000000000000000
0 ou 1 puis 11111111111 puis tout mot de 52 bits non tous nuls.
t
gh
2.3.3 Dépassements de capacité et problèmes de précision
De même que les entiers, les nombres à virgule flottante possèdent certaines limites in-
évitables. En voici un rapide inventaire, qui sera détaillé dans la troisième partie Ingénierie
numérique et simulation, au cours de laquelle ces problèmes se poseront concrètement.
i
Les nombres à virgule flottante étant représentés sur un nombre donné de bits, il existe
pyr
Tout calcul dont le résultat dépasse cette limite produit une situation qui est également
appelée dépassement arithmétique ; cependant, au lieu de produire une valeur par simple
troncature des bits surnuméraires, on utilise les nombres spéciaux +∞ et −∞, selon le
signe du résultat du calcul. Dans les opérations qui suivent, ceux-ci respectent les règles
de calcul usuelles sur les limites et produisent un NaN lorsqu’aucune règle de calcul ne peut
être appliquée sans ambiguïté.
Une situation similaire mais qui n’existait pas pour les entiers se produit lorsque l’on veut
représenter un nombre trop proche de 0 :
• Son exposant est inférieur à −1 022, le plus petit exposant représentable sur 11 bits.
• Ou ce nombre est inférieur en valeur absolue au plus petit nombre dénormalisé.
On parle alors de dépassement par valeurs inférieures ou de soupassement arithmétique (en
anglais underflow). Selon les cas, le résultat d’un calcul qui tombe dans cette plage de
valeurs peut soit être arrondi à zéro (le signe du résultat est cependant conservé), soit
produire une erreur.
Livre_silo 30 août 2013 16:32 Page 48
es
Informatique pour tous
48
l
rol
2.3.4 Les arrondis
Il est rare que le résultat d’un calcul faisant intervenir deux nombres à virgule flottante
donne un résultat représentable exactement sur 64 bits. Même sans effectuer de calcul, les
nombres décimaux ne sont pour la plupart pas représentables exactement dans ce format.
Ey
Par exemple, le nombre 0, 4 admet pour développement en base 2 :
1 1 1 1 1 1 1 1
+ 3 + 6 + 7 + 10 + 11 + 14 + 15 + . . . = 0, 011001100110011 . . .
22 2 2 2 2 2 2 2
qui est un développement infini périodique.
t
Si l’on essaye de construire sa représentation sur 64 bits, on obtient :
gh
signe exposant mantisse bits non représentés
0 01111111101 1001100110011001... 1001100110011001 10011001...
pyr
haut. On notera que cette opération est facile à effectuer, puisqu’il suffit d’arrondir vers
le haut si le premier bit non représenté vaut 1, vers le bas sinon. Seul cas particulier, si la
valeur des bits non représentés est exactement égale à la moitié de la valeur du dernier bit
de la mantisse, alors le nombre est arrondi de telle sorte que son dernier bit vaille 0.
La valeur approchée choisie pour 0, 4 est donc la suivante :
0, 011001100110011001100110011001100110011001100110011010
Co
≃ 0, 400000000000000022204460492503
Livre_silo 30 août 2013 16:32 Page 49
es
2 – Représentation des nombres
49
l
rol
D’autres erreurs d’arrondis se présentent lorsque l’on effectue des calculs, notamment entre
des nombres dont les ordres de grandeur sont très différents. Dans ce cas, on effectue le
calcul comme si la précision était infinie, puis on arrondit le résultat obtenu.
1 + 2−54 = 1, 000000000000000000000000000000000000000000000000000001
Ey
demanderait 54 bits de mantisse pour être représenté exactement ; il est donc arrondi à 1.
Cet arrondi peut paraître bénin, mais s’il se produit dans une suite de calculs, il peut contre-
venir aux propriétés habituelles des opérations sur les nombres réels. Par exemple, en Py-
thon, on peut observer
In [5]: 1 + 2**-54 - 1
t
Out[5]: 0.0
gh
La somme est en effet calculée en premier et arrondie à 1, puis la différence est calculée et
le résultat vaut donc 0. En revanche
In [6]: 1 - 1 + 2**-54
Out[6]: 5.551115123125783e-17
Comme la différence est calculée en premier, le résultat est ici celui attendu ; mais
i
pyr
subir des erreurs d’arrondis. On remplacera donc un tel test par une condition de la forme
abs(a-b)<epsilon où epsilon est une valeur proche de zéro, choisie en fonction du problème
à traiter et de l’ordre de grandeur des erreurs auxquelles on peut s’attendre sur a et b.
Exercice 2.42 Trouver le nombre à virgule flottante représenté par le mot binaire
0001000000111101001110010101100000000000000000000000000000000000.
Exercice 2.43 Comment est représenté le nombre 2−1 022 (qui est égal à environ 2, 225 × 10−308 ) ?
Exercice 2.44 Représenter les nombres suivants avec la valeur la plus précise que vous pourrez trouver
dans la littérature : la vitesse de la lumière dans le vide, le nombre d’Avogadro, la constante de Coulomb.
Pour lesquels de ces nombres la précision des nombres à virgule flottante sur 64 bits est-elle insuffisante ?
Connaissez-vous une grandeur physique ou chimique pour laquelle l’étendue des nombres à virgule flot-
tante est insuffisante ?
Exercice 2.45 Par quel mot binaire est représenté en machine le nombre entier 13 ? Et le nombre à virgule
flottante 13, 0 ?
Exercice 2.46 À combien de décimales environ correspondent 52 chiffres binaires après la virgule ?
Livre_silo 30 août 2013 16:32 Page 50
es
Informatique pour tous
50
l
rol
Exercice 2.47
1 Quel est le plus grand nombre que l’on peut représenter en virgule flottante sur 64 bits ?
2 Quel est le plus petit nombre, donc négatif, que l’on peut représenter en virgule flottante sur 64 bits ?
3 Quel est le plus petit nombre normalisé strictement positif que l’on peut représenter en virgule flottante
sur 64 bits ?
Il est à noter qu’en Python, la commande float_info de la bibliothèque sys fournit ces informations,
Ey
ainsi que la plupart des caractéristiques de la représentation des nombres en virgule flottante : taille de
la mantisse, différence relative entre deux valeurs consécutives, etc. (Attention, les valeurs possibles pour
l’exposant sont décalées d’une unité.)
Exercice 2.48 Reprendre les questions de l’exercice précédent pour les nombres à virgule flottante simple
précision : ceux-ci sont représentés sur 32 bits, avec 1 bit de signe, 8 bits d’exposant et 23 bits de mantisse.
Ils sont utilisés dans certains langages de programmation, notamment pour des raisons d’économie de
mémoire et de rapidité des calculs ; en Python cependant, leur utilisation n’apporterait pas un bénéfice
significatif.
t
Exercice 2.49 Déterminer l’écriture binaire et une valeur décimale approchée du plus grand nombre à
virgule flottante sur 64 bits strictement inférieur à 1.
gh
Même question avec le plus petit nombre strictement supérieur à 1.
Exercice 2.50 * En s’inspirant de l’algorithme de conversion des entiers naturels en base k, concevoir un
algorithme de conversion d’un nombre décimal vers son écriture en virgule flottante.
On pourra commencer par le cas des nombres compris entre 1 et 2, pour lesquels l’exposant est connu.
On traitera ensuite les nombres inférieurs à 1 ou supérieurs à 2 en se ramenant au cas précédent.
i
pyr
Exercice 2.51 * Cet exercice a pour objectif d’étudier l’intérêt des nombres dénormalisés et quelques-
unes de leurs particularités.
1 Soit x1 le plus petit nombre à virgule flottante normalisé strictement positif représentable sur 64 bits,
et x2 le plus petit nombre représentable strictement supérieur à x1 .
x2 − x1
Calculer x1 et x2 . Combien vaut l’écart relatif ?
x1 − 0
2 Refaire la même étude avec les deux plus petits nombres dénormalisés y1 et y2 .
y2 − y1
3 Calculer également l’écart relatif et interpréter ce résultat en termes de densité des nombres
x2 − x1
Co
Exercice 2.54 *
1 Montrer qu’à chaque multiplication de deux nombres à virgule flottante, comme on arrondit le calcul
en ne gardant que 52 chiffres après la virgule, on introduit une erreur relative de l’ordre de 2−52 .
2 Quelle est la valeur de cette erreur en base dix ?
3 Si on fait plusieurs multiplications, ces erreurs s’accumulent. Quelle est l’erreur relative d’un calcul qui
est formé d’un million de multiplications, qui dure quelques millisecondes sur un ordinateur usuel ?
Livre_silo 30 août 2013 16:32 Page 51
es
2 – Représentation des nombres
51
l
rol
Exercice 2.55 On considère le programme Python suivant :
x = 1.0
y = x + 1.0
while y - x == 1.0:
x = x * 2.0
Ey
y = x + 1.0
1 Si l’on calculait sur des nombres décimaux exacts, que se passerait-il lors de l’exécution de ce pro-
gramme ?
2 Écrire ce programme dans un éditeur Python et l’exécuter. Que constate-t-on ?
3 Modifier le programme de façon à déterminer au bout de combien d’exécutions du corps de la boucle
il s’arrête, ainsi que la valeur de x à la fin de cette exécution.
4 Comment est représentée cette dernière valeur de x ? Et celle de y ?
5 Proposer une explication de ce comportement.
Exercice 2.56 On considère le programme Python suivant :
t
a = 0.0
gh
for n in range(10):
a = a + 0.1
print(repr(a))
1 Si l’on calculait sur des nombres décimaux exacts, que se passerait-il lors de l’exécution de ce pro-
gramme ?
2 Écrire ce programme et l’exécuter. Que constate-t-on ?
3 Vérifier que la représentation binaire de 0, 1 est
i
0011111110111001100110011001100110011001100110011001100110011010.
Quel nombre décimal cette représentation désigne-t-elle en réalité ?
pyr
4 En déduire les représentations binaires des différentes valeurs prises par a au cours de l’exécution de ce
programme et les nombres décimaux que cette représentation désigne en réalité.
5 Expliquer l’affichage obtenu.
Exercice 2.57 * La représentation binaire en virgule flottante sur 64 bits permet-elle de représenter :
1 tous les entiers relatifs représentables sur 64 bits ?
2 plus de nombres distincts que la représentation des entiers relatifs sur 64 bits ? ou moins ?
3 des nombres décimaux qui ne soient pas entiers ?
Co
Livre_silo 30 août 2013 16:32 Page 52
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 53
l es
rol
t Ey Algorithmique
Deuxième partie
gh
et programmation
i
pyr
Dans cette partie, nous présentons les notions clés de l’algorithmique (cha-
pitres 3 et 4) en nous attachant systématiquement à démontrer que les al-
gorithmes écrits produisent le résultat attendu. Nous abordons également
la traduction de ces algorithmes sous forme de programmes. Nous présen-
Co
Livre_silo 30 août 2013 16:32 Page 54
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 55
l es
rol
3
t Ey
Expressions :
gh
types et opérations
i
pyr
Livre_silo 30 août 2013 16:32 Page 56
es
Informatique pour tous
56
l
rol
3.1 Expressions et types simples
3.1.1 Expression
Une expression est une suite de caractères définissant une valeur. Pour calculer cette valeur,
Ey
la machine doit évaluer l’expression. Voici des exemples d’expressions : 42, 1+4, 1.2 / 3.0,
x+3.
Out[1]: 42
In [2]: 1+4
Out[2]: 5
i
Les valeurs en Python sont typées selon l’objet qu’elles représentent. Une valeur peut ainsi
pyr
être de type entier, de type flottant, de type chaîne de caractères… Des types similaires
existent dans la plupart des langages de programmation. Leur représentation en mémoire
varie beaucoup d’un langage à l’autre, mais ce sont souvent les mêmes objets que l’on
cherche à traduire.
Une expression en Python n’a pas de type a priori car le type de sa valeur dépend de l’envi-
ronnement, plus précisément des types de ses sous-expressions. Pour simplifier, on consi-
dèrera dans un premier temps des expressions dont les valeurs sont toutes d’un même type.
Co
L’expression 42 est de type entier alors que 1.2 / 3.0 est de type flottant.
Pour afficher le type d’une expression après l’avoir évaluée, on utilise type :
In [3]: type(42)
Le mot qui suit class indique le type de la valeur, entier (int en anglais) pour la première
expression et flottant (float en anglais) pour la seconde.
On ignorera ici ce mot class qui fait référence au fait que Python est un langage orienté
objet.
Livre_silo 30 août 2013 16:32 Page 57
es
3 – Expressions : types et opérations
57
l
rol
Dans la plupart des langages de programmation, une expression est :
• soit une constante, comme 42 ;
• soit un nom de variable (valeurs littérales comme x ou compteur) ;
• soit une expression entre parenthèses, comme en mathématiques (2-3) ;
Ey
• soit composée de plusieurs expressions réunies à l’aide d’un opérateur, comme 1 + (3 * 4)
où les expressions 1 et 3 * 4 sont réunies par l’opérateur + ;
• soit composée d’une fonction appliquée à d’autres expressions, comme fact(4).
Dans les prochaines sections, on présente les constantes et les opérateurs sur les types
simples usuels, puis on verra la notion de variable. On présentera enfin des types plus
complexes. La notion de fonction sera l’objet du chapitre 5.
t
3.1.2 Entiers
gh
Constantes
Les constantes entières sont écrites en base 10 avec des nombres ayant autant de chiffres
qu’on le souhaite. On dit que Python utilise des entiers longs ; par abus de langage, on
parle également de précision arbitraire pour exprimer que tous les chiffres significatifs
i
sont mémorisés.
pyr
Out[5]: 42
In [6]: -12
Co
Out[6]: -12
In [7]: 12345678910111213141516171819
Out[7]: 12345678910111213141516171819
Out[8]: 6
Out[9]: -2
Livre_silo 30 août 2013 16:32 Page 58
es
Informatique pour tous
58
l
rol
• la multiplication utilisant le symbole * ;
In [10]: 2*4
Out[10]: 8
Ey
// et %.
In [11]: 17 // 5
Out[11]: 3
In [12]: 17 % 5
Out[12]: 2
Out[13]: 1267650600228229401496703205376
• l’opposé utilisant également le symbole - mais devant une expression, on parle alors de
i
position préfixe.
pyr
In [14]: -(2+3)
Out[14]: -5
POUR ALLER PLUS LOIN La division euclidienne appliquée aux nombres négatifs
On rappelle ici une variante d’un théorème vu en mathématiques :
éorème. Pour tous entiers n, m avec m ̸= 0, il existe un unique couple (q, r) ∈ Z2 tel que n = qm + r
Co
Out[15]: 1
et
In [16]: 8 % 5
Out[16]: 3
Out[17]: 0
Attention, ce théorème est différent de celui que l’on utilise usuellement dans le cours de
mathématiques :
Livre_silo 30 août 2013 16:32 Page 59
es
3 – Expressions : types et opérations
59
l
rol
éorème. Pour tous entiers n, m avec m ̸= 0, il existe un unique couple (q, r) ∈ Z × N tel que
n = qm + r et 0 ⩽ r < |m|.
Cette différence s’exprime lorsque l’on divise par des nombres négatifs.
Si on divise 3 par −2 on obtient ces deux calculs :
Ey
3 = −2 × −2 + (−1) = −1 × −2 + 1
En Python, c’est le premier couple (−2, −1) qui est renvoyé car −2 < −1 ⩽ 0, mais en
mathématiques, on considère le second couple (−1, 1) car 0 ⩽ 1 < | − 2| = 2.
La division euclidienne ne pouvant s’effectuer qu’avec un diviseur non nul, il est possible
qu’une expression n’ait pas de valeur car elle reviendrait à diviser par 0. Python l’indique
t
par un message d’erreur :
gh
In [18]: 1 // 0
ne pas avoir à demander à l’utilisateur laquelle des deux expressions calculer, les langages
pyr
Livre_silo 30 août 2013 16:32 Page 60
es
Informatique pour tous
60
l
rol
3.1.3 Flottants
Constantes
Les nombres à virgule flottante, souvent appelés plus simplement flottants, ont été vus au
Ey
chapitre 2.
Pour écrire un nombre flottant (sous réserve qu’il ne dépasse pas la capacité de ce type),
on utilise le symbole . à la place de la virgule.
In [19]: 3.2
Out[19]: 3.2
In [20]: -0.01
t
Out[20]: -0.01
gh
EN PRATIQUE Écriture abrégée des flottants
Lorsque l’un des deux côtés du symbole . est 0 on peut l’omettre :
In [21]: 2.
i
Out[21]: 2.0
pyr
In [22]: .3
Out[22]: 0.3
En toute généralité, on peut vouloir écrire un flottant sous la forme m10n où m la mantisse
et n l’exposant sont des nombres relatifs. Par exemple, 0, 0012 = 12 × 10−4 et −1 230 =
−123 × 101 . Pour traduire cela en Python, on utilise la lettre e pour séparer la mantisse et
Co
l’exposant :
In [23]: 12e-4
Out[23]: 0.0012
In [24]: -123e1
Out[24]: -1230.0
On remarque que Python ajoute .0 même quand il s’agit d’un entier au sens mathématique
du terme. Cela permet de rappeler d’un coup d’œil qu’il est du type flottant.
Out[25]: 4.3
Livre_silo 30 août 2013 16:32 Page 61
es
3 – Expressions : types et opérations
61
l
rol
Ici on remarque que les flottants offrent une précision moins bonne que les entiers pour
les calculs sur des valeurs entières :
In [26]: 2.0 ** 100.0
Out[26]: 1.2676506002282294e+30
Ey
Lors d’une opération entre un flottant et un entier, l’entier considéré est converti à la volée
en flottant.
In [27]: 1.2 * 2
Out[27]: 2.4
Out[29]: 0
pyr
Pour obtenir le quotient décimal de deux entiers, il suffit d’indiquer que l’un des arguments
doit être pris comme un flottant, soit en ajoutant un ., soit en utilisant le convertisseur
float :
In [30]: 1. / 3
Out[30]: 0.3333333333333333
In [31]: float(1) / 3
Out[31]: 0.3333333333333333
Co
Dans les versions 3.x de Python, l’opérateur / n’existe que pour les flottants ; quand on
l’applique à des entiers, ces derniers sont convertis en flottants et le résultat est un flottant
égal à leur quotient décimal :
In [32]: 1 / 3
Out[32]: 0.3333333333333333
Livre_silo 30 août 2013 16:32 Page 62
es
Informatique pour tous
62
l
rol
• Pour représenter une grandeur physique, on choisit plutôt des flottants. Même
lorsqu’il est possible d’utiliser des entiers pour représenter des grandeurs physiques,
cela n’a généralement que peu d’intérêt, celles-ci étant pour la plupart connues avec
seulement une dizaine de chiffres significatifs.
Ey
• Enfin, si on doit effectuer des calculs sur des données dont les ordres de grandeur
sont très différents, les flottants ne sont pas appropriés (voir section 2.3.4).
4 L’attraction exercée par la Lune sur la Terre est très nettement supérieure à celle exercée par les satellites
artificiels (de l’ordre de 1017 fois supérieure pour de nombreux satellites géostationnaires par exemple).
pyr
part, on a besoin de montants inférieurs à une unité (on veut représenter des centièmes
d’euros voire moins : les cours des devises sont en général exprimés en euros à 10−4 près)
et d’autre part le calcul en virgule flottante n’est pas adapté en raison des erreurs liées
aux approximations (voir exercice 3.5). Pour un comptable, il n’est pas acceptable qu’un
centime d’euro manque à l’appel !
Il est donc préférable de représenter les quantités monétaires par des entiers. On prendra
par exemple comme unité le centime d’euros, voire 10−4 euro. Il faut évidemment prêter
attention à la question des divisions : lorsqu’un comptable répartit un montant à payer en
vingt-trois parts, il est nécessaire pour lui que la somme des vingt-trois parts soit rigoureu-
sement égale au montant à payer ; il convient donc de voir comment répartir le reste de la
division euclidienne.
Certains langages de programmation proposent cependant un type de données prévu pour
représenter les décimaux. Python possède un module implantant un tel type de données :
le module decimal.
Livre_silo 30 août 2013 16:32 Page 63
es
3 – Expressions : types et opérations
63
l
rol
SAVOIR-FAIRE Convertir une expression numérique d’un type à un autre
Pour utiliser le nombre 42, on écrira 42 si l’on veut utiliser un entier et 42.0 ou sim-
plement 42. si l’on veut un flottant.
Ey
On peut, en cas de besoin, convertir un entier en flottant en lui appliquant la fonction
float et un flottant en un entier par la fonction int. Attention : int(x) ne calcule pas
la partie entière de x mais le tronque, c’est-à-dire calcule la partie entière de |x|, puis
affecte le résultat du signe de x.
Enfin, si on utilise un entier dans une opération qui requiert un flottant (quotient
décimal ou racine carrée par exemple), Python effectue la conversion automatique-
ment. Il est tout de même recommandé d’écrire explicitement les conversions là où
t
on souhaite qu’elles aient lieu, ne serait-ce que dans un souci de lisibilité.
gh
Exercice 3.4 Les expressions 8.5/2.5, int(8.5)/int(2.5) et int(8.5/2.5) sont-elles équivalentes ?
Non et à deux titres. Au niveau des types d’abord, la dernière expression est un entier, alors que les deux
premières sont des flottants puisqu’elles sont le résultat de quotients décimaux /.
Ensuite, si on calcule leurs valeurs :
• Dans la première expression, le quotient 8.5/2.5 s’évalue à 3.4.
i
• Dans la seconde expression, int(8.5) et int(2.5) s’évaluent respectivement à 8 et 2. Ces valeurs sont
pyr
ensuite automatiquement reconverties en flottants pour effectuer la division, mais leur partie décimale
a été perdue au passage : 8.0/2.0 s’évalue à 4.0.
• Dans la troisième expression, on retrouve le quotient 8.5/2.5 de la première expression. Sa valeur 3.4
est ensuite tronquée pour obtenir l’entier 3.
On remarquera que, même en effectuant une conversion supplémentaire vers les entiers, les deux der-
nières expressions ne peuvent être rendues égales.
Exercice 3.5 *
1 Qu’affiche Python lorsqu’on lui demande de calculer 1 - 1./3 - 1./3 - 1./3 ? Expliquer.
Co
Exercice 3.6 Quelle est la valeur des expressions suivantes, lorsqu’elle existe ?
• 1.5 + 1.5
• 1.5 - 1
• 3. * 7
• (1-2).
• .5-.3
• 4 / (9 - 3**2)
• float(7 // 2)
Vérifier les réponses dans un interpréteur.
Exercice 3.7 Les expressions int(a/b) et a//b sont-elles équivalentes pour toutes les valeurs entières de
a et de b ?
Le démontrer ou proposer un contre-exemple.
3.1.4 Booléens
Livre_silo 30 août 2013 16:32 Page 64
es
Informatique pour tous
64
l
rol
Constantes
Les booléens constituent un type spécial dont les constantes sont particulièrement simples :
il n’y en a que deux, True et False. Ils servent à représenter le résultat de l’évaluation d’expres-
sions logiques qui peuvent prendre soit la valeur vraie, représentée par True, soit la valeur
Ey
fausse, représentée par False.
pyr
b1 b2 not b1 b1 and b2 b1 or b2
Out[33]: False
Out[34]: False
Out[35]: True
Livre_silo 30 août 2013 16:32 Page 65
es
3 – Expressions : types et opérations
65
l
rol
Les opérateurs and et or sont dits paresseux : ils ne calculent que ce qui est nécessaire pour
évaluer une expression. Par exemple :
In [36]: 0 != 0 and 1/0 == 2
Out[36]: False
Ey
Le booléen situé à gauche de and valant False, celui de droite n’est pas évalué. Écrite dans
l’ordre inverse, l’expression aurait produit une erreur de division par zéro. De même, l’opé-
rateur or n’évalue pas son membre droit si son membre gauche vaut True.
Out[38]: False
pyr
Out[39]: True
Out[40]: False
Opérateurs de comparaison
L’apparition la plus fréquente de booléens se fait lors de comparaisons d’autres types.
Co
Out[41]: True
In [42]: 1 == 0
Out[42]: False
Python dispose d’un raccourci pour l’expression not (e1 == e2), à savoir e1 != e2.
Livre_silo 30 août 2013 16:32 Page 66
es
Informatique pour tous
66
l
rol
In [43]: 0.1 + 0.1 + 0.1 == 0.3
Out[43]: False
Tester l’égalité de deux flottants est donc presque toujours une erreur. Il vaut mieux tester
si la différence de ces deux flottants est ou non significative, c’est-à-dire est plus petite
Ey
qu’une précision donnée.
Les types que l’on va considérer dans cet ouvrage sont pour la plupart également compa-
rables pour une relation d’ordre fixée. On peut alors utiliser cet ordre pour comparer deux
expressions.
L’ordre strict s’écrit :
t
In [44]: 1 < 3
gh
Out[44]: True
Out[45]: True
i
Pour effectuer des comparaisons au sens large, on ajoute = après le symbole de comparai-
pyr
son :
In [46]: 1 <= 3
Out[46]: True
In [47]: 1 <= 1
Out[47]: True
Co
In [48]: 3 >= 1
Out[48]: True
Enfin, il est possible d’effectuer des comparaisons avec plus de deux éléments. On écrit
alors cela comme en mathématiques :
In [49]: 1 < 2 < 3
Out[49]: True
Out[50]: True
Livre_silo 30 août 2013 16:32 Page 67
es
3 – Expressions : types et opérations
67
l
rol
ATTENTION Les opérateurs logiques bit à bit
Il existe en Python comme dans d’autres langages deux opérateurs & et | ressemblant à and
et or mais avec de subtiles différences de signification qui peuvent rendre leur comporte-
ment difficile à comprendre :
Ey
• Ils ont une plus forte précédence que les comparaisons, ainsi on aura :
In [51]: False == False | True
Out[51]: False
car dans cette expression on évalue d’abord False | True.
• Ils ne sont pas paresseux, les deux membres sont systématiquement évalués :
In [52]: (0 != 0) & (1/0 == 2)
Out[53]: 8
pyr
Exercice 3.8 Quelle est la valeur des expressions booléennes suivantes, lorsqu’elle existe ?
• 3 * 3.5 > 10
• 3. * 7 == 21
• 3 - 1 => 1
• 0 < 10**-300 == 100**-150
• not (2-1 == 1 == 4+3)
• not (True and False)
• not (not True)
Co
Livre_silo 30 août 2013 16:32 Page 68
es
Informatique pour tous
68
l
rol
3.2 Variables
3.2.1 Notion de variable
Dans un programme, une variable sert à désigner une zone mémoire de l’ordinateur. On
Ey
peut y stocker une valeur, accéder à cette dernière et la changer.
Pour faire référence à une zone mémoire on utilise un nom de variable. On représentera
dans ce chapitre et les suivants une variable par un diagramme en forme de rectangle dont
le contenu est la valeur de la variable, surmonté d’une étiquette figurant le nom de la
variable.
x
42. .
t
Ainsi la variable nommée x de contenu 42 sera représentée par :
gh
Dans certains langages de programmation, une variable peut avoir deux noms, comme
y z
. qui a à la fois le nom y et le nom z, mais ce comportement n’est pas possible en
3.14
Python, excepté dans le cas de structures complexes telles les listes (voir chapitre 6).
On considère qu’une valeur à laquelle n’est attaché aucun nom de variable n’existe pas en
i
mémoire. C’est raisonnable car il s’agit alors d’espace mémoire que le système peut réutiliser
pyr
ailleurs.
Une fois que l’on est familier avec la notion de variable, on peut s’intéresser de plus près
à la façon précise dont un langage la met en œuvre. Mais il est important, quand on ap-
prend l’informatique, de commencer par acquérir les concepts généraux qui pourront res-
servir dans un autre contexte, et ensuite seulement de préciser comment ces concepts se
traduisent dans un cadre particulier.
Livre_silo 30 août 2013 16:32 Page 69
es
3 – Expressions : types et opérations
69
l
rol
x
L’expression x+3 prend ainsi la valeur 5 dans l’état 2. car le nom de variable x a été
x
remplacé par son contenu 2. Dans l’état 4. , cette expression prend la valeur 7.
Ey
x y z
Dans l’état 1. 2. 3. qui comporte plusieurs variables, l’expression x+y*z s’éva-
lue en 1+2*3 et a donc la valeur 7.
Au cours de l’évaluation d’une expression, l’état ne peut pas changer. Ainsi, dans l’expres-
sion x*x, aux deux occurrences du nom x sera substituée la même valeur.
Si, lors de l’évaluation d’une expression, un nom de variable est utilisé alors qu’il n’appa-
t
raît pas dans l’état courant, il devient impossible d’attribuer une valeur à cette expression.
Python renvoie alors le message suivant :
gh
In [54]: x
Pour qu’une expression ait une valeur, il faut donc que chacun des noms de variables qu’elle
contient soit présent dans l’état courant. Toutefois, cela n’est pas suffisant. On a déjà vu
i
pyr
x
pas avoir de valeur, par exemple dans l’état 0. , alors que x est bien présent dans cet
état.
des expressions, D l’ensemble des valeurs possibles (l’union de toutes les constantes de tous
les types) et V l’ensemble des noms de variables. Un état peut alors être décrit sous la forme
d’un sous-ensemble de V × D, dans lequel deux éléments ne partagent pas le même nom
de variable. Cela signifie qu’à chaque nom de variable on associe la valeur correspondante.
x y z
L’état 1. 2. 3. correspond alors à l’ensemble {(x, 1), (y, 2), (z, 3)}.
eval : E × S 7→ D
En effet, comme on vient de le voir, elle n’a pas toujours de valeur à associer à un couple
(expression,état). On dit que l’évaluation est une fonction partielle.
å
Livre_silo 30 août 2013 16:32 Page 70
es
Informatique pour tous
70
l
rol
La définition de eval se fait en raisonnant sur la forme des expressions ; on parle alors de
définition par induction. Par exemple, on pourra définir des règles de la forme :
Ey
À gauche du symbole =, le symbole + fait référence à l’opérateur de construction des
expressions Python. À droite de l’égalité, il s’agit de l’opération mathématique d’addition.
a b
Exercice 3.11 Déterminer de tête la valeur des expressions suivantes dans l’état 5. -1. . Vérifier
vos réponses à l’aide de l’interpréteur interactif.
• a + 2
t
• b - 1
• a + 2*b
gh
• a * a * a
• b <= 0
• b <= 0 or a < 10
• a * b < 2
Exercice 3.12 * Déterminer dans chacun des cas suivants tous les états tels que :
• ab + 1 vaut 2.
i
• jj / jj vaut 1.
• ab * jj vaut 0.
pyr
nom_de_variable = expression
Jusqu’ici, on a uniquement évalué des expressions. Une instruction est une autre forme
d’interaction qui demande d’effectuer une modification de l’état. En général, une instruc-
tion n’a pas de valeur ; après l’avoir exécutée, Python n’affiche rien. Par exemple :
In [55]: x = 2
redonne directement l’invite de l’interpréteur, non sans avoir modifié l’état qui contient
x
désormais 2. . Il suffit d’évaluer une expression à la suite pour le constater :
In [56]: x + 1
Out[56]: 3
Livre_silo 30 août 2013 16:32 Page 71
es
3 – Expressions : types et opérations
71
l
rol
Comme on l’a annoncé plus haut, l’instruction :
In [57]: y = x
x y
fait passer dans l’état 2.
2. . En effet, l’expression x a été évaluée à 2 et c’est le
Ey
résultat de cette évaluation qui est placé dans la variable de nom y.
On remarque qu’il n’est pas possible de supprimer une déclaration de l’état. Pour que x ne
soit plus défini, il n’y a pas d’autre solution que de relancer Python.
Il n’est pas recommandé d’utiliser ce genre de mécanisme et il vaut mieux considérer qu’il
est impossible de supprimer une variable.
i
3.2.4 Affectation
pyr
Pour changer la valeur d’une variable, on utilise la même instruction que pour la déclara-
tion.
x
Si on est dans l’état 1. , l’instruction :
In [58]: x = 2
Co
x
fait passer dans l’état 2. .
Une variable peut changer de type par affectation :
In [59]: x = 1.2
Dans cette instruction, x joue deux rôles bien différents. À gauche, il s’agit du nom de la
variable sur laquelle s’effectue l’affectation. À droite, il apparaît comme élément de l’ex-
Livre_silo 30 août 2013 16:32 Page 72
es
Informatique pour tous
72
l
rol
x
pression qui va être évaluée. Ainsi, si l’état courant est 3. , l’évaluation de x+1 va donner
x
la valeur 4 et l’affectation est équivalente à x = 4. On passe donc dans l’état 4. .
Ey
EN PRATIQUE Opération-affectation
L’instruction x = x+1 est équivalente à l’instruction x += 1, qu’on peut lire « ajouter 1 à x »,
sous-entendu dans l’état courant. Des opérations-affectations similaires existent pour la
plupart des opérateurs courants : par exemple y *= e multiplie la variable y par l’expression
e, et z -= e retranche l’expression e à la variable z.
t
Il existe une expression particulière, input(), qui attend que l’utilisateur tape quelque chose
au clavier et qui prend pour valeur la chaîne de caractères correspondante. On l’utilise très
gh
souvent sous la forme variable = input(), de sorte que la variable contiendra ce qui est tapé
au clavier.
In [61]: a = input()
3.5
i
Précisons que 3.5 est ici ce que l’utilisateur a tapé au clavier ; cette instruction, comme les
pyr
Out[62]: '3.5'
On voit que la valeur donnée à a est une chaîne de caractères ; si on veut plutôt récupérer
une valeur numérique, on écrira int(input()) ou float(input()) pour obtenir une valeur res-
pectivement entière ou en virgule flottante. On verra plus tard que cette expression n’est
Co
Combien en voulez-vous ? 42
Livre_silo 30 août 2013 16:32 Page 73
es
3 – Expressions : types et opérations
73
l
rol
SAVOIR-FAIRE Échanger le contenu de deux variables
x y
On se place dans l’état 1. 2. et l’on souhaite placer le contenu de la variable
Ey
y dans la variable x et le contenu de la variable x dans la variable y.
pyr
t = x
x = y
y = t
On va exécuter ces instructions l’une après l’autre : après la première, on passe dans
x y t x y t
l’état 1. 2. 1. , après la deuxième dans l’état 2. 2. 1. et en-
Co
x y t
fin après la troisième dans l’état 2. 1. 1. .
On constate que les valeurs des variables nommées x et y ont bien été échangées.
Il faut toutefois prendre garde au fait que la variable t existe peut-être déjà dans l’état
courant. L’essentiel est de choisir une variable qui soit n’est pas présente dans l’état
courant, soit ne nous intéresse plus.
On verra page 75 une solution spécifique à Python pour résoudre ce problème de
l’échange de deux variables.
Exercice 3.13 On considère un état dans lequel sont définies trois variables de noms x, y et z. Décrire
une suite d’instructions permettant de placer :
• le contenu de x dans z,
• le contenu de y dans x,
• le contenu de z dans y.
Livre_silo 30 août 2013 16:32 Page 74
es
Informatique pour tous
74
l
rol
Exercice 3.14 Partant de l’état initial vide, décrire l’évolution de l’état lors de l’exécution des instructions
suivantes, évaluées du haut vers le bas :
• x = 3
• y = 2
• x = 1 + y * x
• y = y
Ey
• y = 1.2 - x
pyr
Comme en mathématiques, pour construire une expression n-uplet, il suffit de placer des
expressions entre parenthèses séparées par des virgules.
Voici un couple composé d’un entier et d’un flottant :
In [64]: (1, 2.2)
et un triplet d’entiers :
In [65]: (0, 1, 2)
Out[65]: (0, 1, 2)
t
.
On arrive alors dans l’état (1, 2.2) . On remarque que dans cette instruction, on peut
omettre les parenthèses. Ainsi, l’instruction suivante lui est équivalente :
In [67]: t = 1, 2.2
Livre_silo 30 août 2013 16:32 Page 75
es
3 – Expressions : types et opérations
75
l
rol
Pour accéder aux composantes du n-uplet, c’est-à-dire aux sous-valeurs qu’il contient, on
utilise l’expression t[i] où i est le numéro de la composante, on parle de son indice.
Ey
En Python, comme dans la plupart des langages de programmation, on commence à nu-
méroter à partir de 0 et non de 1.
Out[68]: 1
t
et pour la seconde :
gh
In [69]: t[1]
Out[69]: 2.2
Les n-uplets sont immuables. Cela signifie qu’il n’est pas possible d’affecter de nouvelles
In [70]: t[0] = 2
Déconstruction
Co
t x y
.
amène ainsi dans l’état (1, 2.2) 1. . .
2.2
Il est important de remarquer que l’expression n-uplet est évaluée avant de faire les affec-
tations. On peut donc s’en servir pour échanger deux variables d’un coup :
In [72]: x,y = y, x
Livre_silo 30 août 2013 16:32 Page 76
es
Informatique pour tous
76
l
rol
Cas où n = 0 ou 1
Il n’existe qu’un 0-uplet, c’est celui qui ne contient rien, il s’écrit (). C’est une valeur un peu
étrange dont l’utilité est limitée.
Plus courant, un 1-uplet ne contenant que la valeur v s’écrit (v,). Ici, la virgule finale est
Ey
essentielle, car c’est elle qui distingue le 1-uplet (v,) de l’expression (v) dont la valeur est v.
Concaténation
Il est possible de coller un n-uplet et un p-uplet pour obtenir un (n + p)-uplet. On parle
de concaténation. L’opérateur correspondant en Python est l’opérateur +.
In [73]: (1,2) + (3,4,5)
t
Out[73]: (1,2,3,4,5)
gh
Ici, si on veut ajouter un seul élément à un n-uplet, il faut faire attention à bien considérer
un 1-uplet.
En effet, l’expression suivante produit une erreur :
In [74]: (1,2) + 3
i
pyr
Out[75]: (1,2,3)
Test d’appartenance
Il est possible de tester si une valeur appartient à un n-uplet à l’aide de l’opérateur in :
Co
In [76]: 3 in (1,2,3)
Out[76]: True
Out[77]: 2
In [78]: len( () )
Out[78]: 0
Livre_silo 30 août 2013 16:32 Page 77
es
3 – Expressions : types et opérations
77
l
rol
3.3.2 Chaînes de caractères : strings
Construction
Le type des chaînes de caractères, string en anglais et dans Python, est celui permettant de
Ey
représenter des textes. On considère dans un premier temps des textes élémentaires, ceux
composés d’une unique lettre ; on les appelle les caractères.
En Python, les caractères peuvent être n’importe quelle lettre de l’alphabet, mais aussi des
symboles, comme les signes de ponctuation :
In [79]: 'a'
Out[79]: 'a'
t
In [80]: ' ?'
gh
Out[80]: ' ?'
Une chaîne de caractères est une suite finie de caractères consécutifs, qu’on note entre
apostrophes ou guillemets :
In [81]: 'Ceci est une chaine'
i
pyr
Comme les n-uplets, les chaînes de caractères regroupent plusieurs valeurs. D’ailleurs la
chaîne 'Bonjour' et le septuplet ('B','o','n','j','o','u','r') représentent le même mot.
La principale distinction entre ces deux types, c’est qu’une chaîne ne peut contenir que
des caractères. Ce faisant, on dispose d’opérations spécifiques pour son traitement. Les
opérations définies sur les n-uplets sont, en revanche, également disponibles sur les chaînes.
Accès à un caractère
Comme pour les n-uplets, on peut stocker une chaîne dans une variable :
In [83]: s = 'Bonjour'
Out[84]: 'n'
Livre_silo 30 août 2013 16:32 Page 78
es
Informatique pour tous
78
l
rol
In [85]: s[0] = 'A'
Concaténation
Ey
Comme pour les n-uplets, on concatène deux chaînes à l’aide de l’opérateur + :
In [86]: 'Bonjour '+ 'lecteur !'
Longueur
Comme pour les n-uplets, on utilise len pour obtenir la longueur d’une chaîne :
t
In [87]: len('Bonjour')
gh
Out[87]: 7
Sous-chaînes
Un ensemble de caractères consécutifs à l’intérieur d’une chaîne s’appelle une sous-chaîne.
Ainsi, 'lecteur' ou 'jour lec' sont des sous-chaînes de 'Bonjour lecteur !'. Pour extraire
i
pyr
In [89]: s[0:7]
Out[89]: 'Bonjour'
Co
In [90]: s[8:15]
Out[90]: 'lecteur'
Out[91]: ''
Test d’appartenance
Comme pour les n-uplets, l’opérateur in sert à tester l’appartenance d’un caractère à une
chaîne.
In [92]: 'o' in 'Bonjour'
Out[92]: True
Livre_silo 30 août 2013 16:32 Page 79
es
3 – Expressions : types et opérations
79
l
rol
Il est à noter qu’il est également possible de tester la présence d’une sous-chaîne dans une
chaîne avec la même construction :
In [93]: 'lecteur' in 'Bonjour lecteur !'
Out[93]: True
Ey
In [94]: 'Bjr' in 'Bonjour lecteur !'
Out[94]: False
Out[95]: '1.2'
Il est possible de reconvertir une telle chaîne vers une valeur d’un type simple :
In [96]: int('123')
i
Out[96]: 123
pyr
In [97]: float('1.2')
Out[97]: 1.2
In [98]: bool('True')
Out[98]: True
Les listes seront étudiées dans le chapitre 6. On se contente ici d’une présentation suc-
cincte : une liste est un n-uplet dont on peut changer la valeur des composantes.
Pour construire une liste, on remplace les parenthèses par des crochets :
In [99]: [1, 2, 3]
Out[99]: [1, 2, 3]
In [101]: L[1]
Out[101]: 2
In [102]: L + [3, 4]
Out[102]: [1, 2, 3, 4]
Livre_silo 30 août 2013 16:32 Page 80
es
Informatique pour tous
80
l
rol
Ici cependant, aucune erreur n’apparaît si on change la valeur d’un élément :
In [103]: L[0] = 42
Ey
In [104]: L
Out[104]: [42, 2]
Exercice 3.15 Quel est le type adapté pour représenter les données suivantes :
pyr
3.3.4 Conversions
Il est possible de convertir des n-uplets en listes et réciproquement, comme pour les types
simples :
In [105]: list( (1, 2, 3) )
Out[105]: [1, 2, 3]
Out[106]: (1, 2)
Livre_silo 30 août 2013 16:32 Page 81
es
3 – Expressions : types et opérations
81
l
rol
On peut également éclater une chaîne et la convertir en la liste de ses caractères :
In [107]: list( 'Bonjour')
Ey
inverse n’est pas possible avec une conversion :
In [108]: str( [ 'M', 'o', 'i'] )
pyr
Exercice 3.16 Écrire une expression qui indique si les 6 voyelles de l’alphabet sont présentes dans une
chaîne de caractères s.
Exercice 3.17 Écrire une expression qui vérifie si la chaîne de caractères s commence par une majuscule
Co
Livre_silo 30 août 2013 16:32 Page 82
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 83
l es
rol
4
t Ey Instructions :
gh
langage minimal
de l’algorithmique
i
pyr
Livre_silo 30 août 2013 16:32 Page 84
es
Informatique pour tous
84
l
rol
4.1 Instructions
4.1.1 Notion d’algorithme
Un algorithme est une procédure permettant de résoudre un problème, écrite de façon
Ey
suffisamment détaillée pour être suivie sans posséder de compétence particulière ni même
comprendre le problème que l’on est en train de résoudre.
On compare souvent les algorithmes à des recettes de cuisine. Cette comparaison est cor-
recte au sens où il n’est pas nécessaire de comprendre pourquoi le four doit être à 180°C
et pas à 250°C pour réussir la recette ; elle atteint ses limites quand la recette demande
des gestes techniques pour lesquels une expérience en cuisine est nécessaire. De la même
t
façon, une notice de montage peut constituer un algorithme permettant de monter un
meuble sans savoir comment son assemblage a été conçu, à condition que tous les outils
gh
nécessaires soient fournis avec la notice.
Un algorithme a cette autre particularité qu’il permet en réalité de résoudre une classe
de problèmes similaires, et non pas un problème unique. Ainsi, un algorithme d’addition
comme celui que l’on apprend à l’école primaire sert à calculer la somme de n’importe quels
i
nombres décimaux, et on voit mal quelle utilité il aurait s’il ne calculait la somme que de
deux nombres fixés une fois pour toutes.
pyr
Pour faire fonctionner un algorithme, il faut donc lui fournir des données précisant l’ins-
tance du problème qu’il devra traiter. En retour, l’algorithme construira un résultat répon-
dant à cette instance du problème.
Les algorithmes ont existé bien avant les ordinateurs, pour réaliser des tâches purement
matérielles comme pour résoudre des problèmes très calculatoires. L’exécution d’un algo-
rithme étant complètement déterminée par celui-ci, la difficulté que posait leur utilisation
à la main résidait dans le volume de calculs parfois nécessaires et les inévitables erreurs hu-
maines introduites dans le procédé. Avec l’apparition des premiers ordinateurs, il devenait
possible de faire exécuter un algorithme de façon beaucoup plus rapide et plus sûre que
jamais.
Le prix à payer est de devoir traduire cet algorithme sous une forme non ambiguë et lisible
par la machine. Un programme est la traduction d’un algorithme dans un langage particu-
lier, à la fois interprétable par la machine et compréhensible par l’homme. Il est constitué
d’un assemblage d’instructions, regroupées dans un fichier texte appelé le code source du
programme.
L’exécution du programme commence à la première instruction, puis en exécute d’autres en
suivant des règles précises. Le parcours des instructions au cours de l’exécution est appelé
le flot d’exécution.
Livre_silo 30 août 2013 16:32 Page 85
es
4 – Instructions : langage minimal de l’algorithmique
85
l
rol
4.1.3 Langage minimal de l’algorithmique
Dans le chapitre 3, on a vu que l’ensemble des variables définies à un instant donné de
l’exécution d’un programme constituait un état de l’exécution du programme. On a égale-
ment vu qu’il existait une instruction servant à déclarer une variable ou à changer la valeur
Ey
d’une variable existante.
De manière plus générale, on appelle instruction un ordre de modification de l’état courant
de l’exécution d’un programme. On sépare les instructions en deux grandes familles :
• d’une part les instructions simples qui manipulent directement l’état courant ;
• d’autre part les instructions composées qui assemblent d’autres instructions et modifient
le flot d’exécution en fonction de l’état courant.
t
Les instructions simples sont la déclaration et l’affectation, regroupées lors de l’étape d’ini-
gh
tialisation d’une variable en Python. Les instructions composées sont au nombre de trois :
• la séquence, qui exécute deux instructions l’une à la suite de l’autre ;
• le test, ou instruction conditionnelle, qui sert à n’exécuter une instruction que dans cer-
tains états ;
• la boucle, qui exécute plusieurs fois la même instruction dans un programme.
i
Il est remarquable que ces cinq instructions, à elles seules, suffisent à exprimer tous les
pyr
algorithmes imaginables. Bien sûr, une telle affirmation demande à être précisée, surtout
dans la mesure où on n’a pas donné de définition formelle de ce qu’était un algorithme.
C’est là l’objet de la thèse de Church-Turing, qui affirme que tout procédé de calcul pouvant
être décrit de façon systématique peut l’être avec ces cinq instructions.
4.1.4 Entrées/sorties
Co
Bonjour
In [2]: b=42
Livre_silo 30 août 2013 16:32 Page 86
es
Informatique pour tous
86
l
rol
In [3]: print(b+3)
45
Ey
Par défaut, print écrit les différentes expressions qui lui sont données en arguments sur la
même ligne, séparées par des espaces, puis revient à la ligne. On peut les séparer par une
autre chaîne de caractères en ajoutant un argument de la forme sep='...', et modifier la fin
de ligne par un argument de la forme end='...'. Ainsi, l’instruction suivante ne revient pas
à la ligne (la chaîne end est vide).
In [5]: print(1, 2, 3, sep='*', end='')
t
1*2*3
gh
EN PRATIQUE Dans les versions Python 2.x
L’instruction print s’utilise de la même façon, sauf qu’on ne met pas de parenthèses autour
de ce qu’on veut afficher. Mettre des parenthèses inutiles dans les instructions print d’un
programme Python 2.x ne produira pas d’erreur, mais l’affichage obtenu ne sera pas tout
à fait celui voulu.
i
pyr
x
Si on part de l’état 1. , après exécution de la première instruction on passe dans l’état
x x
2. , puis après la seconde dans l’état 4. .
Livre_silo 30 août 2013 16:32 Page 87
es
4 – Instructions : langage minimal de l’algorithmique
87
l
rol
L’importance de l’ordre dans lequel sont exécutées les instructions se confirme : si on in-
x
verse l’ordre des deux instructions, ce programme produit l’état 3. .
x = x * 2
Ey
x = x + 1
• b = b - 1
• c * 2
pyr
• print(a + b)
• x = int(input())
• a == 3
• a == b or c == 1
Exercice 4.2 Décrire l’évolution de l’état au cours de l’exécution du programme ci-dessous, en partant
b
de l’état 5. .
Co
a = 2
a = b - 7
b = b + 1
c = a - b
a = (a-1) / (a+1)
1 Décrire l’évolution de l’état au cours de l’exécution de ce programme pour différentes valeurs initiales
de a et b.
2 Formuler une conjecture sur les valeurs finales de a et de b en fonction de leurs valeurs initiales.
3 Démontrer cette conjecture dans le cas où les valeurs initiales de a et b sont des entiers relatifs.
4 Que penser de cette conjecture dans le cas où a et b contiennent initialement des nombres en virgule
flottante ? Et si on remplace la dernière ligne par b = b / 2 ?
Livre_silo 30 août 2013 16:32 Page 88
es
Informatique pour tous
88
l
rol
Exercice 4.4 * Démontrer que la séquence est une opération associative, autrement dit qu’à partir d’un
même état e, les instructions « (i1 suivie de i2 ) suivie de i3 » et « i1 suivie de (i2 suivie de i3 ) » produisent
le même état e′ .
En quoi cette propriété légitime-t-elle la notion de bloc d’instructions ?
Ey
4.2 Instructions conditionnelles
4.2.1 Test simple
Une instruction conditionnelle n’est exécutée que si une condition donnée est vérifiée par
l’état courant.
t
Pour traduire cela, on utilise l’instruction if, qui a en Python la syntaxe suivante :
gh
if condition:
bloc_d_instructions
Le bloc d’instructions est exécuté uniquement si condition est vérifiée. Dans l’exemple qui
suit, on ajoute 1 à la variable x seulement si, dans l’état courant, elle a une valeur impaire ;
si x a une valeur paire, l’instruction d’ajout est ignorée :
i
if x % 2 == 1:
pyr
x = x + 1
Indenter signifie ajouter un même nombre d’espaces devant chacune des lignes définissant
les instructions. Alors que dans certains langages cette pratique n’est qu’une recommanda-
tion, elle est une obligation dans Python.
Pour s’y conformer aisément, on peut se fixer pour convention qu’une indentation est
constituée de quatre espaces. Le niveau d’indentation d’une ligne correspond alors au
nombre d’espaces en début de ligne divisé par quatre.
# cette ligne a un niveau d'indentation de 0
# cette ligne a un niveau d'indentation de 1
# cette ligne a un niveau d'indentation de 2
Au sein d’un bloc, le niveau d’indentation doit être le même. Le code suivant est valide et
la ligne y = y // x ne s’exécute que si x est non nul :
if x != 0:
y = 3
y = y // x
Livre_silo 30 août 2013 16:32 Page 89
es
4 – Instructions : langage minimal de l’algorithmique
89
l
rol
mais celui-ci ne l’est pas :
if x != 0:
y = 3
y = y // x
Lorsque l’on tente de le faire exécuter par Python, on obtient l’erreur suivante :
Ey
File ``fichier.py'', line 3
y = y // x
^
IndentationError: unexpected indent
La première instruction qui suit une instruction conditionnelle et qui est placée au même
niveau d’indentation que l’instruction if marque la fin du bloc. En effet, seules les instruc-
tions indentées font partie du bloc.
t
Ici, la dernière instruction s’exécute toujours après le if :
gh
if x != 0:
y = 3
y = y // x
En cas de if au sein d’un if, le second bloc doit avoir un niveau d’indentation encore
supérieur. Par exemple :
i
if x != 0:
pyr
y = x * x
if y % 2 == 0:
y = y + 1
x = x + y
Là, l’instruction y = y + 1 n’est exécutée que si la seconde condition est vérifiée en plus de
la première.
Exercice 4.5 Quel est le résultat de l’exécution des instructions suivantes dans l’état
Co
a b c
0. 2. 1. ?
if a == 0:
b = 4
else:
c = 5
b = 1
et
if a == 0:
b = 4
else:
c = 5
b = 1
Livre_silo 30 août 2013 16:32 Page 90
es
Informatique pour tous
90
l
rol
4.2.3 Test avec alternative
On a vu au chapitre 3 qu’il était possible d’obtenir la négation d’un élément du type bool à
l’aide de l’opérateur not. On pourrait penser que cela permet d’exécuter certaines instruc-
tions lorsqu’une condition est vérifiée et d’autres quand elle ne l’est pas, avec deux tests.
Ey
Par exemple, si l’on voulait ajouter 1 à x quand sa valeur est impaire et la diviser par 2 sinon,
on pourrait écrire :
if x % 2 == 1:
x = x + 1
if not(x % 2 == 1):
x = x // 2
Cependant, cet extrait de programme pose de multiples problèmes. On peut déjà remar-
t
quer qu’il est redondant, puisque le programmeur doit écrire deux fois la même condition.
gh
Il est également inefficace : cette condition devra être évaluée deux fois, ce qui ne pose pas
de problème dans l’exemple donné, mais peut prendre un temps de calcul non négligeable
dans d’autres cas.
Enfin et surtout, l’action de ce programme sur l’état n’est pas celui que l’on voulait. En effet,
dans le cas où la valeur de x est impaire, la première instruction conditionnelle ajoute 1 à x.
i
Dans le nouvel état obtenu, x devient pair. La seconde instruction conditionnelle s’exécute
pyr
dans ce nouvel état et la condition not(x % 2 == 1) s’évalue à True. Par conséquent, pour une
valeur initiale impaire de x, le contenu de x est remplacé non pas par x+1 mais par (x+1)//2.
Pour ces trois raisons, on enrichit la syntaxe des tests pour proposer une alternative :
if condition:
bloc_d_instructions_si_la_condition_est_vérifiée
else:
bloc_d_instructions_si_la_condition_n_est_pas_vérifiée
Co
Livre_silo 30 août 2013 16:32 Page 91
es
4 – Instructions : langage minimal de l’algorithmique
91
l
rol
Deux bonnes habitudes sont à prendre très tôt, qui sont déterminantes pour la lisibi-
lité d’un programme :
• choisir des noms de variables parlants ;
• ajouter des commentaires dans les programmes, autrement dit des lignes écrites en
Ey
langue naturelle que la machine ne cherche pas à interpréter comme des instruc-
tions et qui expliquent le rôle des différentes parties du programme.
Pour préciser qu’une ligne est un commentaire, on la fait précéder d’un symbole par-
ticulier. En Python, il s’agit d’un dièse #. Si l’on veut écrire un commentaire sur plu-
sieurs lignes, il faut faire précéder chacune d’entre elles de ce symbole ou entourer
le commentaire de trois guillemets doubles """. Ces commentaires doivent donner
t
des informations supplémentaires sur le sens du programme. Inutile de commenter
en disant par exemple « ceci est une boucle » ; mieux vaut expliquer le rôle de cette
gh
boucle.
Exercice 4.6 Le programme ci-dessous affiche l’équation réduite de la droite d’équation cartésienne
ax + by + c = 0. En proposer une version plus lisible et documentée.
if b == 0:
i
if a != 0:
print("x =", -c/a)
pyr
else:
m = -a/b
p = -c/b
print("y =", m, "x +", p)
Enfin, il peut être utile d’indiquer rapidement à quoi correspondent chacun des cas testés. On obtient le
programme suivant.
# a x + b y + c = 0 est l'équation cartésienne d'une droite
if b == 0:
# droite parallèle à l'axe des ordonnées
if a != 0:
# sinon ce n'est pas une droite
print("x =", -c/a)
else:
# cas général
pente = -a/b
coefficient_directeur = -c/b
print("y =", pente, "x +", coefficient_directeur)
Exercice 4.7 Que penser de ce qu’affiche le programme précédent dans les cas où la pente vaut 0 ou 1
et dans le cas où le coefficient directeur vaut 0 ?
Modifier ce programme pour qu’il produise un affichage plus agréable.
Livre_silo 30 août 2013 16:32 Page 92
es
Informatique pour tous
92
l
rol
4.2.4 Tests imbriqués
Comme on a pu le voir dans la section sur l’indentation, il est possible de réaliser une
instruction conditionnelle dans une instruction conditionnelle. On parle alors de tests im-
briqués.
Ey
Lorsque les tests imbriqués ne servent qu’à séparer une situation en plus de deux cas de
figure, l’indentation obligatoire en Python peut rendre difficile la compréhension du pro-
gramme.
Par exemple, le programme suivant exécute trois instructions différentes suivant la valeur
de x % 3 :
if x % 3 == 0:
t
x = x + 1
else:
gh
if x % 3 == 1:
x = x - 1
else:
x = 2 * x
Il n’y pas de raison pour que l’instruction x = x - 1 soit plus imbriquée que l’instruction
x = x + 1. En effet, elle correspond juste au cas où x % 3 vaut 1. Il est possible de mettre
i
toutes ces instructions au même niveau en contractant le else et le if qui le suit : on utilise
pyr
Livre_silo 30 août 2013 16:32 Page 93
es
4 – Instructions : langage minimal de l’algorithmique
93
l
rol
5 Chacun des cas construits peut être lui-même écrit à l’aide d’une instruction condi-
tionnelle, ce qui permet de structurer l’écriture du programme.
6 Ne pas oublier de revenir au même niveau d’indentation que l’instruction if pour
la première instruction qui suit l’instruction conditionnelle.
Ey
Exercice 4.8 Écrire un programme qui détermine si un automobiliste est en excès de vitesse, connaissant
sa vitesse et le type de voie sur lequel il circule (donné sous forme d’une chaîne de caractères).
Les cas à distinguer pour le type de voie sont par exemple 'agglomération', 'route' et 'autoroute' ; il
serait bien entendu possible d’en prévoir d’autres. Grâce au mot-clé else, il n’est pas nécessaire de citer
explicitement le dernier cas, mais on peut mettre un commentaire pour expliciter à quoi il correspond.
Ensuite, dans chacun des cas, il faut distinguer si l’automobiliste dépasse la vitesse limite autorisée ou
t
non. On peut donc écrire le programme suivant.
if voie == 'agglomération':
gh
if vitesse > 50:
exces = True
else:
exces = False
elif voie == 'route':
if vitesse > 90:
i
exces = True
else:
pyr
exces = False
else: # voie == 'autoroute'
if vitesse > 130:
exces = True
else:
exces = False
On remarque cependant que l’instruction conditionnelle interne est toujours de la même forme et on aura
donc ici plutôt intérêt à utiliser une variable pour ne pas avoir à la répéter.
Co
if voie == 'agglomération':
limite = 50
elif voie == 'route':
limite = 90
else: # voie == 'autoroute'
limite = 130
La seconde instruction conditionnelle n’est ici plus imbriquée dans la première, elle doit donc être placée
au même niveau d’indentation.
Enfin, on peut remarquer que, puisqu’on ne cherche qu’à mettre une valeur booléenne dans
la variable exces, la dernière instruction conditionnelle pourrait être remplacée par l’affectation
exces = vitesse > limite.
Livre_silo 30 août 2013 16:32 Page 94
es
Informatique pour tous
94
l
rol
SAVOIR-FAIRE Comprendre un algorithme et expliquer ce qu’il fait
On identifie le rôle de chacune des variables utilisées. Si nécessaire, on peut dérouler à
la main une exécution du programme en notant l’état au fur et à mesure pour visualiser
Ey
l’évolution des variables.
Les contenus des variables a, b et c ne sont pas modifiés dans ce programme : il s’agit de données à partir
desquelles on calcule une valeur, ici dans la variable m.
Dérouler ensuite le programme pour quelques valeurs de a, b et c permet rapidement de conjecturer qu’à
i
la fin de l’exécution, m contient le maximum des trois valeurs. Il serait judicieux d’appeler plutôt cette
variable max pour rendre son rôle évident et éviter par exemple la confusion avec un minimum.
pyr
Exercice 4.10 Le programme suivant détermine si un individu est en surpoids en calculant son indice de
masse corporelle.
masse = float(input("Quelle est votre masse en kg ?"))
taille = float(input("Quelle est votre taille en m ?"))
imc = masse / taille**2
if imc > 25:
print("Vous etes en surpoids.")
Adapter ce programme pour qu’il détermine également si l’individu est en sous-poids, ce qui correspond
à un indice de masse corporelle inférieur à 18.
Livre_silo 30 août 2013 16:32 Page 95
es
4 – Instructions : langage minimal de l’algorithmique
95
l
rol
Il n’est pas nécessaire ici de connaître la définition de l’indice de masse corporelle pour comprendre le
déroulement du programme ni pour faire la modification voulue. En effet, on va réutiliser le calcul effectué
à la troisième ligne sans avoir besoin de le modifier. Ensuite, on peut s’inspirer des deux dernières lignes
pour écrire le cas du sous-poids, qui est très similaire à celui du surpoids.
Il suffit donc d’ajouter les deux lignes suivantes à la fin du programme :
Ey
if imc < 18:
print("Vous etes en sous-poids.")
• les cas limites soient essayés : nombres nuls ou négatifs, liste vide, etc.
pyr
Exercice 4.11 Proposer des jeux de tests pour le programme de l’exercice 4.9.
Comme on calcule ici le maximum des trois nombres a, b et c, il faut au moins tester tous
les ordres possibles pour ceux-ci. Cela donne déjà 6 cas distincts pour le triplet (a,b,c) :
{(1, 2, 3); (1, 3, 2); (2, 1, 3); (2, 3, 1); (3, 1, 2); (3, 2, 1)}.
De plus, il pourrait être intéressant de vérifier ce qui se passe dans des cas particuliers comme celui où a,
b et c sont égaux.
Co
Exercice 4.12 Modifier le programme de l’exercice 4.9 pour qu’il détermine le minimum des trois valeurs
a, b et c.
Exercice 4.13
1 Écrire un programme qui calcule la négation d’une variable booléenne sans utiliser l’opérateur not.
2 Écrire un programme qui calcule le et logique de deux variables booléennes sans utiliser l’opérateur and.
3 Écrire un programme qui calcule le ou logique de deux variables booléennes sans utiliser l’opérateur or.
Exercice 4.14 La fonction random.random() renvoie un nombre à virgule flottante pseudo-aléatoire com-
pris entre 0 inclus et 1 exclu. Pour utiliser cette fonction, il suffit d’exécuter l’instruction import random
dans l’interpréteur interactif, ou de la placer en début de programme.
À l’aide de random.random(), écrire un programme qui simule la loi uniforme sur l’intervalle [a; b], où a
et b sont deux réels donnés.
Exercice 4.15 Toujours à l’aide de la fonction random.random(), écrire un programme qui choisit la valeur
booléenne True ou False de façon équiprobable.
Exercice 4.16 Toujours à l’aide de la fonction random.random(), écrire un programme qui simule la loi
uniforme sur l’intervalle d’entiers Ja; bK, où a et b sont deux entiers donnés.
Livre_silo 30 août 2013 16:32 Page 96
es
Informatique pour tous
96
l
rol
4.3 Boucles conditionnelles
4.3.1 Nécessité des boucles
Maintenant que l’on dispose d’instructions conditionnelles, on va essayer de réaliser un
Ey
programme qui, à partir d’un état contenant une variable entière c de valeur n, définit une
nouvelle variable p de valeur 2n et place la valeur 0 dans c.
Si on suppose que n est compris entre 0 et 2, on pourrait écrire le programme suivant :
p = 1
if c != 0:
p = p * 2
c = c - 1
t
if c != 0:
p = p * 2
gh
c = c - 1
p = 1
pyr
if c != 0:
p = p * 2
c = c - 1
if c != 0:
p = p * 2
c = c - 1
if c != 0:
p = p * 2
Co
c = c - 1
On voit bien les limites d’une telle définition. Pour que cela soit valable avec des valeurs
quelconques de c, il faudrait écrire un programme contenant une infinité de lignes (litté-
ralement ici, car les entiers en Python sont non bornés).
On remarque pourtant que les instructions à effectuer après le test c != 0 sont toujours
les mêmes. On aimerait donc exécuter ces instructions tant que le test est vérifié. C’est
exactement ce que l’on appelle une boucle conditionnelle.
Le terme boucle fait référence au flot d’exécution. Celui-ci boucle autour des instructions
à exécuter tant que la condition est vérifiée.
Livre_silo 30 août 2013 16:32 Page 97
es
4 – Instructions : langage minimal de l’algorithmique
97
l
rol
Les contraintes d’indentation pour le bloc d’instructions sont les mêmes que pour une
instruction conditionnelle et la fin du bloc est marquée par le retour au niveau d’indentation
du while. Ce bloc d’instructions est appelé le corps de boucle et chaque passage dans ce
bloc est appelé une itération. Lorsqu’on effectue la première itération, on dit qu’on entre
dans la boucle ; on en sort lorsque l’on a fini d’exécuter la dernière itération.
Ey
Le programme calculant 2n deviendra alors :
p = 1
while c > 0:
p = p * 2
c = c - 1
c
3.
t
On va détailler l’exécution de ce programme à partir de l’état :
gh
c p
• Avant la première itération : 3. 1.
c p
• Après la première itération : 2. 2.
c p
i
pyr
c p
• Après le troisième itération : 0. 8.
Ce dernier état est l’état final, car la condition c > 0 n’est plus vérifiée et on sort donc de la
boucle.
Exercice 4.17 Exécuter ce programme à l’aide d’un débogueur et vérifier que l’évolution des variables
est bien celle prévue.
Co
Exercice 4.18 Modifier ce programme pour qu’il calcule kn où k et n sont deux entiers naturels quel-
conques pouvant être choisis par l’utilisateur.
Livre_silo 30 août 2013 16:32 Page 98
es
Informatique pour tous
98
l
rol
Exercice 4.19 Déterminer le rang du dernier terme strictement positif de la suite récurrente définie par
un+1 = 12 un − 3n, la valeur de u0 étant donnée dans la variable u_0.
On utilisera dans ce programme les variables n pour le rang courant et u pour la valeur de un .
1 Le calcul des termes un devra s’arrêter dès que un ⩽ 0 ; en prenant la négation de cette expression et
en la traduisant avec les variables du programme, on trouve la condition u > 0.
Ey
2 Le corps de la boucle consiste simplement à calculer la valeur de un+1 à partir de celle de un et à
mettre à jour le rang. Les deux lignes ci-après ne doivent pas être interchangées sinon la formule de
récurrence n’est pas correctement traduite.
u = 0.5*u - 3*n
n = n + 1
La variable u étant modifiée, la valeur de la condition u > 0 pourra changer au cours de l’exécution de
la boucle.
3 En amont de la boucle, il faut initialiser les variables u et n.
t
u = u_0
n = 0
gh
4 Enfin, à la sortie de la boucle, on a atteint le premier terme un négatif ou nul, mais il était demandé le
rang du dernier terme strictement positif. Il faut donc « revenir en arrière » d’un rang.
n = n - 1
while u > 0:
u = 0.5*u - 3*n
pyr
n = n + 1
n = n - 1
Dans le programme précédent, on remarque que la variable c joue le rôle d’un compteur.
Au départ, c contient le nombre d’itérations à effectuer. Après chaque itération, on enlève
un à ce nombre. La condition d’arrêt de la boucle teste si les n itérations ont été faites.
Mathématiquement, il est garanti que l’on sorte de la boucle car :
• la valeur de c est un entier strictement positif ;
• elle décroît strictement après chaque itération.
Comme il n’existe pas de suite infinie strictement décroissante d’entiers naturels, il ne peut
y avoir qu’un nombre fini d’itérations.
Livre_silo 30 août 2013 16:32 Page 99
es
4 – Instructions : langage minimal de l’algorithmique
99
l
rol
Pour illustrer cela, on considère maintenant un programme réalisant l’algorithme usuel de
division euclidienne pour des entiers naturels. Ici, l’état initial contient deux variables n et d
et, à l’issue de l’exécution, on souhaite que les deux variables q et r contiennent le quotient
et le reste dans la division euclidienne de la valeur de n par la valeur de d.
q = 0
Ey
r = n
while r >= d:
q = q + 1
r = r - d
n d
On va détailler l’exécution de ce programme à partir de l’état .
17 4. . On omet ces
deux variables, qui ne sont pas modifiées.
t
q r
gh
• Avant la première itération : 0. .
17
q r
• Après la première itération : 1. .
13
q r
• Après la deuxième itération : 2. 9.
i
q r
pyr
Ici, si la valeur de d est un entier strictement positif, la variable r reste positive tout au long
de l’algorithme et, après chaque itération, elle diminue de la valeur de d, donc elle décroît
Co
Livre_silo 30 août 2013 16:32 Page 100
es
Informatique pour tous
100
l
rol
p
Partant de l’état initial 5. , on obtient successivement les états suivants :
c p
1 0. 5.
Ey
c p
2 1. 3.
c p
3 0. 4.
c p
4 1. 2.
t
c p
5 0. 3.
gh
c p
6 1. 1.
c p
7 0. 2.
i
c p
1. 0.
pyr
8
On note pi et ci les valeurs des variables p et c après l’itération i.
On considère alors l’expression 2pi +3ci . Comme pi et ci sont toujours des entiers naturels,
cette expression est bien un entier naturel. On étudie l’effet de la i + 1-ème itération sur
l’état.
• Si ci = 0 alors pi+1 = pi − 2 et ci+1 = 1, d’où
Co
Dans tous les cas, la quantité 2pi + 3ci diminue strictement à chaque itération et ainsi la
boucle se termine.
Livre_silo 30 août 2013 16:32 Page 101
es
4 – Instructions : langage minimal de l’algorithmique
101
l
rol
SAVOIR-FAIRE Démontrer qu’une boucle se termine effectivement
On identifie un variant, autrement dit une expression (c’est souvent le simple contenu
d’une variable) :
Ey
• qui est un entier positif tout au long de la boucle,
• et qui diminue strictement après chaque itération.
On peut alors en conclure que la boucle se termine.
vérifiée tout au long de l’exécution d’une boucle. Cette démarche est à rapprocher du rai-
sonnement par récurrence : en ne s’intéressant qu’aux valeurs initiales des variables et à leur
pyr
évolution au cours d’une seule itération, on peut en déduire des propriétés valides quel que
soit le nombre d’itérations.
Exercice 4.21 Démontrer que le programme de calcul de 2n est correct, c’est-à-dire que lorsque l’exécu-
tion se termine, la variable p contient bien la valeur 2n où n ⩾ 0 est la valeur initiale de la variable c.
Pour raisonner, on note ci et pi les valeurs des variables c et p après l’exécution de la i-ème itération.
L’état au moment de l’entrée dans la boucle est
c0 = n p0 = 1
De plus, le corps de la boucle assure les relations suivantes pour toute itération i :
ci+1 = ci − 1 pi+1 = 2pi
On remarque alors que la propriété suivante est toujours vérifiée : pour toute itération i, on a pi = 2n−ci
et ci ⩾ 0.
Livre_silo 30 août 2013 16:32 Page 102
es
Informatique pour tous
102
l
rol
En effet :
• Elle est vérifiée pour i = 0 : p0 = 1 = 20 = 2n−c0 et c0 = n ⩾ 0.
• Si on a pi = 2n−ci et si l’on effectue une itération de plus, on a alors :
Ey
Puisqu’on a effectué l’itération, ci > 0 et donc ci+1 ⩾ 0.
Toutefois, lorsque la condition c > 0 n’est plus vérifiée, en sortie de boucle, on a ci = 0 et donc pi = 2n .
Toute la subtilité de ce raisonnement tient bien entendu dans le choix du bon invariant.
Exercice 4.22 * Démontrer la correction du programme de division euclidienne présenté page 99 à l’aide
d’un invariant de boucle.
p = p * 2
c = c - 1
pyr
Mis à part le fait que l’on teste c != 0 au lieu de c > 0, on peut penser que ce programme
fonctionne comme le précédent calculant 2n .
c c p
Par exemple, partant de l’état 3. , on obtient l’état final 0. 8. .
c c p
Co
Livre_silo 30 août 2013 16:32 Page 103
es
4 – Instructions : langage minimal de l’algorithmique
103
l
rol
SAVOIR-FAIRE Corriger un programme à l’aide d’un débogueur
1 On identifie les variables critiques, dont la valeur peut radicalement influencer
le comportement du programme. En particulier, les variables intervenant dans la
Ey
condition d’une instruction while sont particulièrement importantes puisqu’elles
conditionnent le nombre de fois où le corps de cette boucle est exécuté.
2 On identifie, pour chacune de ces variables, les endroits clés du programme qui la
concernent : par exemple, lorsqu’on lui affecte une valeur, en début ou en fin de
boucle.
3 On crée un point d’arrêt à ces endroits, puis on déroule l’exécution du programme
à l’aide du débogueur et on surveille les valeurs des variables critiques dans l’explo-
t
rateur de variables.
Voir le chapitre 1 page 30 pour l’utilisation concrète du débogueur de Spyder.
gh
Exercice 4.23 Utiliser un débogueur pour identifier l’erreur commise dans le programme ci-après. Le
comportement attendu est le suivant : on demande des nombres entiers au clavier tant que la suite des
nombres fournis est strictement croissante. La variable n compte le nombre de valeurs entrées au clavier.
precedent = int(input())
i
nouveau = int(input())
pyr
n = 2
while precedent < nouveau:
nouveau = int(input())
precedent = nouveau
n = n + 1
Un premier test du programme permet de se rendre compte que celui-ci s’arrête dès le troisième nombre
entré. Il n’est donc pas nécessaire de placer des points d’arrêt, on peut suivre l’évolution de precedent et
nouveau sur toute l’exécution du programme.
Co
On voit alors que le problème se pose aux deux premières lignes du corps de la boucle. L’instruction
nouveau = int(input()) écrase la dernière valeur entrée au clavier, puis l’instruction precedent = nouveau
rend forcément fausse la condition de la boucle.
Il faut ici intervertir ces deux lignes pour que la variable precedent mémorise correctement l’avant-dernière
valeur tapée au clavier.
Exercice 4.24 Le programme qui suit se termine-t-il pour toutes les valeurs initiales de c ?
p = 1
if c > 0:
while c != 0:
p = p * 2
c = c - 2
Livre_silo 30 août 2013 16:32 Page 104
es
Informatique pour tous
104
l
rol
Exercice 4.25 ** Pour accélérer le calcul de kn , on se propose d’exploiter les identités suivantes, qui
montrent comment calculer une puissance de k en remplaçant k par son carré et l’exposant par sa moitié :
{
k2n = (k2 )n
k2n+1 = k(k2 )n
Ey
L’algorithme est le suivant :
r = 1
while n > 0:
if n % 2 == 1:
r = r * k
k = k**2
n = n // 2
On a pu voir dans la partie précédente que les boucles conditionnelles étaient nécessaires
pyr
pour effectuer des calculs lorsqu’il n’est pas possible de borner le nombre d’étapes néces-
saires. En pratique, dans de nombreux cas, on connaît à l’avance le nombre d’itérations
qu’il faudra effectuer, ce qui rend inutile d’utiliser une boucle conditionnelle.
Le compteur de boucle est ici entièrement géré par la boucle for. Il n’est pas besoin de
l’incrémenter, ni de tester qu’il ne dépasse pas une certaine limite.
Il reste possible d’utiliser ce compteur au sein du corps de la boucle. Dans la boucle
for c in range(n), la variable c parcourt les entiers de 0 à n − 1. On remarque que le pa-
ramètre passé à range est donc la valeur avant laquelle on s’arrête. Comme souvent en
informatique, on numérote à partir de 0.
Ainsi, si l’on souhaite calculer la factorielle d’un entier n, définie par les relations :
0! = 1 (n + 1)! = (n + 1) × n!
Livre_silo 30 août 2013 16:32 Page 105
es
4 – Instructions : langage minimal de l’algorithmique
105
l
rol
on écrit le programme suivant :
p = 1
for c in range(n):
p = (c+1) * p
Exercice 4.26 Démontrer au moyen d’un invariant de boucle que le programme ci-dessus calcule bien n!
Ey
dans la variable p.
Exercice 4.27 Écrire un programme qui calcule la n-ième puissance itérée de k, autrement dit le nombre
i
..
.k
kk formé de n exemplaires de k.
pyr
Les exposants les plus hauts doivent être calculés en premier, sinon cette expression serait équivalente à
n−1 2
kk 22 = 22
4
= 216 = 65536 et non pas
(( ). )Par exemple, la quatrième puissance itérée de 2 est 2
2
2 22 2×2×2
= 2 8
= 2 = 256.
Livre_silo 30 août 2013 16:32 Page 106
es
Informatique pour tous
106
l
rol
SAVOIR-FAIRE Initialiser les variables
On a vu que, tant que l’on n’a pas affecté une valeur à une variable, celle-ci est absente
de l’état. Il faut donc s’assurer qu’à chaque fois qu’une variable est utilisée dans une
Ey
expression, elle est déjà présente dans l’état, sous peine d’une erreur. Pour cela :
1 On identifie la première ligne du programme où la variable est utilisée. Lorsque
l’algorithme comporte des instructions conditionnelles, il peut y avoir plusieurs
telles lignes pour la même variable, en fonction du résultat du test.
2 On vérifie que cette première ligne est toujours une affectation de cette variable.
Elle peut être rendue difficile à repérer parce qu’une instruction for la gère ou parce
qu’elle est combinée avec une saisie au clavier.
t
3 Enfin, si une variable n’est pas initialisée, il faut déterminer une valeur d’initialisa-
tion cohérente avec la suite du programme et ajouter l’instruction correspondante
gh
avant la première utilisation de la variable.
for i in range(n):
if i % 2 == 0:
pyr
carre = i ** 2
else:
carre = 0
total = total + carre
Ce programme semble prévu pour calculer la somme des carrés des entiers pairs strictement inférieurs
à n. Il comporte quatre variables :
• n est initialisée par une entrée au clavier en tout début de programme.
• i est un compteur de boucle et n’a donc pas besoin d’être initialisée.
Co
• carre apparaît pour la première fois dans l’instruction conditionnelle et elle y est correctement initialisée,
quel que soit le résultat du test i % 2 == 0.
• total n’apparaît que dans la dernière ligne de la boucle. Cette ligne est bien une affectation de total,
mais avec une expression qui dépend elle-même de total : il sera impossible d’évaluer cette expression
lors de la première itération. Il faut donc initialiser total avant d’entrer dans la boucle, avec la valeur 0
pour ne pas perturber la somme qui sera calculée par la suite.
Livre_silo 30 août 2013 16:32 Page 107
es
4 – Instructions : langage minimal de l’algorithmique
107
l
rol
Toute valeur de type composé sur laquelle ces deux opérations sont possibles est dite ité-
rable.
Savoir identifier le dernier élément à traiter est également utile pour assurer la terminaison
de la boucle, mais pas indispensable à proprement parler pour effectuer l’itération.
Ey
Pour construire une boucle qui parcourt un itérable, on utilise la syntaxe suivante :
for element in iterable:
corps_de_boucle
Par exemple, voici comment calculer la somme des éléments d’un n-uplet :
t = (1, 5, 9)
s = 0
for e in t:
t
s = s + e
gh
De même, voici comment compter les espaces dans une chaîne de caractères :
chaine = 'Les itérables sont vraiment épatants.'
esp = 0
for c in chaine:
if c == ' ':
esp += 1
i
pyr
Cette expression admet d’ailleurs deux formes plus générales, range(m,n) (le premier élé-
ment est m et le dernier est n) et range(m,n,p) (l’élément venant après i est i + p).
Si on évalue une expression range dans un interpréteur interactif, on obtient la réponse
In [6]: range(5)
Out[6]: range(0, 5)
car Python ne calcule pas explicitement cette séquence d’éléments. On peut, en revanche,
récupérer une liste par conversion explicite :
In [7]: list(range(5))
Out[7]: [0, 1, 2, 3, 4]
Livre_silo 30 août 2013 16:32 Page 108
es
Informatique pour tous
108
l
rol
EN PRATIQUE Dans les versions Python 2.x
Ce comportement est différent : range renvoie toujours une liste. L’avantage de ne pas
calculer cette plage de valeurs en Python 3.x est qu’on peut ainsi utiliser des valeurs comme
range(2**32) qui seraient très coûteuses à calculer.
Ey
SAVOIR-FAIRE Choisir entre une boucle for et une boucle while
Si on connaît à l’avance le nombre de répétitions à effectuer, ou plus généralement, si
on veut parcourir une valeur itérable, on choisit une boucle for.
t
À l’inverse, si la décision d’arrêter la boucle ne peut s’exprimer que par un test, c’est
la boucle while qu’il faut choisir.
gh
Exercice 4.29 Quelle boucle est adaptée à l’écriture de programmes traitant les problèmes suivants :
1 Calculer la valeur absolue d’un nombre donné.
2 Calculer la norme d’un vecteur dans un espace vectoriel de dimension finie (pour une des normes
usuelles).
i
pyr
4 Une boucle while : il n’est pas nécessaire de parcourir tous les diviseurs de n, on s’arrête au premier
rencontré, ce que l’on traduit dans la condition de la boucle.
5 En principe, une boucle while : on ne sait pas dans combien de mois aura lieu le prochain vendredi
13 puisque c’est justement ce qu’on cherche. Il est certainement possible de trouver une formule qui
répond directement à la question, mais cela demande un travail préliminaire à l’écriture du programme.
Exercice 4.30 Écrire les programmes proposés dans l’exercice précédent.
Livre_silo 30 août 2013 16:32 Page 109
es
4 – Instructions : langage minimal de l’algorithmique
109
l
rol
On considère par exemple un programme qui détermine si l’entier n est premier : il faut
a priori parcourir les entiers entre 2 et n2 et vérifier qu’aucun d’entre eux ne divise n.
√ √
√ s’arrêter à n car si n = p × q avec p > n alors q est un diviseur
En réalité, on peut
de n inférieur à n. On peut donc écrire le programme suivant :
Ey
import math
premier = True
for i in range(2, int(math.sqrt(n))+1):
if n % i == 0:
premier = False
La racine carrée est calculée à l’aide de la fonction math.sqrt. On n’oubliera pas l’instruction
import math pour y avoir accès.
t
Cependant, on s’aperçoit vite que lorsque n n’est pas premier, ce programme effectue des
calculs inutiles, puisqu’on pourrait arrêter la boucle dès qu’on a trouvé un diviseur. Une
gh
première solution consiste à réécrire la boucle for en une boucle while, ce qui est toujours
possible. On peut alors préciser dans la condition du while qu’il faut s’arrêter dès qu’un
diviseur est trouvé, autrement dit dès que premier prend la valeur False.
premier = True
i = 2
i
pyr
premier = False
i = i + 1
Une autre approche est de conserver la boucle for et d’en sortir au moyen de l’instruction
break dès qu’on rencontre un diviseur.
premier = True
for i in range(2, int(math.sqrt(n))+1):
if n % i == 0:
Co
premier = False
break
Ainsi, il n’est pas nécessaire de gérer le compteur de boucle à la main. Cette méthode reste à
utiliser avec parcimonie, car dans des programmes plus conséquents, elle peut compliquer
la compréhension des différents cas de sortie de la boucle.
Livre_silo 30 août 2013 16:32 Page 110
es
Informatique pour tous
110
l
rol
SAVOIR-FAIRE Construire des boucles imbriquées
Imbriquer des boucles est judicieux si on a identifié une procédure « doublement ré-
pétitive ».
Ey
1 Si l’une des instructions qui se répète dépend de l’autre, il faut qu’elle constitue la
boucle interne.
2 De même, si l’une des instructions doit se répéter sans « interférence » entre ses
itérations, elle doit être la boucle interne.
3 Ensuite, on s’interroge pour savoir si le compteur de la boucle interne doit ou non
dépendre de celui de la boucle externe.
4 On écrit le corps la boucle interne.
t
5 On écrit enfin le corps de la boucle externe, ce qui se réduit souvent à ajouter
quelques instructions avant ou après le corps de la boucle interne.
gh
6 On prendra garde aux trois niveaux d’indentation présents dans un tel programme.
Exercice 4.31 Écrire un programme qui affiche à l’écran un rectangle de n lignes sur p colonnes rempli
de caractères * sur le modèle suivant (pour n = 3 et p = 5) :
*****
i
*****
pyr
*****
Modifier ensuite ce programme pour qu’il affiche un triangle rectangle isocèle de taille n constitué de
caractères * sur le modèle suivant (pour n = 3) :
*
**
***
Il faut écrire n lignes identiques, mais nous ne connaissons pour l’instant aucun moyen de créer directe-
ment une chaîne de p caractères identiques. Nous allons donc utiliser des boucles imbriquées.
Co
Une fois que l’on est passé à la ligne, il est impossible de remonter. Il faut donc que la boucle interne
affiche une ligne complète et que la boucle externe reproduise n lignes.
Les lignes étant identiques, la boucle interne ne dépend pas de la boucle externe.
La boucle interne consiste simplement à afficher p étoiles sans revenir à la ligne.
for j in range(p):
print("*", end="")
La boucle externe se contente de répéter n fois la boucle externe et de revenir à la ligne à la fin de chaque
itération. Le programme complet est donné ci-après.
for i in range(n):
for j in range(p):
print("*", end="")
print()
Dans le second programme demandé, toutes les lignes ne sont pas identiques : la boucle interne dépend
donc de la boucle externe. Il est facile de voir qu’il faudra afficher i étoiles sur la i-ème ligne. Il suffit donc
de modifier le nombre d’itérations de la boucle interne, en prenant garde que i va parcourir les valeurs
de 0 à n − 1.
Livre_silo 30 août 2013 16:32 Page 111
es
4 – Instructions : langage minimal de l’algorithmique
111
l
rol
for i in range(n):
for j in range(i+1):
print("*", end="")
print()
Exercice 4.32 Écrire des programmes qui affichent les triangles de taille n constitués de caractères * sur
Ey
les modèles suivants (pour n = 3) :
1 *
**
***
2 ***
**
*
3 *
***
t
*****
gh
Exercice 4.33 * Écrire un programme de construction du tableau périodique des éléments au moyen de
boucles imbriquées.
4.5 Exercices
i
Exercice 4.34 Reprendre le programme de l’exercice 4.8 et l’adapter pour qu’en cas d’excès de vitesse,
pyr
il donne le montant de la contravention encourue. Celle-ci est de 68 e si l’excès commis est inférieur à
20 km/h, 135 e au delà.
Exercice 4.35 Écrire un programme qui détermine l’ordre de grandeur d’un nombre x non nul donné,
autrement dit l’entier relatif n tel que 10n ⩽ |x| < 10n+1 . On n’utilisera pas les fonctions logarithmes
du module math.
Exercice 4.36 Écrire un programme qui trouve le plus petit multiple commun à 2 entiers naturels m et n.
Exercice 4.37
Co
1 Écrire un programme qui reçoit comme donnée un quadruplet (jours, heures, minutes, secondes)
et vérifie que celui-ci respecte les conventions habituelles sur les durées (secondes entre 0 et 59, etc.).
2 Écrire un programme qui reçoit une durée exprimée en secondes et construit un quadruplet
(jours, heures, minutes, secondes) désignant la même durée et respectant les conventions habi-
tuelles sur les durées.
Exercice 4.38 *
1 Écrire un programme qui calcule la valeur d’un entier connaissant son écriture binaire fournie sous
forme d’une chaîne de caractères.
2 Écrire un programme qui construit dans une chaîne de caractères l’écriture binaire d’un entier naturel
donné.
3 Généraliser à une base quelconque. Pour des raisons pratiques, on pourra se limiter aux bases inférieures
ou égales à 36 ; voyez-vous pourquoi ?
Livre_silo 30 août 2013 16:32 Page 112
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 113
l es
rol
5
t Ey Fonctions
i gh
pyr
Dans ce chapitre, nous verrons qu’on peut définir une fonction pour isoler
une instruction qui revient plusieurs fois dans un programme. Une fonc-
tion est définie par :
• son nom ;
• ses arguments qui porteront les valeurs communiquées par le programme
principal à la fonction au moment de son appel ;
Co
Livre_silo 30 août 2013 16:32 Page 114
es
Informatique pour tous
114
l
rol
On souhaite écrire un programme qui affiche de la manière suivante les horaires de trois
trains pour Brest, Strasbourg et Toulouse :
Le train en direction de Brest partira à 9h
---------------------------------------------------------
Le train en direction de Strasbourg partira à 9h30
Ey
---------------------------------------------------------
Le train en direction de Toulouse partira à 9h45
---------------------------------------------------------
dupliquée autant de fois. Si par exemple on fait une faute d’orthographe à "partira", alors
pyr
Ainsi, pour chaque couple d’une destination d et d’un horaire h, on effectue les deux ins-
tructions suivantes :
print("Le train en direction de", d, "partira à", h)
print("---------------------------------------------------------")
Cela ne suffit pas nécessairement à éliminer toute redondance. En effet, on peut imaginer
avoir besoin d’afficher un départ de train à un autre endroit du programme. Il faudrait alors
copier les deux instructions, avec les valeurs de d et h pertinentes. Ce qu’il nous faut donc,
c’est un moyen d’isoler ces deux instructions. C’est exactement ce que permet de faire une
fonction.
Livre_silo 30 août 2013 16:32 Page 115
es
5 – Fonctions
115
l
rol
5.1 La notion de fonction
Une fonction est une suite d’instructions qui dépend de paramètres. Ainsi, dans l’exemple
précédent, on peut définir une fonction appelée annoncer_train et paramétrée par une des-
tination d et un horaire h de la façon suivante :
Ey
def annoncer_train(d, h):
print("Le train en direction de", d, "partira à", h)
print("---------------------------------------------------------")
Comme on le voit sur cet exemple, une fonction est introduite par le mot-clé def, suivi du
nom de la fonction, de ses paramètres entre parenthèses, de deux-points, puis d’un bloc
d’instructions. Ce dernier s’appelle le corps de la fonction. Il doit donc être indenté et la
fin de ce bloc marque la fin de la définition de fonction. Les variables d et h, qui figurent
t
comme paramètres dans la définition de la fonction, s’appellent les arguments formels.
gh
En utilisant la fonction annoncer_train, le programme se réduit alors aux trois instructions
suivantes :
annoncer_train("Brest", "9h")
annoncer_train("Strasbourg", "9h30")
annoncer_train("Toulouse", "9h45")
i
pyr
annoncer_train("Brest", "9h"), les expressions "Brest" et "9h", que l’on donne en arguments,
s’appellent les arguments effectifs de l’appel.
Organiser un programme à l’aide de fonctions évite les redondances. En outre, cela les
rend plus clairs et plus faciles à lire : pour comprendre le programme précédent, il n’est pas
nécessaire de savoir comment la fonction annoncer_train est programmée, il suffit de savoir
ce qu’elle fait. Enfin, cela permet d’organiser l’écriture du programme. On peut décider
Co
Livre_silo 30 août 2013 16:32 Page 116
es
Informatique pour tous
116
l
rol
def hypotenuse(x, y):
z = math.sqrt(x*x + y*y)
return z
√
On l’utilise, par exemple pour calculer 32 + 42 , de la manière suivante :
Ey
In [1]: hypotenuse(3.0, 4.0)
Out[1]: 5.0
Dans cette instruction, l’appel hypotenuse(3.0, 4.0) transmet les arguments effectifs 3.0
et 4.0 à la fonction, en lieu et place des arguments formels x et y. La fonction hypotenuse
calcule alors la valeur math.sqrt(x*x + y*y) et la renvoie. En Python, ce retour de valeur
est effectué par l’instruction return. C’est le processus inverse du passage d’arguments, qui
transmet des informations du programme principal vers le corps de la fonction. L’appel à
t
la fonction hypotenuse constitue alors une expression, dont la valeur est la valeur renvoyée,
à savoir 5.0. Celle-ci est affichée par l’instruction print.
gh
Si on oublie de faire précéder l’expression renvoyée par le mot-clé return, comme dans la
fonction hypotenuse suivante :
def hypotenuse(x, y):
math.sqrt(x*x + y*y)
i
celle-ci est toujours acceptée, mais elle ne renvoie pas de valeur. Ainsi, si on affiche le
pyr
Out[2]: None
on obtient, non pas la valeur attendue 5.0, mais une valeur particulière None. De manière
générale, l’absence d’instruction return dans une fonction se traduit par une instruction
implicite return None. C’était en particulier le cas pour la fonction annoncer_train précédente.
Co
Par opposition aux fonctions qui renvoient des valeurs, on pourra en général considérer
l’appel à une fonction sans return (ou qui renvoie None) comme une instruction. On verra
cependant dans la prochaine section que certaines fonctions sont à la fois des expressions
et des instructions.
Livre_silo 30 août 2013 16:32 Page 117
es
5 – Fonctions
117
l
rol
De manière générale, il est important dans un algorithme de distinguer le résultat calculé
des sorties affichées à l’écran. De même, il faut distinguer les données sur lesquelles on
travaille des entrées tapées au clavier.
Ey
Quand elle ne se trouve pas à la fin d’une fonction, mais au milieu, l’instruction return a
pour effet d’interrompre le déroulement de la fonction. Ainsi, on peut écrire :
def puissance(x, n):
if (x == 0):
return 0
r = 1
for i in range(n):
r = r * x
t
return r
gh
Si la valeur de x est nulle, l’instruction return 0 interrompra le déroulement de la fonction
et aucune des instructions suivantes (à partir de r = 1) ne sera exécutée.
Quand on conçoit une fonction, il est préférable de lui donner un nom explicite,
pyr
car elle est susceptible d’apparaître à de nombreux endroits dans le programme (On
n’imagine pas que les fonctions de la bibliothèque Python s’appellent f1, f2, etc.). En
revanche, les noms des arguments formels peuvent être courts, car leur portée, et donc
leur signification, est limitée au corps de la fonction.
Il convient par ailleurs de documenter convenablement les fonctions, en spécifiant
les hypothèses faites sur les arguments formels, leur relation avec le résultat renvoyé,
mais aussi les effets de la fonction (affichage, etc.) le cas échéant. Python propose un
Co
mécanisme pour associer une documentation à toute fonction, sous la forme d’une
chaîne de caractères placée au début du corps de la fonction. Ainsi, on peut écrire :
def puissance(x, n):
"""calcule x à la puissance n, en supposant x > 0 et n >= 0"""
r = 1
for i in range(n):
r = r * x
return r
Exercice 5.1 Écrire une fonction qui prend en arguments deux entiers x et y et qui renvoie -1 si x < y,
0 si x = y et 1 si x > y.
Exercice 5.2 Écrire une fonction qui prend en arguments deux entiers n ⩾ 0 et d > 0 et qui renvoie un
couple formé du quotient et du reste de la division euclidienne de n par d. Le quotient et le reste seront
calculés par soustractions successives.
Livre_silo 30 août 2013 16:32 Page 118
es
Informatique pour tous
118
l
rol
Le nom à donner à la fonction, à ses arguments et à ses variables sont pour ainsi dire donnés dans l’énoncé.
On n’oublie pas de mentionner les conditions d’utilisation dans la documentation.
def division_euclidienne(n, d):
"""calcule q et r tels que n = q * d + r et 0 <= r < d,
en supposant n >= 0 et d > 0
Ey
"""
r = n
q = 0
while r > d:
r = r - d
q = q + 1
return (q, r)
t
SAVOIR-FAIRE Utiliser un débogueur avec des fonctions
gh
On a vu jusqu’ici que, en mode débogueur, on pouvait exécuter un programme ins-
truction par instruction à l’aide de la commande Pas en avant arrow-step-over.png
(ou next). Lorsque
le programme comporte des fonctions, cette commande considère chaque appel de
fonction comme une instruction unique et passe donc directement à l’instruction sui-
vante. Ce n’est pas approprié si on veut voir ce qui se passe dans le corps de la fonction.
i
Pour entrer dans le corps d’une fonction au moment où elle est appelée, on clique sur
pyr
def g():
s = 0.
for j in range(10):
a = f(j)
s = s + 1. / (a-210)
return s
print(g())
Livre_silo 30 août 2013 16:32 Page 119
es
5 – Fonctions
119
l
rol
1 Le programme s’arrête sur l’erreur suivante :
ZeroDivisionError: 'float division by zero'
2 Il est clair que la division par zéro provient de la ligne s = s + 1. / (a-210) dans la fonction g ; en
revanche, la lecture seule du code ne nous permet pas de déterminer quand la variable a prend la
valeur 210.
Ey
3 Si l’on n’utilise que la commande Pas en avant, le débogueur n’entre pas dans l’appel à la fonction g
et on ne voit pas ce qui se passe.
À la ligne print(g()), il faut donc utiliser Pas vers l’intérieur. Ensuite, on peut effectuer Pas en avant
dans le corps de g jusqu’à déterminer que la division par zéro a lieu à l’itération j=6. Si par erreur on
continue à utiliser Pas vers l’intérieur, on peut se retrouver dans le corps de f. Pour ne pas avoir à
exécuter les 100 itérations à la main, on peut effectuer Pas vers l’extérieur.
Remarquons que placer un point d’arrêt à la ligne qui produit la division par zéro évite de dérouler
toutes les itérations dans g à la main.
t
POUR ALLER PLUS LOIN Un générateur pseudo-aléatoire
gh
Le lecteur qui aura eu la curiosité de dérouler une exécution de la fonction f dans l’exercice
corrigé ci-avant aura pu remarquer le caractère apparemment imprévisible des valeurs suc-
cessives prises par la variable x (apparemment seulement puisqu’il s’agit d’une suite pério-
dique). Ce sont des procédures telles que celle-ci qui permettent aux ordinateurs de simuler
le hasard. On parle de générateur pseudo-aléatoire.
i
pyr
Pendant l’appel, et après avoir exécuté y = 8, on se trouve dans un état augmenté d’une
variable locale y, c’est-à-dire :
x y
7. 8.
Livre_silo 30 août 2013 16:32 Page 120
es
Informatique pour tous
120
l
rol
En particulier, l’instruction suivante échoue en indiquant que la variable y n’est pas définie :
print(y)
On dit que la portée de la variable y est limitée au corps de la fonction f. Les variables
globales, elles, ont une portée qui s’étend généralement sur l’ensemble du programme.
Ey
Si on souhaite faire référence à une variable globale dans une fonction, par exemple pour
écrire une fonction qui réinitialise la variable globale x à 0, alors il ne faut pas écrire :
def reinitialise():
x = 0
En effet, cela ne fait qu’affecter une variable locale x à la fonction reinitialise. On peut
s’en convaincre en exécutant le code suivant :
t
reinitialise()
print(x)
gh
et en observant qu’il affiche toujours la valeur 7. Pour que la fonction reinitialise puisse
avoir accès à la variable globale x, il faut désigner cette dernière comme telle :
def reinitialise():
global x
x = 0
i
Ainsi, le programme :
pyr
reinitialise()
print(x)
elle est utilisée dans la fonction sans être affectée ou si elle est déclarée globale.
def f():
global a
a = a + 1
c = 2 * a
return a + b + c
Dans cette fonction, a est globale car elle est déclarée comme telle, b est globale car elle est
utilisée mais non affectée et c est locale car elle est affectée mais n’est pas déclarée globale.
Si une variable x est déclarée globale dans une fonction f mais pas dans une fonction g, et
si le nom de variable x est utilisé dans g, comme dans l’exemple suivant :
def f():
global x
x = 2
def g():
x = 3
Livre_silo 30 août 2013 16:32 Page 121
es
5 – Fonctions
121
l
rol
alors il faut considérer qu’on a deux variables x différentes : une globale et une locale dans g.
Dans la fonction g, le nom x désigne la variable locale et non la globale, qui du fait de son
homonymie, ne peut être utilisée. C’est le seul cas où une variable globale a une portée
qui ne couvre pas l’ensemble du programme. Ainsi, le programme suivant affiche 2 car
l’affectation x = 3 concerne la variable x locale à g et non la variable globale x :
Ey
x = 1
f()
g()
print(x)
Ainsi, un appel à une fonction peut modifier l’état du programme principal. C’est évidem-
ment possible aussi avec une fonction qui renvoie une valeur : un appel à une telle fonction
est donc à la fois une expression (puisqu’il prend une valeur) et une instruction (puisqu’il
t
modifie l’état).
gh
SAVOIR-FAIRE Du bon usage des variables globales
De façon générale, une bonne pratique consiste à utiliser les variables globales pour
représenter les constantes du problème. En pratique, on ne devrait pas recourir souvent
i
Comme pour les fonctions, il est préférable de donner aux variables globales des noms
pyr
longs et explicites, ce qui les distinguera de fait des variables locales qui portent ha-
bituellement des noms courts (comme les paramètres formels).
Exercice 5.4 Quelles sont les variables locales et globales de la fonction f? Qu’affiche le programme
suivant ?
def f():
Co
global a
a = a + 1
c = 2 * a
return a + b + c
a = 3
b = 4
c = 5
print(f())
print(a)
print(b)
print(c)
Livre_silo 30 août 2013 16:32 Page 122
es
Informatique pour tous
122
l
rol
suivant affiche le résultat 2 si les arguments de somme sont évalués de gauche à droite et 3
sinon :
n = 0
def g(x):
global n
Ey
n = n + 1
return x + n
def somme(x, y):
return x + y
print(somme(n, g(1)))
Il se trouve qu’en Python, l’ordre d’évaluation est toujours de la gauche vers la droite. Ce
n’est pas le cas dans d’autres langages de programmation, où il peut être au contraire de
la droite vers la gauche, voire non spécifié. Même en ne considérant que Python, rien ne
t
garantit que l’ordre d’évaluation ne changera pas dans de prochaines versions. Aussi est-il
gh
important de ne jamais écrire un programme dont le résultat en dépende. Dans l’exemple
précédent, on peut par exemple forcer g(1) à être évaluée en premier, en stockant sa valeur
dans une variable a :
a = g(1)
print(somme(n, a))
i
Exercice 5.5 Donner un exemple de programme montrant que l’évaluation des arguments de l’opéra-
teur + se fait également de gauche à droite.
pyr
a = 3
Co
b = 4
print(f(a)+b)
Proposer une adaptation de ce programme dans laquelle le résultat affiché ne dépend pas de l’ordre
d’évaluation.
Alors le programme :
a = 4
print(f(a))
Livre_silo 30 août 2013 16:32 Page 123
es
5 – Fonctions
123
l
rol
De manière plus surprenante, la variable a contient toujours la valeur 4 après l’appel à
print(f(a)). En effet, on part de l’état suivant :
a
4.
Ey
Immédiatement après l’appel à f(a), on se retrouve dans un état :
a x
4. 4.
De ce point de vue, il n’y a pas de différence entre un paramètre formel et une variable
pyr
locale : les deux ont une durée de vie limitée à l’appel de la fonction.
Ce mécanisme de transmission des arguments s’appelle le passage par valeur, car c’est seule-
ment la valeur de l’argument effectif qui est transmise à la fonction appelée, et non l’argu-
ment effectif lui-même.
On considère le programme suivant qui définit une fonction f appelée depuis une fonc-
tion g :
def f():
print('fonction 1')
def g():
f()
Sans surprise, le message 'fonction 1' s’affiche si on appelle g(). Rien n’empêche de redé-
finir ensuite la fonction f, par exemple pour qu’elle affiche plutôt 'fonction 2' :
def f():
print('fonction 2')
De manière plus surprenante, c’est maintenant le message 'fonction 2' qui est affiché
quand on appelle de nouveau g(), alors qu’il s’agit toujours de la même fonction g. On
appelle ce phénomène la liaison dynamique. Dit simplement, au moment de l’appel à une
fonction f, Python en considère la dernière définition.
D’autres langages de programmation proposent au contraire une liaison statique où, dans
l’exemple précédent, c’est toujours la fonction f initiale qui est appelée par la fonction g et
donc toujours 'fonction 1' qui est affiché.
Livre_silo 30 août 2013 16:32 Page 124
es
Informatique pour tous
124
l
rol
Exercice 5.7 Expliquer pourquoi il n’est pas possible d’écrire une fonction echange qui échange le contenu
des deux variables entières passées en arguments ?
Ey
5.2 Mécanismes avancés
5.2.1 Fonctions locales
Il arrive que l’utilisation d’une fonction soit limitée à la définition d’une autre fonction.
On va supposer, par exemple, qu’on veut écrire une fonction max3 calculant le maximum de
trois entiers. Pour cela, il est élégant de commencer par la définition d’une fonction max2
t
calculant le maximum de deux entiers, pour l’utiliser ensuite deux fois. Cependant, on ne
souhaite pas nécessairement rendre visible la fonction max2. On la définit donc localement
gh
à la fonction max3, de la manière suivante :
def max3(x, y, z):
def max2(u, v):
if u > v:
return u
i
else:
return v
pyr
def g(n):
return n + a
return g(y) + g(z)
Livre_silo 30 août 2013 16:32 Page 125
es
5 – Fonctions
125
l
rol
Pour calculer la somme des carrés des entiers de 1 à 10, on commence par définir une
fonction carre.
def carre(x):
return x*x
Ey
Puis, on la passe en argument à la fonction somme_fonction :
In [3]: somme_fonction(carre, 10)
Out[3]: 385
On peut même éviter de nommer la fonction carre, puisqu’elle est réduite à une simple
expression, en utilisant une fonction anonyme. Une telle fonction s’écrit lambda x: e, où e est
une expression pouvant comporter la variable x. Elle désigne la fonction x 7→ e(x). Ainsi
t
l’exemple précédent se réécrit-il plus simplement :
gh
In [4]: somme_fonction(lambda x: x*x, 10)
Out[4]: 385
De même qu’on peut passer une fonction en argument, on peut renvoyer une fonction
comme résultat. En particulier, il est possible d’écrire une fonction qui prend comme ar-
guments deux fonctions f et g et qui renvoie la composée f ◦ g :
i
pyr
return f(g(x))
return h
Out[6]: 256
Les fonctions de première classe servent à exprimer de façon élégante des algorithmes
génériques qu’on serait sinon obligé de réécrire pour chaque fonction. Des exemples d’ap-
plication en sont donnés dans les chapitres 8 et 9.
Exercice 5.8 * Écrire une fonction derive qui prend en argument une fonction f et un flottant ϵ > 0 et
renvoie la fonction :
f (x + ϵ) − f (x)
x 7→
ϵ
Vérifier que la fonction renvoyée par derive(carre, 0.001) est effectivement une approximation de x 7→
2x.
Livre_silo 30 août 2013 16:32 Page 126
es
Informatique pour tous
126
l
rol
5.2.3 Fonctions partielles
Les fonctions considérées jusqu’à présent sont des fonctions totales, c’est-à-dire qu’elles ren-
voient toujours un résultat, quelle que soit la valeur de leurs arguments. Il existe aussi des
fonctions dites partielles parce qu’elles ne renvoient pas un résultat pour toutes les valeurs
Ey
possibles des arguments. Par exemple, la fonction divise ci-après est partielle :
def divise(x, y):
return x // y
En effet, tout appel à divise avec un deuxième argument égal à 0 va interrompre l’exécution
de la fonction et provoquer l’affichage du message d’erreur suivant :
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
t
File "<stdin>", line 2, in divise
ZeroDivisionError: integer division or modulo by zero
gh
Pour définir plus proprement une fonction partielle, il est préférable de vérifier que les
valeurs passées en argument sont bien dans son domaine d’utilisation. On peut pour cela
vérifier une précondition à l’aide d’une instruction assert e. Cette instruction évalue l’ex-
pression booléenne e et provoque une erreur AssertionError si e est fausse. Ainsi, on peut
i
ajouter une instruction assert y != 0 à la fonction divise pour interrompre son exécution
si y est égal à 0 :
pyr
Le message d’erreur suivant est alors affiché dès que l’expression y != 0 est évaluée à False :
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Co
Afin d’afficher un message d’erreur plus informatif que AssertionError, on peut associer
une deuxième expression à assert. Cette seconde expression sera évaluée uniquement si la
première est fausse et sa valeur sera affichée pour compléter le message d’erreur. Ainsi, on
peut écrire :
def divise(x, y):
assert y != 0, 'division par 0 impossible'
return x // y
Le message suivant sera affiché chaque fois que l’expression y != 0 sera fausse :
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in divise
AssertionError: division par 0 impossible
Il est préférable de placer ces préconditions au début du corps de la fonction, afin de s’as-
surer qu’aucune instruction n’est exécutée si les arguments ne sont pas dans le domaine de
Livre_silo 30 août 2013 16:32 Page 127
es
5 – Fonctions
127
l
rol
la fonction. Par exemple, on considère la fonction suivante, qui incrémente une variable
globale y et ne s’assure que très tardivement de la valeur de son argument x :
def f(x):
global y
...
Ey
y = y + 1
...
assert x != 0
return 1 // x
Bien que chaque appel à f(0) provoque une erreur AssertionError, toutes les instructions
placées avant assert x != 0, en particulier l’affection y = y + 1, seront exécutées. Ceci peut
être une source d’erreur difficile à détecter dans un programme.
t
Une autre solution pour écrire une fonction partielle est de ne renvoyer aucun résultat
quand les arguments sont en dehors du domaine. Par exemple, on peut écrire la fonction
gh
divise en utilisant une instruction conditionnelle qui teste la valeur de y avant de diviser :
def divise(x, y):
if y != 0:
return x // y
Comme on l’a vu dans la section 5.1.1, la valeur None sera renvoyée par divise si la variable y
i
est égale à 0. Cette solution présente tout de même l’inconvénient de ne pas informer
pyr
fonctions écrites dans le langage et fournies avec lui sont regroupées dans ce qu’on appelle
la bibliothèque standard.
Python n’échappe pas à la règle et prétend même être un langage « piles incluses » pour
signifier que tout ce dont le programmeur peut avoir besoin est fourni avec le langage. Les
différentes fonctions sont organisées en modules thématiques variés : représentation exacte
des fractions, compression de fichiers, protocoles réseau….
Parmi les modules les plus utiles à l’élève de classes préparatoires — et, pour certains, déjà
mentionnés plus haut — on peut citer :
• math qui contient toutes les fonctions habituellement utilisées en analyse ;
• random qui calcule des nombres pseudo-aléatoires ;
• fractions qui sert à manipuler des nombres rationnels en valeur exacte ;
• numpy qui fournit des outils variés pour le calcul scientifique. Ce dernier ne fait pas partie
de la bibliothèque standard et doit donc être installé à part.
Livre_silo 30 août 2013 16:32 Page 128
es
Informatique pour tous
128
l
rol
Pour utiliser les fonctions d’un module, on commence par importer ce dernier une fois
pour toutes, en début de session interactive ou en début de programme. On prend ici
l’exemple du module math :
In [7]: import math
Ey
Pour toute la suite de la session (ou du programme), on peut alors utiliser les fonctions de
ce module en faisant précéder leur nom de celui du module :
In [8]: math.cos(14)
Out[8]: 0.1367372182078336
Cette notation permet au programmeur de définir par exemple sa propre fonction sqrt,
gh
sans risquer une homonymie avec celle fournie par le module. De même, plusieurs modules
peuvent proposer des fonctions ayant le même nom, mais qui seront différenciées par leurs
préfixes.
Une autre forme possible est de n’importer que la fonction dont on a besoin, par exemple
from math import sqrt. On utilise alors directement sqrt sans le préfixe math, au prix d’un
i
pyr
Enfin, les modules ne fournissent pas que des fonctions : certains proposent des constantes,
de nouveaux types de données, etc.
In [10]: math.pi
Out[10]: 3.141592653589793
Co
Livre_silo 30 août 2013 16:32 Page 129
es
5 – Fonctions
129
l
rol
Enfin, on veillera à consulter la documentation qui correspond à la version du langage
que l’on utilise (pour Python, c’est particulièrement important à cause de l’incompa-
tibilité entre les versions 2.x et 3.x).
Ey
5.2.5 Méthodes
Le langage Python appartient à la famille des langages dits orientés objet. Dans cet ouvrage,
cet aspect du langage n’est pas du tout abordé. Néanmoins, l’accès à certaines bibliothèques
oblige à introduire la notion de méthode liée à la programmation orientée objet.
t
Pour ce qui concerne cet ouvrage, une méthode n’est rien d’autre qu’une fonction associée à
une valeur, telle qu’un entier, une chaîne de caractères, une liste, etc. Une valeur à laquelle
gh
sont associées des méthodes est appelée un objet. Par exemple, on prend s une chaîne de
caractères définie par :
s = 'Ceci est une chaine'
pyr
In [11]: len(s)
Out[11]: 19
Out[12]: 19
Co
Tout comme une fonction, une méthode peut prendre des arguments. Ainsi, on compte
le nombre d’occurrences du caractère 'e' dans la chaîne s avec la méthode count de s :
In [13]: s.count('e')
Out[13]: 4
In [14]: ''.join(['a','b','c'])
Out[14]: 'abc'
In [15]: '-'.join(['d','e','f'])
Out[15]: 'd-e-f'
Livre_silo 30 août 2013 16:32 Page 130
es
Informatique pour tous
130
l
rol
Cette méthode fonctionne également avec une liste de chaînes pour argument :
In [16]: ' '.join(['Ceci', 'est', 'une', 'phrase'])
On réalise l’opération duale, c’est-à-dire découper une chaîne selon un séparateur donné,
Ey
à l’aide la méthode split :
In [17]: 'Ceci est une phrase'.split('')
D’autres méthodes seront présentées dans la suite de cet ouvrage, notamment pour mani-
puler les piles et effectuer des entrées/sorties dans des fichiers.
t
gh
5.3 La récursivité
Avertissement : les contenus abordés dans cette section sont au programme de seconde année uni-
quement. En particulier, certains exercices nécessitent d’avoir traité la complexité (section 6.1).
√
On considère la suite (un ) suivante, qui calcule une approximation de 3 :
i
pyr
u0 = 2
( )
1 3
un = un−1 +
2 un−1
On peut calculer cette suite à l’aide d’une fonction u, qui prend un argument n, et telle que
u(n) renvoie la valeur de un . En Python, on peut écrire la fonction u en suivant directement
la définition précédente :
Co
def u(n):
if n == 0:
return 2.
else:
return 0.5 * (u(n-1) + 3. / u(n-1))
On remarque que dans la dernière ligne, il y a deux appels à la fonction pour calculer
un−1 . En effet, rien n’interdit d’appeler la fonction u à l’intérieur de son propre corps.
On nomme alors cela une fonction récursive. Pour plus d’efficacité, on peut « factoriser »
les deux appels u(n-1) en stockant le résultat dans une variable locale, ce qui supprime un
appel potentiellement coûteux à u :
...
else:
x = u(n-1)
return 0.5 * (x + 3. / x)
√
Ainsi, pour calculer une valeur approchée de 3, il suffit d’écrire le programme suivant :
print(u(2))
Livre_silo 30 août 2013 16:32 Page 131
es
5 – Fonctions
131
l
rol
Il affiche 1.7321428571428572 à l’écran. Pour comprendre comment ce résultat est calculé, on
va suivre pas à pas l’exécution de cet appel de fonction.
Juste après l’appel u(2), la fonction u compare la valeur de son argument formel n (qui vaut
ici 2) avec 0 et exécute la branche else de l’instruction conditionnelle. Avant d’exécuter
Ey
l’instruction x = u(n-1), l’état de la mémoire est donc le suivant :
n x
u(2) 2. ?.
La variable locale x n’a toujours pas reçu de valeur. L’exécution se poursuit par l’appel u(2-1).
Toute la subtilité des fonctions récursives se dévoile dans ce deuxième appel. Que ce soit les
paramètres formels ou les variables locales, toutes les variables dont la portée est limitée à
t
une fonction s’ajoutent à l’état mémoire du programme lorsque celle-ci est appelée. Ainsi,
gh
l’état mémoire après ce deuxième appel est constitué de deux variables n et de deux variables
locales x : celles allouées par l’appel u(2) et celles allouées pour u(1). Ces variables n’ont de
commun que le nom qu’on leur a donné, car elles représentent bien des cases mémoire
distinctes.
n x
1. ?.
i
u(1)
pyr
n x
u(2) 2. ?.
Tout comme pour l’appel précédent, la nouvelle variable locale x ne contient toujours pas de
Co
valeur avant d’exécuter x = u(1-1). Ce dernier appel à la fonction u aboutit à l’état mémoire
suivant :
n
u(0) 0.
n x
u(1) 1. ?.
n x
u(2) 2. ?.
Livre_silo 30 août 2013 16:32 Page 132
es
Informatique pour tous
132
l
rol
Seule une nouvelle case pour n est allouée en mémoire (puisque la branche else n’est pas
exécutée). L’appel u(0) se termine alors par return 2., ce qui a pour effet, non seulement
de supprimer de la mémoire les variables locales allouées pour cet appel, mais également
d’affecter la valeur 2. à la variable x de l’appel précédent. On se trouve alors dans l’état
mémoire suivant :
Ey
n x
u(1) 1. 2..
n x
u(2) 2. ?.
t
gh
De la même manière, l’appel u(1) se termine par return 0.5 * (2. + 3. / 2.) et on revient à
l’état mémoire du premier appel :
n x
u(2) 2. .
1.75
i
Enfin, l’appel u(2)√se termine et la fonction renvoie la valeur 1.7321428571428572 comme
pyr
approximation de 3.
def u(n):
r = 2.
for i in range(n):
r = 0.5 * (r + 3. / r)
return r
Les mêmes calculs sont effectués, dans le même ordre. Cependant, le rapprochement
avec la définition de la suite (un ) est moins évident. En particulier, dans l’instruction
r = 0.5 * (r + 3. / r), il faut comprendre que l’occurrence de r dans le membre droit dé-
signe ui et que celle de r dans le membre gauche désigne ui+1 . Puisqu’on termine avec
i = n − 1, on a bien calculé un .
Livre_silo 30 août 2013 16:32 Page 133
es
5 – Fonctions
133
l
rol
De la même façon, on peut définir une fonction f prenant en argument un entier naturel n
en se ramenant au calcul de f(0) d’une part et de f(n) en fonction de f(n-1) d’autre part.
La fonction f prend alors la forme suivante :
def f(n):
if n == 0:
Ey
return ...
else:
return ... f(n-1) ...
L’exemple le plus classique est sûrement celui de la fonction factorielle, dont la définition
est donnée page 104 :
def factorielle(n):
if n == 0:
t
return 1
else:
gh
return n * factorielle(n-1)
Il est important de noter qu’une telle fonction ne terminera pas sur un argument n négatif.
En effet, factorielle(-1) appellerait factorielle(-2), qui appellerait factorielle(-3), etc. Il
s’agit donc d’une fonction partielle, à laquelle on peut appliquer toute solution discutée
dans la section 5.2.3. En particulier, on peut s’assurer que n est bien un entier naturel en
i
écrivant :
pyr
def factorielle(n):
assert n >= 0
...
Le schéma de récurrence simple peut être appliqué à des fonctions ayant d’autres argu-
ments que n. Ainsi, la fonction puissance qui calcule x à la puissance n peut facilement être
définie par récurrence simple sur n, de la manière suivante :
def puissance(x, n):
Co
if n == 0:
return 1
else:
return x * puissance(x, n-1)
Comme en mathématiques, le schéma de récurrence simple peut être adapté à des défini-
tions impliquant plusieurs cas de base. Ainsi, on évite une multiplication inutile quand n
vaut 1 dans la fonction puissance en la réécrivant de la façon suivante :
def puissance(x, n):
if n == 0:
return 1
elif n == 1:
return x
else:
return x * puissance(x, n-1)
Livre_silo 30 août 2013 16:32 Page 134
es
Informatique pour tous
134
l
rol
L’écriture de fonctions récursives n’est pas limitée au schéma de récurrence simple. On
peut également utiliser un schéma de récurrence forte, c’est-à-dire effectuer des appels
récursifs sur des valeurs strictement inférieures à n-1. Reprenant l’exemple du calcul de xn ,
on propose un meilleur algorithme qui exploite les deux identités suivantes :
Ey
x2k = (xk )2
x2k+1 = x(xk )2
à savoir x0 = 1. Dans le cas récursif, on commence par calculer x⌊ 2 ⌋ dans une variable r,
n
puis on teste la parité de n pour choisir entre les deux identités ci-avant. Finalement, on
t
obtient le code suivant :
gh
def puissance_rapide(x, n):
if n == 0:
return 1
else:
r = puissance_rapide(x, n // 2)
if n % 2 == 0:
return r * r
i
else:
return x * r * r
pyr
Exercice 5.9 * Écrire une variante (toujours récursive) de la fonction puissance_rapide qui exploite plutôt
les identités suivantes : {
x2k = (x2 )k
x2k+1 = x(x2 )k
Co
Livre_silo 30 août 2013 16:32 Page 135
es
5 – Fonctions
135
l
rol
def briques(n):
assert n >= 1
if n == 1:
return 0
elif n == 2 or n == 3:
return 1
Ey
else:
return briques(n-2) + briques(n-3)
def impair(n):
return (n != 0) and pair(n-1)
i
pyr
On va prendre l’exemple de la fonction factorielle définie plus haut page 133. On veut
montrer par récurrence sur n ⩾ 0 la propriété suivante :
Hn : factorielle(n) termine et renvoie la valeur n!
La propriété H0 est vérifiée car factorielle(0) se réduit à return 1. Pour n > 0, on suppose
Hn−1 et on cherche à montrer Hn . Le calcul de factorielle(n) commence par un appel
récursif à factorielle(n-1). Par hypothèse de récurrence, cet appel termine et renvoie la
valeur (n − 1)!. Puis l’appel à factorielle(n) multiplie ce résultat par n et renvoie le produit.
Donc, cet appel termine et renvoie bien n × (n − 1)! = n!, ce qui démontre Hn .
Il est important de noter que nous n’avons rien démontré quant aux appels à la fonction
factorielle sur des arguments négatifs. En particulier, ils peuvent ne pas terminer (ce qui
est le cas ici), renvoyer des valeurs farfelues, ou encore échouer (ce qui serait le cas avec
assert n >= 0 par exemple).
Livre_silo 30 août 2013 16:32 Page 136
es
Informatique pour tous
136
l
rol
Si on a utilisé un schéma de récurrence forte pour définir une fonction récursive, alors
il faudra bien entendu en démontrer la correction par récurrence forte également. Avec
l’exemple de la fonction puissance_rapide page 134, on cherche à montrer par récurrence
forte sur n ⩾ 0 la propriété suivante :
Ey
Hn : puissance_rapide(x, n) termine et renvoie la valeur xn
La propriété H0 est vérifiée car puissance_rapide(x, 0) se réduit à return 1 (en admettant
que l’on a posé 00 = 1 arbitrairement). Soit maintenant n > 0 ; on suppose Hi pour
tout 0 ⩽ i < n et on veut montrer Hn . Le calcul de puissance_rapide(x, n) commence
par un appel récursif r = puissance_rapide(x, n // 2). On pose k = ⌊ n2 ⌋. Comme n > 0,
on a k < n. On peut donc appliquer l’hypothèse de récurrence Hk , qui affirme que l’ap-
pel puissance_rapide(x, n // 2) termine et renvoie la valeur xk . On distingue alors deux
t
cas, selon la parité de n. Si n est pair, c’est-à-dire n = 2k, alors le programme effectue
return r * r. Donc il termine et renvoie x × x = x
k k 2k
= xn , ce qui démontre Hn . On
gh
procède de même lorsque n est impair.
Exercice 5.10 ** Démontrer la terminaison et la correction de la fonction puissance page 133.
Exercice 5.11 Montrer la correction de la fonction briques définie plus haut (page 134).
Exercice 5.12 ** Que se passe-t-il avec la fonction puissance_rapide si on écrit n au lieu de n
i
/ 2 // 2
(en Python 3) ou encore n / 2. ?
pyr
Même si cette limite semble basse, elle n’exclut pas pour autant l’utilisation de fonctions
Co
récursives en Python. En effet, il existe de nombreuses situations où l’on sait que le nombre
d’appels sera bien inférieur à 1 000. C’est le cas en particulier pour des fonctions qui font un
nombre d’appels logarithmique en la taille des données. Voir par exemple l’exercice 5.15.
Livre_silo 30 août 2013 16:32 Page 137
es
5 – Fonctions
137
l
rol
Cependant, rien n’exclut qu’il s’agisse d’une expression plus compliquée. La célèbre « fonc-
tion 91 » de McCarthy en est un exemple :
def f91(n):
if n <= 100:
Ey
return f91(f91(n + 11))
else:
return n - 10
Il existe même des fonctions récursives dont on ne sait pas démontrer la terminaison,
comme la célèbre suite de Syracuse :
def syracuse(n):
if n == 1:
return 1
t
elif n % 2 == 0:
return syracuse(n // 2)
else:
gh
return syracuse(3 * n + 1)
Lorsqu’on débogue une fonction récursive, il faut évidemment entrer dans les appels
récursifs pour comprendre ce qui se passe : on est naturellement conduit à effectuer
Pas vers l’intérieur.
Cependant, comme expliqué dans ce chapitre, à chaque appel récursif, il est créé
un nouvel ensemble de variables locales à la fonction. L’explorateur de variables ne
montre que les variables en cours d’utilisation, autrement dit celles qui sont propres
Co
Livre_silo 30 août 2013 16:32 Page 138
es
Informatique pour tous
138
l
rol
5.3.3 Complexité d’une fonction récursive
On explique ici comment calculer le coût d’une fonction récursive, à savoir le nombre
d’opérations élémentaires qu’elle effectue ou son occupation mémoire totale. La notion
de complexité sera présentée plus en détail dans le prochain chapitre, section 6.1.
Ey
Reprenons l’exemple de la fonction u (définie p. 130) :
def u(n):
if n == 0:
return 2.
else:
x = u(n-1)
return 0.5 * (x + 3. / x)
t
et évaluons le nombre d’opérations arithmétiques (addition, multiplication et division)
qu’elle effectue.
gh
Si n désigne la valeur de son argument, notons C(n) ce nombre d’opérations. En suivant
la définition de la fonction u, on obtient les deux équations suivantes :
C(0) = 0
C(n) = C(n − 1) + 3
i
En effet, dans le cas n = 0, on ne fait aucune opération arithmétique. Et dans le cas n > 0,
pyr
on fait d’une part un appel récursif sur la valeur n − 1, d’où C(n − 1) opérations, puis trois
opérations arithmétiques (une multiplication, une addition et une division). Il s’agit d’une
suite arithmétique de raison 3, dont le terme général est :
C(n) = 3n
Le nombre d’opérations arithmétiques effectuées par la fonction u est donc proportionnel
Co
à n.
Si en revanche on avait écrit la fonction u plus naïvement, avec deux appels récursifs u(n-1),
c’est-à-dire :
def u(n):
if n == 0:
return 2.
else:
return 0.5 * (u(n-1) + 3. / u(n-1))
Livre_silo 30 août 2013 16:32 Page 139
es
5 – Fonctions
139
l
rol
Une autre manière d’évaluer le coût d’une fonction récursive est de calculer le nombre
d’appels, puis d’évaluer le coût de chaque appel. Si on note A(n) le nombre d’appels récur-
sifs dans les deux exemples précédents, on a :
A(0) = 0
Ey
A(n) = A(n − 1) + 1
A(0) = 0
A(n) = A(n − 1) + A(n − 1) + 1
t
dans le second cas. Le terme général est donc A(n) = n dans le premier cas et
A(n) = 2n − 1 dans le second. Puisqu’on n’a ici aucune opération arithmétique dans
gh
le cas de base n = 0 et exactement trois opérations arithmétiques dans le cas récursif, on
retrouve immédiatement la valeur de C(n) calculée précédemment. D’une manière géné-
rale, la valeur de C(n) ne se déduit pas toujours aussi facilement de la valeur de A(n).
En effet, il peut y avoir des opérations dans le cas de base et/ou un nombre d’opérations
arithmétiques variant selon la valeur de n dans le cas récursif.
i
Comme nous l’avons expliqué page 131, chaque appel récursif alloue de la mémoire pour
pyr
les paramètres effectifs et les variables locales de cet appel. L’occupation mémoire d’un
calcul récursif admet donc pour majorant le produit du nombre d’appels récursifs par la
quantité de mémoire allouée par chaque appel. Dans les deux exemples précédents, on a
calculé explicitement le nombre d’appels A(n). L’occupation mémoire est donc 2n dans le
premier cas (il y a deux cases mémoire, une pour n et une autre pour x) et 2n − 1 dans le
second cas (il y a une case mémoire, pour n). Cependant, dans le second cas, les 2n −1 cases
mémoire ne seront pas utilisées simultanément. En effet, celles allouées pour le premier
Co
appel à u(n-1) peuvent être réutilisées pour le second (et en pratique elles le sont). Pour une
analyse plus fine de l’occupation mémoire, il convient donc de calculer le nombre d’appels
imbriqués.
Exercice 5.14 On considère la première version de la fonction puissance, définie p. 133.
1 Combien effectue-t-elle exactement d’appels récursifs pour calculer xn ?
2 Quel est son coût en mémoire ?
Exercice 5.15 * On considère la fonction puissance_rapide définie p. 134.
1 Montrer qu’elle calcule xn en effectuant un nombre total d’appels récursifs proportionnel à log n.
2 A-t-on la même complexité quand on n’utilise pas de variable locale r, mais que l’on écrit directement :
puissance_rapide(x, n // 2) * puissance_rapide(x, n // 2) à la place de r * r ?
5.4 Exercices
Exercice 5.16 Écrire en Python une fonction qui prend comme argument un entier n et renvoie l’entier 2n .
Exercice 5.17 * Écrire en Python une fonction qui prend comme argument un entier n et renvoie un
booléen qui indique si cet entier est premier ou non.
Livre_silo 30 août 2013 16:32 Page 140
es
Informatique pour tous
140
l
rol
Exercice 5.18 Qu’affiche le programme suivant ? Pourquoi ?
def g(x):
global a
a = 10
return 2 * x
Ey
def f(x):
v = 1
return g(x) + v
a = 3
print(f(6)+a)
Proposer une adaptation de ce programme dans laquelle le résultat affiché ne dépend pas de l’ordre
d’évaluation.
t
Exercice 5.19 Qu’affiche le programme suivant ? Pourquoi ?
gh
def g(x):
global v
v = 1000
return 2 * x
def f(x):
v = 1
i
return g(x) + v
pyr
a = 3
print(f(6)+a)
Proposer une adaptation de ce programme dans laquelle le résultat affiché ne dépend pas de l’ordre
d’évaluation.
Exercice 5.20 Soit f la fonction suivante :
def f(x):
Co
while (True):
x = x + 1
if (x == 1000):
return 2 * x
Livre_silo 30 août 2013 16:32 Page 141
es
5 – Fonctions
141
l
rol
4 Écrire une deuxième fonction qui compte les jeux au cours d’un set et s’arrête lorsqu’un joueur gagne
le set. Cette fonction fera appel à la précédente pour savoir qui gagne les jeux. On n’oubliera pas de
prévoir le cas particulier du jeu décisif.
5 Écrire une troisième fonction qui compte les sets et s’arrête lorsqu’un joueur gagne le match. On pourra,
avant de commencer le match, demander en combien de sets gagnants il est joué.
Ey
Exercices utilisant la récursivité
Exercice 5.23 Écrire une fonction récursive qui calcule la somme des n premiers entiers. Quelle est la
complexité de cette fonction ?
Exercice 5.24 * Suite de Fibonacci. On considère la suite de Fibonacci définie par :
F0 = 0
F1 = 1
t
Fn = Fn−2 + Fn−1 pour n ⩾ 2.
gh
Écrire une fonction récursive basée sur ces relations qui prend n en argument et renvoie Fn . Quelle est
sa complexité ?
Accélérer le calcul de Fn en écrivant plutôt une fonction récursive auxiliaire qui prend en arguments Fn−1 ,
Fn et k ⩾ 0 et renvoie Fn+k (on pourra poser F−1 = 1). Quelle est la nouvelle complexité ?
Exercice 5.25 ** Écrire une fonction récursive qui calcule le PGCD des deux entiers naturels passés en
arguments en suivant l’algorithme d’Euclide. Démontrer la terminaison et la correction de cette fonction.
i
Exercice 5.26 Modifier la fonction qui calcule les termes de la suite de Syracuse (voir l’encadré page 137)
pyr
pour qu’elle affiche les éléments de la suite jusqu’au premier rang n0 tel que un0 = 1, ainsi que la valeur
de n0 .
Exercice 5.27 Définir une fonction récursive qui, étant donné un entier n, décide si l’écriture en base 3
de n ne comporte que des 0 et des 1. Quelle est la complexité de cette fonction ?
Exercice 5.28 ** Les tours de Hanoï. Les tours de Hanoï est un jeu inventé par Édouard Lucas en 1883.
Il est formé de sept disques de tailles différentes répartis en trois colonnes. Au départ, tous les disques
sont empilés sur la colonne de gauche par taille croissante.
Co
hanoi.pdf
Les seuls mouvements possibles sont les déplacements d’un disque situé au sommet d’une colonne vers
le sommet d’une autre colonne, à condition que la colonne d’arrivée soit vide ou que le disque déplacé
soit plus petit que son sommet. On note n -> n' le déplacement d’un disque de la colonne n vers la
colonne n'. Le but du jeu est de déplacer tous les disques vers la colonne de droite.
hanoi2.pdf
Écrire un programme qui affiche une solution du jeu sous la forme d’une suite de mouvements.
Indication : dans une variante, le jeu n’est formé que de six disques. Si l’on sait résoudre le jeu à six disques,
comment résoudre le jeu à sept disques ?
Livre_silo 30 août 2013 16:32 Page 142
es
Informatique pour tous
142
l
rol
Exercice 5.29 * Le flocon de Von Koch. Le flocon, courbe définie par Helge Von Koch en 1906, est un
exemple de courbe continue partout et dérivable nulle part. C’est aussi un exemple d’ensemble fractal,
c’est-à-dire dont la dimension de Hausdorff n’est pas un nombre entier.
Ey
vonkoch.pdf
Cette courbe se définit comme la limite de la suite de courbes dont le premier élément est un segment :
vonkoch0.pdf
t
et dans laquelle on passe d’un élément au suivant en divisant chaque segment en trois et en remplaçant
gh
le morceau du milieu par deux segments formant un triangle équilatéral avec le segment supprimé. Le
deuxième élément est donc :
i
vonkoch1.pdf
pyr
le troisième :
vonkoch2.pdf
Co
et le quatrième :
vonkoch3.pdf
Livre_silo 30 août 2013 16:32 Page 143
l es
rol
6
Ey
Notions de complexité
t
gh
et algorithmique
sur les tableaux
i
pyr
Livre_silo 30 août 2013 16:32 Page 144
es
Informatique pour tous
144
l
rol
6.1 Complexité d’un algorithme
6.1.1 Plusieurs algorithmes pour un même problème
Pour traiter un même problème, il existe souvent plusieurs algorithmes. Quand on doit
Ey
choisir, l’un des critères est celui du temps d’exécution, qu’on appelle aussi parfois coût de
l’algorithme. Cette dénomination n’est pas usurpée car ce temps conditionne les ressources
utilisées (machine sur laquelle on exécutera l’algorithme, consommation électrique…).
Pour un logiciel interactif, par exemple, un temps de réponse court est un élément essentiel
du confort de l’utilisateur. De même, certains programmes industriels doivent être utilisés
un grand nombre de fois dans un délai très court et même un programme qui n’est exécuté
t
qu’une seule fois, par exemple un programme de simulation écrit pour tester une hypothèse
de recherche, est inutilisable s’il demande des mois ou des années de calcul.
gh
Par exemple, si l’on cherche à afficher la liste des diviseurs d’un nombre entier n, on peut
écrire l’algorithme naïf suivant :
def diviseurs(n):
for i in range(1,n+1):
if n % i == 0:
i
print(i)
pyr
import math
def diviseurs(n):
for i in range(1,int(math.sqrt(n))+1):
if n % i == 0:
print(i)
if n//i != i:
print(n//i)
√
Ce deuxième algorithme ne fait plus que ⌊ n⌋ itérations, qui effectuent chacune un calcul
de reste, une ou deux comparaisons,
√ et zéro, un ou deux affichage(s).
√ Au
√ total, il coûte donc
un calcul de √racine carrée, ⌊ n⌋ calculs de reste, entre ⌊ n⌋ et 2 ⌊ n⌋ comparaisons et
entre 0 et 2⌊ n⌋ affichages.
En général, on cherche à déterminer comment le temps d’exécution d’un algorithme varie
en fonction d’un paramètre qu’on appelle la taille du problème. Le temps de recherche des
diviseurs d’un entier n dépend de n, qu’on pourra donc naturellement prendre comme taille
du√problème. Comme on l’a vu, selon l’algorithme, ce temps peut être proportionnel à n ou
à n. De même, quand on s’interrogera sur l’efficacité d’un algorithme manipulant des ta-
bleaux, on cherchera à comprendre comment le temps d’exécution de cet algorithme varie
Livre_silo 30 août 2013 16:32 Page 145
es
6 – Notions de complexité et algorithmique sur les tableaux
145
l
rol
en fonction du nombre d’éléments de ce tableau. Une autre possibilité pour la taille du pro-
blème est de considérer la taille de la représentation des données passées à ce programme.
Par exemple, pour un programme traitant un texte, on prend pour taille du problème le
nombre de caractères de ce texte.
Ey
ATTENTION Taille d’un entier
Lorsqu’un problème dépend d’un paramètre considéré n, il y a deux choix naturels pour la
taille du problème : l’entier lui-même ou la taille des données nécessaires à sa représenta-
tion, c’est-à-dire son nombre de chiffres. Dans le problème précédent,
√ si n est l’entier dont
on cherche les diviseurs, il est nécessaire d’effectuer n ou n calculs de restes. S’il s’agit
en revanche de la taille de l’entier dont on cherche les diviseurs, cela signifie que l’entier
considéré possède n chiffres, donc qu’il est compris entre 10n−1 et 10n . Il est donc néces-
t
saire d’effectuer entre 10n−1 et 10n calculs de restes dans le premier cas et d’en effectuer
n−1 n
entre 10 2 et 10 2 dans le second.
gh
L’évaluation du temps mis par un algorithme pour s’exécuter est un domaine de recherche
à part entière, car elle se révèle parfois très difficile. Néanmoins, dans de nombreux cas,
cette évaluation peut se faire en appliquant quelques règles simples.
i
pyr
Livre_silo 30 août 2013 16:32 Page 146
es
Informatique pour tous
146
l
rol
Le cas particulier des boucles imbriquées illustre bien le principe de calcul du coût de la
boucle for. Ainsi, si les deux boucles sont répétées respectivement m et m′ fois, alors le
corps de la boucle interne est exécuté m × m′ fois en tout. Il est en effet répété à cause de
cette boucle interne, mais aussi parce qu’elle-même est répétée dans son intégralité.
Ey
Exercice 6.1 Reprendre les programmes de l’exercice 4.31 et évaluer leur coût.
L’opération de base ici consiste à afficher une étoile à l’écran (ce qu’on peut assimiler à une affectation
qui modifie l’état de la carte graphique au lieu de l’état de la mémoire).
Dans le premier programme, les nombres d’itérations des boucles interne et externe sont indépendants
et ils sont respectivement de p et n. Au total, on effectue n × p affichages dans ce programme, ce qu’on
peut voir dans le résultat produit à l’écran.
Dans le second programme, la boucle interne dépend du compteur de la boucle externe. Le nombre
∑
n−1
n(n+1)
d’affichages effectués est donc : (i + 1) = 2
.
t
i=0
Pour être complet, il faudrait comptabiliser également l’affichage des retours chariot ; ils sont cependant
gh
nettement moins nombreux que les étoiles et ne jouent pas un rôle significatif dans le temps d’exécution
du programme.
de la réalité. Il n’est évidemment utile que dans les cas où il est suffisamment proche de la
réalité.
pyr
d’autre part parce que le compliquer dépasserait le cadre du programme de cet enseigne-
ment.
Livre_silo 30 août 2013 16:32 Page 147
es
6 – Notions de complexité et algorithmique sur les tableaux
147
l
rol
deux fois plus rapide que l’autre. Cela ne change évidemment rien à l’efficacité intrinsèque
de l’algorithme et ce qui nous intéresse réellement n’est pas le temps précis d’exécution d’un
programme, mais l’ordre de grandeur de ce temps en fonction de la taille des données.
Une dernière notion à considérer est celle du terme dominant dans le temps d’exécution
Ey
d’un algorithme. Par exemple, si on a déterminé que ce temps était proportionnel à n2 +3n,
dès que la taille n des données devient un peu importante, il est connu que le terme 3n
augmente beaucoup moins vite que n2 : on dit qu’il est négligeable devant ce dernier. Pour
décrire l’efficacité d’un algorithme, seul le terme qui croît le plus vite a donc un intérêt. Par
exemple, ici pour n ⩾ 3, on a n2 +3n ⩽ 2n2 ; la quantité n2 +3n est donc bornée, à partir
d’un certain rang, par le produit de n2 et d’une constante. On dit alors que la quantité de
n2 + 3n est « un grand O de n2 » et on écrira n2 + 3n = O(n2 ). De manière générale,
t
on dira qu’un algorithme a une complexité en O(f (n)) si son coût est, à partir d’un certain
rang, inférieur au produit de f (n) par une constante ¹.
gh
On va ébaucher un rapide inventaire des complexités qu’on pourra être amené à rencontrer.
Il n’est pas possible, sur le plan théorique, de dire combien de temps un algorithme en O(n)
met à s’exécuter pour une valeur particulière de n, puisque deux algorithmes dont les temps
de calcul seraient respectivement n × 10−9 s et n × 109 s seraient tous les deux en O(n),
bien que le rapport de leurs temps d’exécution soit 1018 . Cependant, on peut donner les
i
ordres de grandeur des temps d’exécution que l’on rencontre en pratique pour un problème
pyr
O(1) temps constant 1 ns Le temps d’exécution ne dépend pas des données traitées, ce
qui est assez rare !
O(n2 ) quadratique 1/4 h Cette complexité reste acceptable pour des données de taille
moyenne (n < 106 ), mais pas au-delà.
Livre_silo 30 août 2013 16:32 Page 148
es
Informatique pour tous
148
l
rol
Tableau 6.1 Ordres de grandeur des temps d’exécution
d’un problème de taille 106 sur un ordinateur à un milliard d’opérations par seconde (suite)
O(nk ) polynômiale 30 ans Ici, nk est le terme de plus haut degré d’un polynôme en n ; il
si k = 3 n’est pas rare de voir des complexités en O(n3 ) ou O(n4 ).
Ey
O(2n ) exponentielle plus de 10300 000 Un algorithme d’une telle complexité est impraticable sauf
milliards d’années pour de très petites données (n < 50). Comme pour la com-
plexité logarithmique, la base de l’exponentielle ne change
fondamentalement rien à l’inefficacité de l’algorithme.
Exercice 6.2 Les algorithmes suivants calculent et affichent différentes listes de nombres. Quelle est la
t
complexité de chacun d’entre eux ?
def table1(n):
gh
for i in range(11):
print(i * n)
def table2(n):
for i in range(n):
print(i * i)
i
def table3(n):
for i in range(n):
pyr
for j in range(n):
print(i * j, end=" ")
print()
• L’algorithme table1 affiche la table de multiplication de n jusqu’au rang 10. La boucle est toujours
exécutée 11 fois et ne comporte qu’une multiplication. Le temps d’exécution ne dépend donc pas de
l’entrée n : la complexité est O(1).
• L’algorithme table2 affiche la suite des carrés des nombres entiers jusqu’à (n − 1)2 . La boucle est
exécutée n fois et ne comporte qu’une multiplication : la complexité est O(n).
Co
• L’algorithme table3 construit une table de multiplication pour tous les entiers de 1 à n en donnant tous
leurs multiples jusqu’au n-ième. Il comprend deux boucles imbriquées, chacune effectuant n répéti-
tions de son corps ; le corps de la boucle interne ne comporte qu’une multiplication. La complexité est
ici O(n2 ).
Livre_silo 30 août 2013 16:32 Page 149
es
6 – Notions de complexité et algorithmique sur les tableaux
149
l
rol
√
Si n est un nombre premier, alors il faudra n − 1 itérations ; pourtant, pour n − 1 et
n + 1 qui sont pairs, le programme s’arrête dès la première itération.
Si la fonction f caractérise l’efficacité d’un algorithme, on veut avoir l’assurance que l’exé-
cution du programme sera terminée en un temps proportionnel à f (n), éventuellement
Ey
moins, mais pas plus. On cherche donc un majorant du temps d’exécution, autrement dit
on retiendra le pire des cas pour exprimer la complexité.
Complexité en espace
Jusqu’ici, on a uniquement discuté du temps d’exécution des algorithmes. Une autre res-
i
pyr
algorithme la place nécessaire en mémoire pour le faire fonctionner. Elle s’exprime égale-
ment sous la forme d’un O(f (n)) où n est la taille du problème.
Évaluer la complexité en espace d’un algorithme ne pose la plupart du temps pas de diffi-
culté ; il suffit de faire le total des tailles en mémoire des différentes variables utilisées. Une
première exception à la règle est le cas où on alloue dynamiquement de l’espace mémoire
au cours du programme (voir chapitre 12). L’autre cas est celui des fonctions récursives,
Co
qui cachent souvent une complexité en espace élevée (voir la section 5.3 pour un aperçu
de l’empreinte mémoire d’une fonction récursive).
Livre_silo 30 août 2013 16:32 Page 150
es
Informatique pour tous
150
l
rol
En revanche, dans le pire cas, la batterie du téléphone est déchargée, il convient donc de
la recharger pendant 4 heures. Le coût dans le pire cas pour un envoi est donc de 4 heures
et 2 minutes.
Néanmoins, le coût pour n envois, où n est grand, est bien inférieur à 4n heures et 2n mi-
nutes. En effet, une fois le téléphone chargé, son utilisateur va pouvoir envoyer un millier
Ey
de SMS avant de devoir recharger⌈la batterie.
⌉ On peut donc dire que dans le pire cas, l’envoi
de n SMS successifs va demander 1 000 n
× 4 heures plus 2n minutes, On a donc la garantie
que pour n grand, le temps mis pour envoyer n SMS est au plus de l’ordre de n × 14 se-
condes plus 2n minutes. Autrement dit, le temps mis pour envoyer un SMS est de l’ordre de
2 minutes et 14 secondes. On dit que cette durée est la complexité amortie représentant le
coût de l’envoi. La notion d’amortissement vient de la comptabilité : le coût d’un kilomètre
en voiture est nul si la voiture fonctionne et si le plein est fait, alors qu’il est extrêmement
élevé s’il faut commencer par acheter la voiture. La notion pertinente pour mesurer ce coût
est en général de calculer l’amortissement des dépenses initiales (l’achat de la voiture) sur
t
la totalité du kilométrage.
C’est une situation qu’on retrouve en informatique : il arrive ainsi que, dans certains pro-
gh
blèmes, une opération ait un coût O(n) dans le pire cas, où n est la taille du problème, et un
coût constant en complexité amortie. En Python, l’opération d’ajout d’un nouvel élément
à la fin d’un tableau de taille n rentre dans ce cadre.
Cependant, la théorie de la complexité amortie dépasse le cadre de cet ouvrage.
i
Exercice 6.3 Quelle est la complexité en temps d’un algorithme de division euclidienne procédant par
soustractions successives ? Et sa complexité en espace ?
pyr
Comparer avec la complexité en temps et en espace de l’algorithme de division euclidienne que l’on
apprend à l’école primaire et au collège (on ne cherchera pas à le programmer).
Exercice 6.4 Quelle est la complexité en temps de l’algorithme écrit dans l’exercice 4.36 ?
Exercice 6.5 * Quelle est la complexité en temps de la version récursive de l’algorithme de Horner pré-
sentée page 154 ?
Et sa complexité en espace ?
Co
3 7 42 1 4 8 12
La particularité d’une structure de tableau est que le contenu de la i-ème case peut être lu
ou modifié en temps constant, c’est-à-dire indépendant de i.
2. Dans la terminologie Python, la structure de données correspondante est appelée une « liste », ce qui est
un peu malheureux. Nous donnerons un peu plus de détails sur cette structure dans le chapitre 12.
Livre_silo 30 août 2013 16:32 Page 151
es
6 – Notions de complexité et algorithmique sur les tableaux
151
l
rol
En Python, on peut construire un tel tableau en énumérant ses éléments entre crochets,
séparés par des virgules :
In [1]: a = [3, 7, 42, 1, 4, 8, 12]
Les cases sont numérotées à partir de 0. On accède à la première case avec la notation a[0],
Ey
à la seconde avec a[1], etc. Si on tente un accès en dehors des cases valides du tableau, alors
on obtient une erreur :
In [2]: a[5]
Out[2]: 8
In [3]: a[7]
pyr
In [4]: 50 * [0,1]
La fonction print affiche le contenu d’un tableau, sous la même forme que celle utilisée
pour le construire :
In [7]: print(a)
Livre_silo 30 août 2013 16:32 Page 152
es
Informatique pour tous
152
l
rol
On peut également extraire tous les éléments situés entre les indices i (inclus) et j (exclu)
d’un tableau t avec la notation t[i:j ]. Ainsi, l’instruction suivante :
In [8]: b = a[2:4]
Ey
générale, la notation t[i:j ] construit un nouveau tableau de longueur j − i. Cette opé-
ration n’est pas instantanée, puisqu’elle revient à faire j − i affectations. Il est important
de comprendre que les cases mémoire de a ont été copiées. En particulier, aucune modifi-
cation des cases de b ne modifiera le contenu de a (et réciproquement). Ainsi, la séquence
d’instructions suivante affiche toujours [3, 0, 42, 1, 4, 8, 12].
b[0] = 5
print(a)
t
En particulier, on peut obtenir une copie du tableau a avec a[0:7], et plus généralement avec
gh
a[0:len(a)].Il existe même un raccourci pour cela, à savoir a[:].
a = [0, 1, 2, 3]
b = a
pyr
alors le tableau b n’est pas une copie de a. En effet, les deux variables a et b désignent toutes
les deux le même tableau. On dit alors que b et a sont des alias.
figures/alias1.pdf
figures/alias2.pdf
Cependant, a et b ne sont pas liés à jamais. Si on affecte à a un nouveau tableau, par exemple
avec a = [4, 5], alors on obtient la situation suivante :
figures/alias3.pdf
Livre_silo 30 août 2013 16:32 Page 153
es
6 – Notions de complexité et algorithmique sur les tableaux
153
l
rol
Comme on l’a vu au chapitre 3, les chaînes de caractères proposent les mêmes opérateurs
que les tableaux pour accéder à un caractère, connaître la longueur de la chaîne, extraire
une sous-chaîne, etc. La seule opération qui ne soit pas commune est la modification d’un
caractère, les chaînes étant immuables. Une conséquence appréciable est que certains des
programmes de ce chapitre sont utilisables sans changement sur des chaînes de caractères.
Ey
6.2.3 Parcours de tous les éléments d’un tableau
Pour exemple, on cherche à calculer la somme de tous les éléments d’un tableau d’entiers.
Un algorithme simple pour cela consiste à initialiser une variable s à 0 et à parcourir tous
les éléments du tableau pour les ajouter un par un à cette variable. La méthode natu-
relle pour effectuer ce parcours consiste à utiliser une boucle for. En effet, la construction
t
for i in range(n) affecte successivement à la variable i les valeurs 0, 1, . . ., n − 1. Ainsi
peut-on écrire la fonction somme de la manière suivante :
gh
def somme(a):
s = 0
for i in range(len(a)):
s += a[i]
return s
i
Cependant, on peut faire encore plus simple, car la boucle for de Python sait parcourir
pyr
directement tous les éléments du tableau a avec for x in a . Ainsi, le programme se simplifie
en :
def somme(a):
s = 0
for x in a:
s += x
return s
Co
On suppose que les coefficients du polynôme A sont stockés dans un tableau a, le co-
efficient ai étant stocké dans a[i]. Ainsi, le tableau [1, 2, 3] représente le polynôme
3X 2 + 2X + 1. Une méthode simple, mais naïve, consiste à écrire une boucle qui réalise
exactement la somme telle qu’elle est écrite ci-dessus. On a donc besoin d’accéder non
seulement à l’indice i, mais aussi à la valeur a[i]. On peut parcourir le tableau en utilisant
la construction range, comme plus haut :
def evaluer(a, x):
s = 0
for i in range(len(a)):
s += a[i] * x**i
return s
Livre_silo 30 août 2013 16:32 Page 154
es
Informatique pour tous
154
l
rol
Là encore, il existe en Python une construction idiomatique, à savoir
for i, ai in enumerate(a) , qui donne simultanément l’indice et la valeur de chaque
case. Ainsi, on peut écrire :
def evaluer(a, x):
s = 0
Ey
for i, ai in enumerate(a):
s += ai * x**i
return s
Une méthode plus efficace pour évaluer un polynôme est d’utiliser la méthode de Horner.
Elle consiste à réécrire la somme précédente de la manière suivante :
A(X) = a0 + X(a1 + X(a2 + · · · + X(an−2 + Xan−1 ) . . . ))
t
Ainsi, on évite le calcul des différentes puissances X i , en factorisant intelligemment et en
ne faisant plus que des multiplications par X. Pour réaliser ce calcul, il faut parcourir le
gh
tableau de la droite vers la gauche, pour que le traitement de la i-ème case de a consiste
à multiplier par X la somme courante, puis à lui ajouter a[i]. Si la variable s contient la
somme courante, la situation est donc la suivante :
A(X) = a0 + X(· · · (ai + X(ai+1 + · · ·)))
| {z }
i
s
Cette version évite notamment l’utilisation de reversed. Elle a cependant le défaut d’être
plus longue à exécuter. En effet, l’expression p[1:] réalise une copie des éléments du ta-
bleau, ce qui revient à effectuer len(p)-1 affectations. Au total, pour un tableau de taille
initiale n, ce programme effectuerait donc (n − 1) + (n − 2) + · · · + 1 affectations.
Livre_silo 30 août 2013 16:32 Page 155
es
6 – Notions de complexité et algorithmique sur les tableaux
155
l
rol
Exercice 6.6 *
1 Écrire une fonction qui prend un tableau d’entiers t en argument et renvoie le tableau des sommes
cumulées croissantes correspondantes, autrement dit un tableau de même taille dont la k-ième com-
∑
k
posante vaut t[i]. Le tableau fourni en argument ne sera pas modifié.
i=0
Ey
2 Évaluer la complexité de cette fonction.
3 Est-il possible d’en écrire une version plus efficace ?
Exercice 6.7 Écrire une fonction qui renvoie un tableau contenant les n premières valeurs de la suite de
Fibonacci (voir exercice 5.24).
pyr
i = 0
while i < len(a) and a[i] != x:
i += 1
return i < len(a)
Il est important de noter que le caractère paresseux du and est ici crucial : il évite l’accès
en dehors des bornes du tableau lorsque i atteint len(a). On peut procéder autrement en
utilisant la construction return à l’intérieur de la boucle pour interrompre son exécution.
Co
Du coup, on peut de nouveau utiliser une boucle for comme dans la section précédente :
def appartient(x, a):
for y in a:
if y == x:
return True
return False
Livre_silo 30 août 2013 16:32 Page 156
es
Informatique pour tous
156
l
rol
Une autre manière de signaler l’échec aurait consisté à utiliser une valeur entière non signi-
ficative. Cependant, la solution avec None est plus robuste, par exemple car elle empêchera
de traiter ce résultat comme un entier significatif dans un code qui appelle la fonction
indice.
Ey
6.3.2 Recherche dichotomique dans un tableau trié
On note que dans le pire des cas, les fonctions appartient et indice précédentes parcourent
tout le tableau et effectuent donc n comparaisons, où n est la longueur du tableau. Dans
certains cas, cependant, la recherche d’un élément dans un tableau peut être réalisée de
manière plus efficace. C’est le cas par exemple lorsque le tableau est trié. On peut alors
exploiter l’idée suivante : on coupe le tableau en deux par le milieu et on détermine si la
t
valeur x doit être recherchée dans la moitié gauche ou droite. En effet, il suffit pour cela de
la comparer avec la valeur centrale. Puis, on répète le processus sur la portion sélectionnée.
gh
On suppose par exemple que l’on cherche la valeur 9 dans le tableau [1, 3, 5, 6, 9, 12, 14].
La recherche s’effectue ainsi :
On cherche dans a[0:7]. 1 3 5 6 9 12 14
i
pyr
0 g d n
<x ? >x
Livre_silo 30 août 2013 16:32 Page 157
es
6 – Notions de complexité et algorithmique sur les tableaux
157
l
rol
On commence par initialiser les variables g et d avec 0 et len(a)-1, respectivement :
def recherche_dichotomique(x, a):
g, d = 0, len(a)-1
Ey
while g <= d:
Il est important de noter qu’on effectue ici une division entière. Qu’elle soit arrondie vers
le bas ou vers le haut, on obtiendra toujours une valeur comprise entre g et d, ce qui assure
d’une part que a[m] existe et qu’il est bien situé entre g et d. Si a[m] est l’élément recherché,
t
on a terminé la recherche :
gh
if a[m] == x:
return m
Sinon, on détermine si la recherche doit être poursuivie à gauche ou à droite. Si a[m] < x,
on poursuit à droite :
if a[m] < x:
i
g = m+1
pyr
Si on sort de la boucle while, c’est que l’élément ne se trouve pas dans le tableau, car il ne
reste que des éléments strictement plus petits (à gauche de g) ou strictement plus grands
(à droite de d). On renvoie alors None pour signaler l’échec.
Co
return None
Livre_silo 30 août 2013 16:32 Page 158
es
Informatique pour tous
158
l
rol
On veut maintenant montrer que la complexité de cet algorithme est au pire O(log n) où
n est la longueur du tableau. En particulier, on effectue au pire un nombre logarithmique
de comparaisons. La démonstration consiste à établir qu’après k itérations de la boucle,
on a l’inégalité suivante :
n
Ey
d−g < k·
2
La démonstration se fait par récurrence sur k. Initialement, on a g = 0 et d = n − 1 et
k = 0, donc l’inégalité est établie. On suppose maintenant l’inégalité vraie au rang k et
g ⩽ d. À la fin de la k + 1-ième itération, on a soit g = m+1, soit d = m-1. Dans le premier
cas, on a donc :
(⌊ ⌋ )
g+d g+d d−g n n
t
d− +1 ⩽d− = < k = k+1 ·
2 2 2 2 ×2 2
gh
Le second cas est laissé au lecteur. On conclut ainsi : pour k ⩾ log2 (n), on a d − g < 1,
c’est-à-dire d − g ⩽ 0. On fait alors au plus une dernière itération.
La complexité de la recherche dichotomique est donc O(log n), alors que celle de la re-
cherche séquentielle est O(n). Il ne faut cependant pas oublier qu’elles ne s’appliquent pas
i
dans les mêmes conditions : une recherche dichotomique est exclue si les données ne sont
pas triées.
pyr
Exercice 6.8 Écrire une fonction qui renvoie l’élément maximal d’un tableau d’entiers. On discutera des
diverses solutions possibles pour traiter le cas d’un tableau de longueur 0.
Exercice 6.9 * Écrire une fonction qui renvoie les deux plus grands éléments d’un tableau d’entiers. On
supposera que le tableau est de longueur au moins 2 ; en revanche, on veillera à ne le parcourir qu’une
seule fois.
Exercice 6.10
Co
1 Écrire une fonction qui renvoie l’indice de la première occurrence de l’élément maximal d’un tableau
d’entiers.
2 Évaluer la complexité de cette fonction.
3 Démontrer que tout algorithme répondant à cette question a une complexité au moins linéaire.
Livre_silo 30 août 2013 16:32 Page 159
es
6 – Notions de complexité et algorithmique sur les tableaux
159
l
rol
On effectue la recherche avec une boucle for, qui va considérer toutes les positions possibles
pour le mot m, c’est-à-dire tous les indices i entre 0 et len(t) - len(m), au sens large.
def recherche_mot(m, t):
for i in range(1 + len(t) - len(m)):
Ey
On teste si le mot m apparaît à la position i avec une seconde boucle, qui compare les
caractères de m et de t un à un. On utilise une variable j pour cela et on s’arrête, soit
lorsque j atteint len(m), soit lorsque les caractères diffèrent :
j = 0
while j < len(m) and m[j] == t[i + j]:
j += 1
Il est important de noter que le caractère paresseux du and est encore ici crucial : il évite
t
l’accès en dehors des bornes du tableau lorsque j atteint len(m). Une fois sorti de la boucle
while, on a reconnu le mot m à la position i si et seulement si j == len(m), auquel cas on ren-
gh
voie i. On interrompt ainsi l’exécution de la fonction dès la première occurrence trouvée :
if j == len(m):
return i
c’est qu’il n’y a pas d’occurrence de m dans t, ce que l’on signale en renvoyant None :
return None
pyr
Livre_silo 30 août 2013 16:32 Page 160
es
Informatique pour tous
160
l
rol
SAVOIR-FAIRE Concevoir un algorithme répondant à un problème
précisément posé
1 Identifier la structure adaptée pour représenter les données du problème (par
Ey
exemple un tableau).
2 Déterminer si le problème peut se ramener à un des algorithmes usuels sur cette
structure (par exemple le parcours du tableau).
3 Apporter les modifications nécessaires à cet algorithme pour répondre au pro-
blème.
Exercice 6.11 Concevoir un algorithme vérifiant qu’une suite est croissante jusqu’à un certain rang.
t
1 On peut représenter les termes de la suite comme un tableau de flottants u.
gh
2 Si la suite est effectivement croissante, il faudra le vérifier à chaque rang, mais si elle ne l’est pas, on
pourra interrompre le parcours du tableau dès qu’on aura trouvé deux valeurs en ordre décroissant.
L’algorithme que l’on va écrire est donc à rapprocher d’une recherche séquentielle.
3 Il y a principalement deux différences avec la recherche séquentielle. D’une part, on ne va pas comparer
un élément avec une valeur fixée, mais avec l’élément suivant. D’autre part, on veut savoir si la suite
est croissante et donc on renverra True dans le cas où on a parcouru tout le tableau sans trouver de
valeurs en ordre décroissant.
i
pyr
def croissante(u):
for i in range(len(u)-1):
if u[i] > u[i+1]:
return False
return True
Exercice 6.12 Modifier le programme 2 pour qu’il affiche toutes les occurrences de m dans t. La complexité
est-elle différente après cette modification ?
Co
Exercice 6.13 Écrire une fonction qui vérifie qu’une chaîne de caractères est composée uniquement de
lettres de l’alphabet, d’espaces et des symboles de ponctuation usuels.
Évaluer sa complexité.
Exercice 6.14 Écrire une fonction qui vérifie qu’une chaîne de caractères est une adresse e-mail valide.
On pensera par exemple à vérifier la présence du symbole @, l’absence de certains caractères, etc.
Exercice 6.15 * Écrire une fonction qui vérifie qu’une chaîne de caractères est un palindrome, c’est-à-dire
qu’elle est identique qu’on la lise de gauche à droite ou de droite à gauche.
Adapter cette fonction pour qu’elle ne tienne pas compte des espaces ni des signes de ponctuation.
6.5 Matrices
On peut choisir de représenter une matrice de dimensions (n, p) par un tableau de lon-
gueur n, dont les éléments sont des tableaux de longueur p.
Livre_silo 30 août 2013 16:32 Page 161
es
6 – Notions de complexité et algorithmique sur les tableaux
161
l
rol
Ainsi, la matrice (2, 3) suivante :
0 1 2
M =
3 4 5
Ey
peut être définie en Python par le tableau m ci-après :
In [9]: m = [[0, 1, 2], [3, 4, 5]]
figures/matrice1.pdf
t
gh
En particulier, on accède à l’élément Mi,j avec l’expression m[i][j]. Bien entendu, on aurait
tout aussi bien pu faire le choix de représenter cette matrice par un tableau de longueur 3,
dont les éléments auraient été les colonnes de la matrice :
In [10]: m = [[0, 3], [1, 4], [2, 5]]
pyr
figures/matrice2.pdf
On pourrait être tenté de construire un tableau v = [0, 1, 2], puis de l’utiliser trois fois
pour chacune des lignes de la matrice :
v = [0, 1, 2]
m = [v, v, v]
Livre_silo 30 août 2013 16:32 Page 162
es
Informatique pour tous
162
l
rol
Il s’agit bien là d’une matrice de dimensions (3, 3). Cependant, sa représentation en mé-
moire n’est pas la même que dans l’exemple précédent et montre au contraire un partage
du tableau v entre les trois lignes.
Ey
figures/partage1.pdf
c’est en fait toute la colonne, c’est-à-dire les trois éléments m[0][1], m[1][1], m[2][1], qui
sont modifiés :
t
figures/partage2.pdf
gh
On peut l’observer facilement avec print(m), qui affiche :
[[0, 17, 2], [0, 17, 2], [0, 17, 2]]
Pour la même raison, on ne peut pas utiliser l’expression [[0] * 4] * 3 pour créer une ma-
trice de dimensions (4, 3) initialisée avec des zéros, car elle correspond en fait à la situation
suivante :
i
pyr
figures/partage3.pdf
6.5.1 Création
Pour créer une matrice de grande taille, on ne souhaite évidemment pas donner tous ses
éléments explicitement. Par ailleurs, les dimensions peuvent être contenues dans des va-
riables. Écrivons donc une fonction creer_matrice pour construire une matrice M de di-
mensions (n, p) où chaque élément Mi,j est initialisé avec une valeur v. On procède en
créant un tableau de taille n initialisé avec None :
def creer_matrice(n, p, v):
m = [None] * n
Livre_silo 30 août 2013 16:32 Page 163
es
6 – Notions de complexité et algorithmique sur les tableaux
163
l
rol
Plus simplement, on peut utiliser la construction par compréhension pour faire la même
chose :
def creer_matrice(n, p, v):
return [[v] * p for i in range(n)]
Ey
On pourrait penser que, plus simplement encore, l’expression [[v] * p] * n suffit à
construire cette matrice, mais ce n’est pas le cas, comme expliqué en détail page 161.
6.5.2 Copie
Si on veut copier une matrice m de dimensions (n, p), il faut prendre soin de copier chacune
de ses n lignes, pour obtenir autant de nouveaux tableaux. De manière élémentaire, on
t
commence par construire un tableau r de taille n initialisé à None :
def copie_matrice(m):
gh
n = len(m)
r = [None] * n
Puis on affecte chaque ligne r[i] avec une copie de la ligne m[i] obtenue en utilisant la
notation m[i][:] :
for i in range(n):
i
r[i] = m[i][:]
return r
pyr
Là encore, on peut utiliser la notation par compréhension, pour réécrire plus simplement
cette fonction :
def copie_matrice(m):
return [m[i][:] for i in range(len(m))]
Quelle que soit la solution retenue, elle convient pour toutes les matrices dont les éléments
Co
sont des types simples (entiers, flottants, booléens). Plus généralement, elle convient pour
toutes les matrices dont les éléments sont immuables, par exemple les chaînes de caractères.
figures/partage4.pdf
Livre_silo 30 août 2013 16:32 Page 164
es
Informatique pour tous
164
l
rol
6.5.3 Dimensions
Un tableau de tableaux ne représente pas nécessairement une matrice de dimensions (n, p).
En effet, rien n’empêche les tableaux qui représentent les lignes de la matrice d’être de
longueurs différentes :
Ey
figures/matrice3.pdf
On va écrire une fonction dimensions qui vérifie qu’un tableau de tableaux m représente
bien une matrice de dimensions (n, p), avec n > 0 et p > 0, et renvoie la paire (n, p).
t
La fonction n’est pas définie (elle échouera) lorsque son argument ne représente pas une
gh
matrice. On commence par déterminer n comme la longueur du tableau m et par vérifier
que n > 0 :
def dimensions(m):
n = len(m)
assert n > 0
i
p = len(m[0])
assert p > 0
Enfin, on vérifie que toutes les lignes de m ont bien la longueur p et on renvoie la paire
(n, p) :
for r in m:
assert len(r) == p
Co
return (n, p)
Il est important de noter que la fonction dimensions fait l’hypothèse que son argument m est
un tableau de tableaux. Dans le cas contraire, elle peut échouer.
6.5.4 Transposition
On va maintenant écrire une fonction qui transpose une matrice M de dimensions (n, p),
c’est-à-dire qui renvoie une nouvelle matrice T de dimensions (p, n), avec Tj,i = Mi,j .
On commence par récupérer les dimensions de la matrice m puis on crée une matrice vide t
de dimensions (p, n) :
def transpose(m):
n, p = dimensions(m)
t = creer_matrice(p, n, None)
Livre_silo 30 août 2013 16:32 Page 165
es
6 – Notions de complexité et algorithmique sur les tableaux
165
l
rol
Puis on initialise les éléments de t avec une double boucle qui parcourt tous les éléments
de m :
for j in range(p):
for i in range(n):
t[j][i] = m[i][j]
Ey
return t
Ici, on remplit t ligne par ligne, mais on aurait pu tout aussi bien la remplir colonne par co-
lonne. Comme pour la création et la copie, on peut utiliser la notation par compréhension
pour réécrire la fonction transpose de la manière suivante :
def transpose(m):
n, p = dimensions(m)
return [[m[i][j] for i in range(n)] for j in range(p)]
t
Bien que cette seconde version soit plus concise que la première, elle n’est pas forcément
gh
plus claire : en particulier, on ne voit pas facilement l’égalité Tj,i = Mi,j , alors qu’elle
est manifeste dans la première version. De manière générale, nous n’abuserons pas de la
notation par compréhension dans cet ouvrage.
On va terminer avec une opération courante, à savoir la multiplication de deux matrices
pyr
a et b. On commence par récupérer les dimensions des deux matrices et par vérifier leur
compatibilité, c’est-à-dire que le nombre de colonnes de a est égal au nombre de lignes
de b :
def mult_matrice(a, b):
n, p = dimensions(a)
q, r = dimensions(b)
assert q == p
Co
On crée alors une nouvelle matrice c de dimensions (n, r), initialisée par 0, puis on effectue
le calcul :
∑
p−1
ci,j = ai,k × bk,j
k=0
La structure de triple boucle de cette fonction montre clairement que sa complexité est
O(n × p × r). L’exercice 6.33 propose de réécrire cette fonction à l’aide de la transposition
et du produit scalaire (sans en changer la complexité cependant).
Livre_silo 30 août 2013 16:32 Page 166
es
Informatique pour tous
166
l
rol
PROGRAMME 3 Opérations élémentaires sur les matrices
def creer_matrice(n, p, v):
"""crée une nouvelle matrice de taille n x p,
initialisée avec la valeur v"""
Ey
return [[v] * p for i in range(n)]
def copie_matrice(m):
"""copie une matrice (mais pas ses éléments)"""
return [m[i][:] for i in range(len(m))]
def dimensions(m):
"""vérifie que m est bien une matrice,
et renvoie ses dimensions (lignes, colonnes)"""
t
n = len(m)
assert n > 0
gh
p = len(m[0])
assert p > 0
for r in m:
assert len(r) == p
return (n, p)
def transpose(m):
i
pyr
for i in range(n):
for j in range(r):
for k in range(p):
c[i][j] += a[i][k] * b[k][j]
return c
Livre_silo 30 août 2013 16:32 Page 167
es
6 – Notions de complexité et algorithmique sur les tableaux
167
l
rol
Le programme principal construit un tableau a et le passe à la fonction f :
a = [0, 1, 2, 3]
f(a)
Ey
est un alias pour le tableau a (voir page 152). Ainsi, à l’entrée de la fonction f, on a :
figures/alias1.pdf
figures/alias2.pdf
t
gh
En particulier, après l’appel à la fonction f, on a a[2] == 42.
Il est parfois utile d’écrire des fonctions qui modifient le contenu d’un tableau reçu en
argument. Un exemple typique est celui d’une fonction qui échange le contenu de deux
cases d’un tableau :
def echange(a, i, j):
i
tmp = a[i]
a[i] = a[j]
pyr
a[j] = tmp
Un autre cas est celui d’une fonction qui trie un tableau ; plusieurs exemples seront donnés
dans le chapitre 13.
Co
Livre_silo 30 août 2013 16:32 Page 168
es
Informatique pour tous
168
l
rol
6.7 Exercices
Exercice 6.16 * Le but de cet exercice est d’écrire une fonction qui détermine la médiane d’un tableau
d’entiers.
1 Programmer l’algorithme suivant : on recherche le minimum et le maximum du tableau, on les supprime
Ey
et on recommence jusqu’à obtenir un tableau de longueur inférieure ou égale à 2. On déduit alors
facilement la valeur de la médiane du ou des entiers restants.
2 Quelle est la complexité de cet algorithme ? On verra au chapitre 13 qu’il existe des méthodes plus
efficaces.
Exercice 6.17 Écrire une fonction qui détermine si tous les éléments d’un tableau (d’entiers ou de flot-
tants) sont positifs ou nuls. On veillera à sortir de la fonction dès qu’une valeur négative est rencontrée.
Exercice 6.18 Écrire une fonction qui prend en arguments trois entiers m, n et p et renvoie un tableau
identique à celui désigné par l’expression list(range(m, n, p)), bien entendu sans utiliser cette dernière
t
expression.
Exercice 6.19 Écrire une fonction qui prend un entier n en argument et renvoie un tableau de n entiers
gh
tirés aléatoirement dans l’intervalle J0, nK. On utilisera la fonction random.randint pour cela.
Exercice 6.20 Écrire une fonction qui renvoie un tableau aléatoire de caractères entre 'a' et 'z'. On
utilisera pour cela les fonctions ord (qui renvoie le code d’un caractère) et chr (qui renvoie le caractère
associé à un code donné).
Exercice 6.21 * Dans cet exercice, on écrira différentes versions d’une fonction qui prend en argument
i
pyr
Livre_silo 30 août 2013 16:32 Page 169
es
6 – Notions de complexité et algorithmique sur les tableaux
169
l
rol
Exercice 6.28 En s’inspirant de la fonction creer_matrice, écrire les trois fonctions suivantes :
1 une fonction qui prend un entier n > 0 en argument et renvoie la matrice identité de dimension n ;
2 une fonction qui prend un entier n > 1 en argument et renvoie la matrice tridiagonale de dimension n
suivante :
1 1 (0)
Ey
.. ..
1 . .
.. .. ..
. . .
.. ..
. . 1
(0) 1 1
3 une fonction qui prend un entier n > 0 en argument et renvoie la matrice de dimension n formée sur
le modèle suivant, ici pour n = 4 :
t
1 2 3 4
gh
2 1 2 3
3 2 1 2
4 3 2 1
Exercice 6.29 * Écrire une fonction qui décide si une matrice donnée est symétrique et une autre qui
décide si elle est antisymétrique. Quelle est la complexité de ces fonctions ?
i
Exercice 6.30
pyr
5 Écrire une fonction scal_matrice qui prend en argument une matrice (de dimensions quelconques) et
un flottant, et calcule le produit de cette matrice par ce flottant.
Exercice 6.31 ** Dans cet exercice, on programme différents jeux d’alignements dans une grille.
1 Écrire un programme qui permet à deux joueurs humains de jouer au Tic-tac-toe. Le programme vérifiera
que les coups joués sont valides et détectera la victoire d’un joueur ou la partie nulle.
2 Modifier ce programme pour qu’il joue de façon aléatoire contre un joueur humain.
3 Modifier ce programme pour qu’il joue de façon suffisamment stratégique pour ne jamais perdre, qu’il
soit premier ou second à jouer.
4 Écrire un programme qui permet à deux joueurs humains de jouer au Morpion : la grille de jeu est
cette fois-ci rectangulaire, de dimensions choisies en début de partie et il faut réaliser un alignement
de 5 pions pour gagner.
Livre_silo 30 août 2013 16:32 Page 170
es
Informatique pour tous
170
l
rol
Exercice 6.32 * Écrire une variante de la fonction copie_matrice qui copie une matrice dont les éléments
sont des tableaux, de manière à ce que les éléments ne soient pas partagés entre la matrice et sa copie.
(Note : il existe en Python une fonction de bibliothèque copy.deepcopy qui effectue une copie récursive
des valeurs.)
Exercice 6.33 Écrire une fonction qui multiplie deux matrices en utilisant la fonction transpose et le
Ey
produit scalaire (voir exercice 6.27 ci-dessus).
Exercice 6.34 * Écrire une fonction qui élève une matrice carrée à la puissance n en réutilisant l’idée de
l’exercice 4.25. Quelle est sa complexité ?
Exercice 6.35 * Déduire de l’exercice précédent une fonction qui calcule le n-ième élément de la suite
de Fibonacci (voir exercice 5.24) avec log n produits matriciels, en utilisant l’identité suivante :
( )n ( )
1 1 Fn+1 Fn
= .
t
1 0 Fn Fn−1
gh
Exercice 6.36 Écrire une fonction construire_matrice(n, p, f) qui renvoie la matrice de dimensions
(n, p) dont le terme général est f(n, p).
i
pyr
Co
Livre_silo 30 août 2013 16:32 Page 171
l es
rol
Troisième partie
t Ey
Ingénierie numérique
et simulation
gh
Dans cette partie, nous nous intéressons à des méthodes numériques pour la
résolution de systèmes linéaires (chapitre 7), d’équations sur les réels (cha-
i
pyr
L’idée pénible à garder à l’esprit est que, en calcul numérique, tout est faux !
En effet, les données prises en entrée sont des approximations des don-
nées « réelles » (parce qu’issues d’autres calculs ou de mesures physiques).
De plus, on résout des équations qui sont une approximation de la vie réelle
(linéarisation d’un phénomène en physique...) et on applique pour cela des
schémas qui introduisent une erreur dans le résultat. Enfin, le moindre cal-
cul en arithmétique flottante induit des erreurs d’arrondis, on note le résul-
tat final sur un morceau de papier, et on se trompe en copiant la deuxième
décimale.
Nous montrons sous quelles conditions on peut contrôler ces erreurs, et
comment choisir les paramètres des méthodes utilisées pour obtenir un ré-
sultat satisfaisant.
Livre_silo 30 août 2013 16:32 Page 172
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 173
l es
rol
7
Pivot de Gauss et
t Ey
gh
résolution de systèmes
i
pyr
Livre_silo 30 août 2013 16:32 Page 174
es
Informatique pour tous
174
l
rol
La résolution d’un système linéaire est une activité formatrice pour un étudiant commen-
çant des études scientifiques. D’une part, c’est l’occasion de réfléchir à des notions clés
telles que les équivalences (leur validité, leur pertinence), la description d’un ensemble de
solutions, et même la notion d’équation : que signifie « résoudre ax = b » ? D’autre part,
il s’agit de perdre la mauvaise habitude fréquente consistant à « bricoler » les équations
Ey
pour arriver à une solution ¹ dont on prétend avec plus ou moins de conviction que c’est la
solution.
L’algorithme du pivot de Gauss sert à résoudre un système linéaire au sens où, partant d’un
système à n équations et p inconnues, il va fournir un système équivalent permettant de
paramétrer l’ensemble des solutions (s’il est non vide), ou de démontrer qu’il n’y a pas de
solution en fournissant une condition nécessaire non compatible. Point important d’un
t
tel algorithme : il ne laisse aucune place à l’astuce, on se contente d’exécuter des tâches
répétitives mais simples, comme lorsqu’on a appris à additionner ou multiplier des en-
gh
tiers en primaire. L’expérience montre que le plus difficile reste d’accepter de changer de
« méthode », pour peu qu’on puisse nommer ainsi ce qu’on pratiquait en général face à un
système linéaire !
L’algorithme du pivot de Gauss, essentiellement basé sur des transvections (opérations sur
les lignes, de la forme Lj ← Lj − λLi ) est très simple à mettre en œuvre, du moins
i
lorsqu’il n’y a pas de paramètres formels, et il possède une complexité raisonnable, à savoir
pyr
— décomposition de Bruhat) ;
• calculer le déterminant de A ∈ Mn (R) ;
• calculer le rang de A ∈ Mn,p (R) ;
• déterminer deux matrices inversibles P et Q telles que P AQ = Jr , avec r le rang de A ;
• etc.
Nous serons confrontés principalement à deux problèmes :
• La précision du résultat : elle dépend évidemment de celle des données, mais même avec
des données exactes, les erreurs d’arrondis peuvent induire des erreurs dans le résultat,
plus ou moins importantes selon la méthode choisie.
• La comparaison d’un réel à zéro : les calculs avec les flottants induisent des erreurs, qui
peuvent faire apparaître ou au contraire disparaître le réel nul (voir chapitre 2). Comparer
un coefficient à zéro n’a donc pas grand sens, alors que dans l’algorithme du pivot de
Gauss, il est crucial de s’assurer que le pivot en est bien un, c’est-à-dire qu’il est non nul !
1. En fait, plutôt un « candidat-solution », les bricolages fournissant des conditions nécessaires sur les incon-
nues... conditions dont on ne sait pas si elles sont suffisantes pour que les équations initiales soient vérifiées.
Livre_silo 30 août 2013 16:32 Page 175
es
7 – Pivot de Gauss et résolution de systèmes
175
l
rol
7.1 Résolution de AX = Y : principe du pivot
L’algorithme du pivot de Gauss sait résoudre des systèmes généraux à n équations et p in-
connues. Pour simplifier, on se placera pour l’essentiel de l’exposé dans le cas où n = p,
avec l’hypothèse supplémentaire de l’existence d’une et une seule solution. On parle de
Ey
système de Cramer. Dans les exercices, on s’autorisera à sortir un peu de ce cadre.
Exercice 7.1 Proposer un système à deux équations et deux inconnues ne possédant aucune solution,
puis un autre possédant une infinité de solutions.
1
−2y + 3z + 2 = −14
x =
2
pyr
⇐⇒ y = 21
z = 4
Dès maintenant, le lecteur doit bien comprendre le rôle de l’équivalence, qui assure que le
système initial possède une unique solution. Les exercices 7.2 et 7.3 précisent la pertinence
puis la validité de ces équivalences.
Co
Exercice 7.2 Dans les équivalences précédentes, quelles sont les implications qui assurent l’existence
d’une solution ? Lesquelles donnent l’unicité ?
Exercice 7.3 Expliquer pourquoi, lors des substitutions, on a bien gardé des systèmes équivalents entre
eux.
La résolution d’un système linéaire passera systématiquement par une première étape pour
le mettre sous forme triangulaire en gardant l’équivalence avec le système initial. Si une
équation « disparaît » ou bien fournit une contradiction, alors on peut en conclure que :
• Si l’équation 0 = 0 apparaît, on peut l’éliminer du système en gardant l’équivalence.
• Si l’équation 0 = β apparaît (avec β ̸= 0), le système initial n’admet pas de solution
(sens ⇒ de l’équivalence).
Pour un système de Cramer, ces situations ne se présenteront pas.
La résolution d’un système triangulaire sera la dernière étape (la plus simple et rapide) de
la résolution d’un système linéaire. On l’appelle souvent phase de remontée : on résout les
Livre_silo 30 août 2013 16:32 Page 176
es
Informatique pour tous
176
l
rol
équations de bas en haut, en substituant aux inconnues les valeurs trouvées dans les lignes
inférieures.
Ey
On peut être confronté à des systèmes faisant intervenir des paramètres : si on rencontre
l’équation 0 = β où β est un paramètre (ou une combinaison de paramètres), il faut alors
commencer une discussion, qui aboutit en général à différentes conclusions. Ne serait-ce
que pour une équation aussi simple que ax = b, l’inconnue étant x, on peut être amené
à discuter sur a = 0. Dans le cadre d’une résolution numérique de système linéaire, on ne
rencontrera pas cette situation.
t
7.1.2 Les transvections
gh
Pour mettre un système sous forme triangulaire, on va réaliser des transvections, c’est-à-dire
des opérations élémentaires de la forme suivante : « ajouter tant de fois telle équation à telle
autre », afin d’éliminer ² des inconnues dans des équations... mais pas dans le désordre !
On va décrire ces opérations pour un système (3, 3) d’inconnues (x, y, z) dans une situa-
i
pyr
e
constitué des lignes L1 , L′2 et L′3 − L′2 est équivalent au premier système et triangulaire :
d
on est ramené à un cas qu’on sait traiter.
Lors de la première étape, on ne touche pas à la première ligne. De même, à la deuxième
étape, on ne touche ni à la première ni à la deuxième ligne, et ainsi de suite.
Dans l’exemple qui suit, on adopte une notation classique : pour dire qu’on change la
seconde ligne L2 en L′2 = L2 + αL1 , on préférera noter L2 ← L2 + αL1 . Cela signifie
qu’on appelle désormais L2 ce que désignait auparavant L2 + αL1 . Après cinq opérations
de ce type, on parlera donc toujours de L8 plutôt que de L′′′′′
8 .
Livre_silo 30 août 2013 16:32 Page 177
es
7 – Pivot de Gauss et résolution de systèmes
177
l
rol
Exemple 2.
− 3z
2x −
2x + 2y = 2 + 2y 3z = 2
L2 ←L2 +L1
−2x − y − 3z = −5 ⇐⇒ 1y − 6z = −3
L3 ←L3 −3L1
Ey
6x + 4y + 4z = 16 − 2y + 13z = 10
− 3z
2x + 2y = 2
L3 ←L3 +2L2
⇐⇒ y − 6z = −3
z = 4
⇐⇒ ...
t
Dans les cas moins favorables, on peut rencontrer en cours de résolution d’un système
gh
(3, 3) ces trois problèmes :
• Le pivot n’est pas là où on veut : si, à la première étape, le coefficient en x de la première
ligne est nul, on peut échanger la première équation avec la deuxième ou la troisième. De
même, si à la seconde étape, le coefficient en y (futur pivot) est nul, on peut échanger la
deuxième équation avec la troisième, mais pas la première (se souvenir qu’on veut arriver
i
à un système triangulaire : il ne faut pas faire réapparaître x dans les deux dernières
pyr
équations).
• Il n’y a plus de pivot en une variable : si tous les coefficients en x sont nuls (c’est rare :
cela revient à dire que x n’apparaît pas dans le système...), on peut prendre y ou z comme
première variable. De même, si après la première étape, y n’apparaît ni dans la deuxième
ni dans la troisième équation, on peut prendre z comme deuxième inconnue.
• Il n’y a plus de pivot : cela signifie que les membres de gauche des équations restantes
sont nuls. Selon que les membres de droite correspondants sont nuls ou pas, ces équa-
Co
Livre_silo 30 août 2013 16:32 Page 178
es
Informatique pour tous
178
l
rol
Avec des flottants, il n’en va pas de même, comme on l’a vu au chapitre 2.
( )
1 1
Exercice 7.4 Que vaut 12 − − 1 ? Et qu’en pense Python ?
3 4
In [1]: 12*(1./3-1./4)-1
Ey
Out[1]: -2.220446049250313e-16
En pratique :
• Les calculs approchés peuvent faire apparaître des termes petits, mais non nuls... censés
pourtant représenter le réel nul. Ceci va transformer en système de Cramer des systèmes
qui n’en étaient pas, ou conduire (même pour un système de Cramer) à prendre comme
pivot une quantité très faible issue de l’accumulation d’approximations. Même pour un
t
système de Cramer, le résultat annoncé n’aura alors plus aucun sens ³.
• Ces termes infinitésimaux risquent en outre d’être utilisés comme pivots : on prendra
gh
leur inverse, ce qui produira de grands flottants, eux-mêmes évidemment sans signifi-
cation.
• A contrario, on peut voir apparaître des coefficients nuls, alors que les objets mathéma-
tiques qu’ils représentent ne le sont pas.
Ainsi, le système suivant n’a pas de solution :
i
pyr
x + 1/4y + z = 0
x + 1/3y + 2z = 0
y + 12z = 1
Pourtant, sa résolution numérique fournira un résultat, présenté à tort comme une solu-
1 1/4 1
Co
tion. De même, la matrice
1 1/3 2 est de rang 2, mais son calcul numérique par
0 1 12
pivot risque de donner 3 comme résultat :
In [2]: resolution([[1,1./4,1],[1,1./3,2],[0,1,12]],[[0],[0],[1]])
Livre_silo 30 août 2013 16:32 Page 179
es
7 – Pivot de Gauss et résolution de systèmes
179
l
rol
Pourtant, lors de sa résolution numérique par pivot de Gauss, après deux transvec-
tions, la troisième équation aura son membre de gauche nul. De même, la matrice
1 1 + 1015 1
1 1 + 10−15 2 est de rang 3, mais son calcul numérique par pivot risque de donner
Ey
0 1015 1
2 comme résultat :
In [3]: rank(array([[1,1+10**15,1],[1,1+10**(-15),2],[0,10**15,1]]))
Out[3]: 2
On retrouve ainsi le simple exemple de l’exercice 7.4 : tester la nullité d’un flottant (ou
t
l’égalité de deux flottants) est fatalement trompeur et dangereux.
gh
ATTENTION L’égalité et les flottants
Tester l’égalité de deux flottants n’a presque jamais de sens, est toujours risqué et doit donc
être évité... sauf bonnes raisons, qui sont alors à expliciter !
i
Pour le problème auquel on s’intéresse, les tests de nullité sont effectués pour trouver un
pyr
pivot. Si on sait que le système est de Cramer, on peut chercher sur la colonne en cours
le coefficient le plus élevé en valeur absolue (ou module). Cela n’interdit pas les mauvaises
surprises ⁴, mais cela marche en pratique plutôt bien. Cela s’appelle la méthode du pivot
partiel, qu’on va exposer dans la section suivante.
On fait ici l’hypothèse que le système initial est de Cramer. Les autres cas seront décrits
en cours de mathématiques. Il est important de noter que les opérations réalisées vont
introduire des systèmes équivalents au premier, qui demeureront donc des systèmes de
Cramer.
Comme signalé plus haut, on veut éliminer des variables dans les équations successives.
On va donc faire en sorte qu’après k étapes, pour tout i entre 1 et k, la i-ème variable ait
disparu de toutes les équations du système à partir de la (i + 1)-ème. Ce sera l’invariant
de boucle.
Ainsi, après la (n − 1)-ème étape, le système sera bien sous forme triangulaire.
Livre_silo 30 août 2013 16:32 Page 180
es
Informatique pour tous
180
l
rol
Dans le pseudo-code qui suit, on résout le système Ax = y. La ligne Li désigne à la fois
les coefficients de A (qui sont dans une matrice, un tableau bidimensionnel) et les seconds
membres, qui sont dans une matrice colonne y. Les indexations de tableaux vont de 0
à n − 1 comme en Python :
Ey
pour i de 0 à n − 2 faire
Trouver j entre i et n − 1 tel que |aj,i | soit maximale.
Échanger Li et Lj (coefficients de la matrice et membres de droite).
pour k de i + 1 à n − 1 faire
ak,i
Lk ← Lk − Li
ai,i
t
Rechercher j entre i et n tel que |aj,i | soit maximale (puis échanger deux lignes) a deux
objectifs : d’une part s’assurer que le coefficient en position (i, i) sera différent de 0 (c’est
gh
essentiel pour pouvoir pivoter) et, d’autre part, minimiser les erreurs numériques dans la
suite du calcul.
Arrivé ici, le système est sous forme triangulaire et il n’y a plus qu’à « remonter », via des
substitutions. Le résultat est mis dans un tableau x et il s’agit donc de calculer :
( )
i
1 ∑
n−1
yi −
pyr
xi = ai,k xk .
ai,i
k=i+1
pour i de n − 1 à 0 faire
pour k de i + 1 à n − 1 faire
yi ← yi − ai,k xk
yi
xi ←
Co
ai,i
Exercice 7.5 Montrer que le caractère « de Cramer » d’un système ne dépend pas des membres de droite
des équations.
Livre_silo 30 août 2013 16:32 Page 181
es
7 – Pivot de Gauss et résolution de systèmes
181
l
rol
Un système linéaire peut être vu comme une équation matricielle : le système de l’exemple 2
2 2 −3 x 2
se traduit par AX = Y , avec A = −2 −1 −3, X = y et Y = −5
.
6 4 4 z 16
Ey
2 2 −3 2
Il est équivalent au système U X = Y , avec U = 0 1 −6 et Y = −3
′ ′
.
0 0 1 4
Lorsque i ̸= j, faire l’opération élémentaire Li ← Li +λLj revient à multiplier (à gauche)
la matrice A par la matrice de transvection Ti,j (λ) de terme général :
t
gh
1 si k = l,
tk,l = λ si k = i et l = j,
0 sinon.
De même, des transvections sur les colonnes (inutiles pour les systèmes de Cramer) cor-
i
pyr
S
équivalent à X = Y ′′ : on a bien résolu le système.
Si enfin on s’intéresse à A supposée inversible sans chercher à résoudre AX = Y , on
adapte facilement la méthode de résolution de cette dernière pour expliciter T inversible
telle que T A soit triangulaire supérieure, puis S telle que ST A = In : la matrice ST est
alors l’inverse de A.
Exercice 7.6 Vérifier que l’opération élémentaire Li ← Li + λLj revient à multiplier A à gauche par
Ti,j (λ). (Presque) sans calcul, démontrer que Ti,j (λ) est inversible et déterminer son inverse.
Exercice 7.7 Interpréter les échanges de lignes Li ↔ Lj et les dilatations Li ← λLi (avec λ ̸= 0)
comme des multiplications par des matrices particulières.
Livre_silo 30 août 2013 16:32 Page 182
es
Informatique pour tous
182
l
rol
POUR ALLER PLUS LOIN Presque toutes les matrices ont une décomposition LU .
Tout d’abord, si on prend des coefficients aléatoires (en différents sens raisonnables), on
obtient avec probabilité 1 un système de Cramer (une matrice inversible).
Encore mieux : on n’aura en général pas de problème de pivot ! Plus précisément, si les
Ey
mineurs principaux (les matrices (k, k) « en haut à gauche » extraites de la matrice initiale)
sont tous inversibles, alors à chaque étape k du pivot ⁵, le coefficient présent en position
(k, k) est non nul, et on peut l’utiliser pour pivoter.
Ainsi, partant d’une matrice A, on trouve une matrice L1 triangulaire inférieure, telle que
U = L1 A soit triangulaire supérieure. Si on note L = L−1
1 , on a la décomposition A = LU
(L pour Lower et U pour Upper).
Si par malheur on trouve à l’étape k le coefficient ak,k qui est nul, alors on prend le plus
petit j > k tel que aj,k soit non nul et on l’utilise comme pivot pour placer des zéros des-
t
sous et à droite par des transvections sur les colonnes. On parvient ainsi, modulo quelques
dernières dilatations, à construire deux matrices L et U respectivement triangulaires infé-
gh
rieure et supérieure, telles que A = LTσ U , avec Tσ une matrice de permutation : c’est la
décomposition de Bruhat ⁶.
Les matrices L et U ne sont pas uniques, mais la permutation σ l’est. Dans la partition de
GLn (R) en n! composantes, celles associées à l’identité — c’est-à-dire celles possédant une
décomposition LU — constituent la « grosse cellule » : c’est un ouvert dense.
i
pyr
Livre_silo 30 août 2013 16:32 Page 183
es
7 – Pivot de Gauss et résolution de systèmes
183
l
rol
Les échanges de lignes se passent de commentaires :
def echange_lignes(A, i, j):
nc = len(A[0])
for k in range(nc):
A[i][k], A[j][k] = A[j][k], A[i][k]
Ey
Enfin viennent les transvections. Comme pour la fonction précédente, on agira par effet
de bord sur la matrice fournie en entrée, en ne renvoyant rien. En particulier, l’appel se
fera via transvection_ligne(A,i,j,mu), et non via A = transvection_ligne(A,i,j,mu) :
def transvection_ligne(A, i, j, mu):
""" L_i <- L_i + mu.L_j """
nc = len(A[0]) # le nombre de colonnes
for k in range(nc):
A[i][k] += mu * A[j][k]
t
Exercice 7.8 Pourquoi µ et pas λ ? :-)
gh
7.2.2 Recoller les morceaux
Grâce à tous ces outils, l’écriture du programme Python devient comme prévu un simple
exercice de traduction. On commence par faire une copie de la matrice fournie en entrée ⁷.
i
pyr
å
7. La fonction copie_matrice a été écrite dans le chapitre précédent ; on peut la remplacer par la fonction
deepcopy de la bibliothèque copy.
Livre_silo 30 août 2013 16:32 Page 184
es
Informatique pour tous
184
l
rol
# Phase de remontée
X = [0.] * n
for i in range(n-1, -1, -1):
X[i] = (Y[i][0]-sum(A[i][j]*X[j] for j in range(i+1,n))) / A[i][i]
return X
Ey
EN PRATIQUE Dans les versions Python 2.x
La conversion de ai,i en flottant est nécessaire si la matrice en entrée est constituée d’en-
tiers, car en Python 2.x, la division est vue comme une division entière : c’est le quotient
dans la division euclidienne qui est renvoyé et non le quotient flottant.
Le choix qui a été fait dans Python 3.x est le suivant : l’opérateur / voit ses paramètres
comme des flottants et renvoie un flottant. Ce choix de conception est pragmatique dans ce
t
contexte, mais il fait perdre un peu en généricité. Par exemple, une résolution manipulant
des rationnels « exacts » devrait être écrite sans cette conversion.
gh
Il est à noter enfin que la phase de remontée a été accélérée grâce à l’utilisation de la
fonction sum de Python. Le programme complet est donné programme 4 ci-après.
In [4]: resolution([[2,2,-3],[-2,-1,-3],[6,4,4]],[[2],[-5],[16]])
pyr
Si on tente de résoudre les systèmes suivants, qui ne sont pas de Cramer, on a bien entendu
quelques ennuis :
x + 2y = 1 x + 2y = 1
2x + 4y = 1 2x + 4y = 2
Co
In [5]: resolution([[1,2],[2,4]],[[1],[1]])
In [6]: resolution([[1,2],[2,4]],[[1],[2]])
Livre_silo 30 août 2013 16:32 Page 185
es
7 – Pivot de Gauss et résolution de systèmes
185
l
rol
PROGRAMME 4 Pivot de Gauss
def chercher_pivot(A, i):
n = len(A) # le nombre de lignes
j = i # la ligne du maximum provisoire
Ey
for k in range(i+1, n):
if abs(A[k][i]) > abs(A[j][i]):
j = k # un nouveau maximum provisoire
return j
A, Y = copie_matrice(A0), copie_matrice(Y0)
n = len(A)
pyr
assert len(A[0]) == n
# Mise sous forme triangulaire
for i in range(n):
j = chercher_pivot(A, i)
if j > i:
echange_lignes(A, i, j)
echange_lignes(Y, i, j)
for k in range(i+1, n):
Co
x = A[k][i] / float(A[i][i])
transvection_ligne(A, k, i, -x)
transvection_ligne(Y, k, i, -x)
# Phase de remontée
X = [0.] * n
for i in range(n-1, -1, -1):
X[i] = (Y[i][0]-sum(A[i][j]*X[j] for j in range(i+1,n))) / A[i][i]
return X
Livre_silo 30 août 2013 16:32 Page 186
es
Informatique pour tous
186
l
rol
En voici une utilisation élémentaire, qui reprend la résolution de l’exemple 1 :
In [7]: numpy.linalg.solve([[2,2,-3],[-2,-1,-3],[6,4,4]],[[2],[-5],[16]])
On note que le résultat renvoyé est un array, mais que numpy accepte en entrée des listes de
Ey
listes ⁸. La précision peut sembler meilleure qu’avec le programme précédent, mais c’est en
fait un leurre d’affichage :
In [8]: numpy.linalg.solve([[2,2,-3],[-2,-1,-3],[6,4,4]],[[2],[-5],[16]]) [0][0]
Out[8]: -14.000000000000023
D’une manière générale, on peut faire le pari que les résolutions effectuées par numpy seront
plus performantes que celles que l’on va coder soi-même sans finesse, tant pour la qualité
t
du résultat que pour le temps de calcul.
Pour autant, on évitera de s’y fier comme à une vérité indiscutable. Un crash-test classique ⁹
gh
en calcul numérique matriciel consiste à inverser les matrices de Hilbert. On verra que ces
matrices sont très mal conditionnées et que tout calcul numérique portant sur de telles
matrices peut induire de grosses erreurs d’approximation.
La matrice de Hilbert d’ordre n ∈ N∗ est la matrice Hn ∈ Mn (R) de terme général
1
pour 1 ⩽ i, j ⩽ n. On peut la définir par exemple ainsi (en pensant au
i
hi,j = i+j−1
décalage d’indice) :
pyr
def hilbert(n):
return [[1./(i+j+1) for j in range(n)] for i in range(n)]
En notant C la matrice de Mn,1 (R) avec des zéros partout, sauf un « 1 » en dernière
position, la résolution de Hn X = C va renvoyer théoriquement la dernière colonne de
Hn−1 . Un résultat classique est le caractère entier de Hn−1 . Par exemple, la « dernière »
−1
composante de H10 (calculée avec un logiciel de calcul formel) est 44 914 183 600 :
Co
In [10]: resolution(hilbert(10),Y)[9]
Out[10]: 44909661051.85882
In [11]: numpy.linalg.solve(hilbert(10),Y)[9][0]
Out[11]: 44908633925.999527
8. D’une manière générale, Python est plutôt de bonne composition avec les types !
9. [Higham], chapitre 28, explique que c’est plus subtil.
Livre_silo 30 août 2013 16:32 Page 187
es
7 – Pivot de Gauss et résolution de systèmes
187
l
rol
Voici les temps de calcul en secondes, sur un ordinateur de puissance moyenne, de la ré-
solution de Hn X = C, pour différentes valeurs de n :
n 50 100 200 400 800
Ey
numpy.linalg.solve 0.0012 0.0034 0.014 0.065 0.37
On reviendra dans le paragraphe suivant sur l’évolution de ces temps de calcul en fonction
de n mais on peut déjà constater que numpy est très rapide. En réalité, les fonctions de cette
bibliothèque ne sont pas écrites en Python mais font plutôt appel à d’autres bibliothèques
très optimisées écrites en C ou Fortran.
t
gh
7.3 Complexité
On s’intéresse ici à la complexité de l’algorithme du pivot de Gauss, c’est-à-dire au nombre
d’opérations élémentaires effectuées lors de la résolution d’un système à n équations et
n inconnues. Cette complexité dépend bien entendu de n ; mais comment ?
i
pour i de 0 à n − 2 faire
Trouver j entre i et n − 1 tel que |aj,i | soit maximale.
Échanger Li et Lj (coefficients de la matrice et membres de droite).
pour k de i + 1 à n − 1 faire
Co
ak,i
Lk ← Lk − Li
ai,i
On va dans un premier temps évaluer cette complexité de façon assez fine et on donnera
ensuite une version « allégée », largement suffisante en première approximation ¹⁰.
Pour chaque valeur de i ∈ J0, n − 2K :
• La recherche de j coûte n − i comparaisons.
• L’échange éventuel des deux lignes coûte 2n + 2 affectations.
• Pour chaque valeur de k entre i + 1 et n − 1, la transvection coûte 2n + 2 affectations
et autant de divisions, multiplications et soustractions.
Livre_silo 30 août 2013 16:32 Page 188
es
Informatique pour tous
188
l
rol
Si on décide de désigner par opération élémentaire chaque comparaison, affectation ou opé-
ration arithmétique sur les flottants, le coût à i fixé est majoré par (n − i) + (2n + 2) +
(n − 1 − i)(2n + 2), c’est-à-dire (2n + 3)(n − i). En sommant sur les i ∈ J0, n − 1K, on
n(n + 1) 5 3
trouve donc un coût majoré par (2n + 3) = n3 + n2 + n. Comme d’habitude
2 2 2
Ey
on ne s’intéresse qu’au terme dominant : la complexité dans le pire des cas est équivalente
à n3 .
Pour être plus rapide sans réellement perdre en pertinence, on peut se contenter de dire :
« Pour chaque i, la recherche du pivot puis l’échange de ligne ont une complexité linéaire en n,
ainsi que chacune des transvections (il y en a au plus n), d’où une complexité quadratique à i fixé.
Puisque i décrit J0, n − 2K, on a finalement une complexité en n3 . »
t
7.3.2 Phase de remontée
gh
Voici la deuxième et dernière phase :
pour i de n − 1 à 0 faire
pour k de i + 1 à n − 1 faire
yi ← yi − ai,k xk
i
yi
xi ←
pyr
ai,i
On reprend maintenant les résultats chronométrés de la section 7.2.3. Lorsque n est mul-
tiplié par 2 :
• Le temps de calcul de la fonction resolution est multiplié à peu près par 8 : il s’agit bien
d’une complexité cubique.
• Celui de solve est multiplié par quelque chose de beaucoup plus fluctuant, mais sensi-
blement inférieur à 8. La documentation en ligne de Python assure que cette résolution
est déléguée à la bibliothèque Fortran LAPACK via une décomposition LU . Une telle dé-
composition est a priori cubique... On atteint les limites de l’analyse au chronomètre,
peu probante ici.
Livre_silo 30 août 2013 16:32 Page 189
es
7 – Pivot de Gauss et résolution de systèmes
189
l
rol
pour lesquels n est très grand, par exemple de l’ordre du milliard. Une résolution « en n3 »
est alors absolument exclue.
Ey
On peut retenir comme ordre de grandeur qu’un ordinateur personnel va réaliser de l’ordre
de 109 opérations élémentaires dans une minute. Par exemple, un algorithme en O(n2 )
s’exécutera en temps raisonnable si n = 104 , mais est à proscrire si n = 107 .
Pour une application industrielle dont le calcul peut durer de l’ordre de quelques jours
dans un centre de calcul dédié ¹¹, on peut imaginer un nombre d’opérations élémentaires
allant jusqu’à 1018 voire 1020 , mais certainement pas 1025 . Par ailleurs, si on doit traiter un
t
système à n équations et n inconnues en manipulant sa matrice à n2 entrées, la question de
la gestion de la mémoire va devenir problématique si n est par exemple de l’ordre de 106 .
gh
Ces systèmes ne sont donc en général pas traités avec une représentation « pleine » des
matrices associées.
On dispose de deux types d’améliorations :
• Des méthodes itératives permettent d’approcher les solutions. Dans le cadre fréquent
i
des matrices creuses (de taille n × n, mais avec un nombre d’entrées non nulles de
pyr
l’ordre de K.n), ces algorithmes ont en général un coût de l’ordre de αn2 voire moins,
avec α dépendant de la qualité de l’approximation souhaitée et des caractéristiques de la
matrice. Dans ces méthodes, on peut en plus paralléliser les calculs. C’est un avantage
intéressant.
• On peut tenter d’améliorer la complexité de l’inversion matricielle « exacte ».
Le premier point sera évoqué dans les exercices. Pour le second, un résultat est assez re-
Co
Démonstration. La première étape, simple, consiste à se ramener au cas des matrices sy-
( )−1 t
métriques définies positives via l’écriture A−1 = t A.A A. Ensuite, lorsque B est
symétrique définie positive, on l’écrit par blocs de tailles (n/2, n/2) puis on effectue le
calcul de B −1 en inversant récursivement différents blocs et leur complément de Schur. On
obtient ainsi un très joli algorithme « diviser pour régner »... dont le lecteur curieux trouvera
les détails par exemple dans [Cormen].
11. Les très gros ordinateurs actuels réalisent de l’ordre de 1015 opérations sur les flottants par seconde ; on
parle de « petaflops ».
Livre_silo 30 août 2013 16:32 Page 190
es
Informatique pour tous
190
l
rol
à nα avec α = ln2 (7) ≃ 2, 69 (algorithme de Strassen, 1972). Depuis, on a fait un peu
mieux, mais les constantes multiplicatives sont telles qu’elles rendent ces méthodes peu uti-
lisables. La question « Peut-on, pour tout α > 2, trouver un algorithme de multiplication
de complexité O(nα ) ? » reste en particulier ouverte.
Ey
Enfin, on peut noter que certaines matrices avec une géométrie particulière donnent lieu à
des résolutions de systèmes simplifiées. Par exemple, la matrice tridiagonale ¹² suivante :
2 −1 (0)
−1 2 −1
Vn =
..
.
..
.
..
.
t
−1 2 −1
gh
(0) −1 2
demande une seule transvection pour chaque pivot, d’où un calcul d’inverse (ou de ré-
solution de Vn X = Y ) de complexité quadratique. Cette « matrice de Virginie ¹³ » in-
tervient dans les schémas numériques de résolution d’équations différentielles telles que
i
∆f = g. Elle est très classique en analyse numérique. Des variantes « tridiagonales par
blocs » existent, en particulier pour résoudre les équations aux dérivées partielles en di-
pyr
mension 2 ou 3.
Livre_silo 30 août 2013 16:32 Page 191
es
7 – Pivot de Gauss et résolution de systèmes
191
l
rol
7.4 Conditionnement d’une matrice
Les aspects théoriques évoqués dans cette section ne sont pas formellement au programme
et nécessitent des connaissances de deuxième année.
Ey
Exercice 7.9 Résoudre les systèmes AX = Y , avec A = H5 (matrice de Hilbert, voir la section 7.2.3) et
−7.7 −7.7
−6 −6
Y =
−2.1, puis Y = −2.1. Que doit-on en conclure ?
−0.5 −0.4
0 0
t
7.4.1 Mesurer les propagations d’erreurs
gh
Comme cela a déjà été évoqué, le résultat d’une résolution de AX = Y comporte en
général des erreurs de trois types, même en suivant un algorithme à la lettre : celles sur la
donnée Y , celles sur la matrice A et enfin celles qui vont apparaître en cours d’exécution,
liées aux calculs en arithmétique flottante. À défaut de pouvoir supprimer ces erreurs, on
aimerait avoir un certain contrôle sur leur propagation.
i
Il est assez délicat de mesurer l’ensemble de ces erreurs, ce qui conduit à quelques raccour-
pyr
cis.
Même en imaginant des données connues exactement, ce qui est une hypothèse très op-
timiste, les premières opérations arithmétiques induiront possiblement une erreur relative
de l’ordre de 2−k , avec k la taille de la mantisse. Ceci est vrai pour A comme pour Y .
On cherche donc, ensuite, à contrôler l’erreur (relative) sur X à l’aide des erreurs relatives
sur A et Y .
Co
Pour cela, on munit Mn,1 (R) (assimilé à Rn ) d’une norme (par exemple la norme eucli-
dienne canonique) et Mn (R) de la norme subordonnée. On suppose que A0 X0 = Y0
(le problème initial) et A1 X1 = Y1 (le système qu’on a résolu), avec δA = A1 − A0
et δY = Y1 − Y0 respectivement petits devant A0 et Y0 . On souhaite, en notant
∥δX∥ |||δA||| ∥δY ∥
δX = X1 − X0 , majorer à l’aide de et ·
∥X0 ∥ |||A0 ||| ∥Y0 ∥
7.4.2 Le conditionnement
Observons ce qui se passe dans un cas très simple.
Exercice 7.10 Conditionnement : cas diagonal, erreur sur le membre de droite.
On suppose que A est une matrice inversible diagonale, A = Diag(λ1 , ..., λn ), avec de plus :
Livre_silo 30 août 2013 16:32 Page 192
es
Informatique pour tous
192
l
rol
Montrer :
∥δX∥ |λn | ∥δY ∥
⩽ × ·
∥X∥ |λ1 | ∥Y ∥
Exercice 7.11 Conditionnement : cas diagonal, erreur sur la matrice. On suppose que A et δA sont
des matrices diagonales avec les notations A = Diag(λ1 , ..., λn ) et δA = Diag(δλ1 , ..., δλn ), et de
Ey
plus :
0 < |λ1 | ⩽ · · · ⩽ |λn | .
On suppose : AX = Y , (A + δA)(X + δX) = Y et on munit Rn de la norme euclidienne canonique.
Montrer :
∥δX∥ |λn | |||δA|||
⩽ × ,
∥X + δX∥ |λ1 | |||A|||
avec ||| ||| la norme subordonnée à ∥ ∥.
t
Dans les exercices précédents, λn est la valeur propre la plus grande (en module), mais
son module est aussi la norme de A (pour la norme subordonnée à la norme euclidienne
gh
1
sur Rn ). De même, est égal à |||A−1 |||. Ce sont bien ces quantités qui, dans le cas
|λ1 |
général, vont permettre de quantifier les propagations d’erreurs dans les résolutions de
système.
i
Si A ∈ Mn (R) est inversible, son conditionnement, noté en général κ(A), associé à une
Avec cette définition, on retrouve dans le cas général les résultats vus plus haut dans un
cas particulier.
éorème. Contrôle des erreurs par le conditionnement.
Co
∥δX∥ ∥δY ∥
• Si AX = Y et A(X + δX) = Y + δY , alors ⩽ κ(A) ·
∥X∥ ∥Y ∥
∥δX∥ |||δA|||
• Si AX = Y et (A + δA)(X + δX) = Y , alors ⩽ κ(A) ·
∥X + δX∥ |||A|||
∥δX∥ |||δA|||
On peut même améliorer la dernière inégalité : ⩽ κ(A) (1 + O(|||δA|||)), le
∥X∥ |||A|||
grand O étant à prendre au sens de : lorsque δA tend vers 0.
Livre_silo 30 août 2013 16:32 Page 193
es
7 – Pivot de Gauss et résolution de systèmes
193
l
rol
Les faits suivants peuvent, pour la plupart, constituer de petits exercices de mathématiques
de seconde année.
• Les matrices d’homothéties λIn ont pour conditionnement ¹⁴ 1.
• Si A est orthogonale, alors κ(A) = 1.
Ey
• Pour une matrice diagonale inversible, le conditionnement est le rapport entre les mo-
dules maximal et minimal des éléments diagonaux.
• Pour une matrice symétrique réelle de valeurs propres (réelles, forcément) λ1 , ..., λn , le
conditionnement est le rapport entre le maximum et le minimum des |λi |.
On va terminer avec deux cas particuliers intéressants : les matrices de Hilbert et de Vir-
ginie, qu’on a évoquées dans les sections précédentes.
for n in [10,100,1000]:
t
spectre = numpy.linalg.eigvals(Virginie(n))
print(numpy.max(spectre)/numpy.min(spectre))
gh
48.3741500787
4133.6429268
406095.042659
Le spectre
( de Vn est en
) fait parfaitement connu : il est constitué des réels de la
i
kπ
forme 2 1 + cos , pour k décrivant J1, nK. On en déduit sans trop de mal que
n+1
pyr
4n2 4
κ(Vn ) ∼ 2 et, puisque 2 ≃ 0.405, les résultats expérimentaux semblent cohérents
π π
avec la théorie !
Exercice 7.12 À l’aide de la fonction eigvals de la sous-bibliothèque numpy.linalg, qui calcule les va-
leurs propres d’une matrice, déterminer le conditionnement de Hn , pour n ∈ J2, 20K. Ces résultats vous
semblent-ils fiables ?
for n in range(2,15):
Co
spectre = numpy.linalg.eigvals(Hilbert(n))
kappa = numpy.max(spectre)/numpy.min(spectre)
print(n,kappa,numpy.log(kappa)/n)
14. Ici, et pour les exemples suivants, le conditionnement est donné pour la norme subordonnée à la norme
euclidienne canonique de Rn .
Livre_silo 30 août 2013 16:32 Page 194
es
Informatique pour tous
194
l
rol
Les valeurs extrêmes du spectre des matrices de Hilbert sont relativement bien connues, mais les résultats
(surtout l’équivalent de la plus petite) sont « non élémentaires » à démontrer !
Exercice 7.13 Comment l’exercice 7.9 a-t-il été conçu ?
Ey
7.5 Exercices
Exercice 7.14 * Démontrer rigoureusement à l’aide d’un invariant de boucle que la première phase de
l’algorithme du pivot de Gauss conduit à un système sous forme triangulaire.
Exercice 7.15 Une façon naïve pour calculer le déterminant d’une matrice consiste à développer selon
une ligne ou colonne, ce qui amène à un calcul récursif coûteux en général.
1 Évaluer la complexité d’un tel algorithme.
t
2 Si un ordinateur peut effectuer 109 opérations sur les flottants par seconde, jusqu’à quelle dimension
peut-on espérer calculer un déterminant par cette méthode en un temps majoré par une journée ?
gh
Une autre façon de calculer le déterminant consiste à pivoter, pour se ramener à une matrice diagonale.
3 Expliciter cet algorithme à l’aide de pseudo-code. Évaluer sa complexité.
4 Programmer effectivement cet algorithme en Python.
5 Le tester, exhiber des cas limites mettant en défaut le programme.
Le lecteur voulant tester sa virtuosité en manipulation de tableaux avec numpy pourra programmer le calcul
naïf du déterminant et vérifier empiriquement la complexité.
i
Exercice 7.16 * En réalisant des opérations élémentaires sur les lignes (et/ou colonnes, mais on peut se
contenter d’opérations sur les lignes), on peut passer d’une matrice inversible quelconque A à la matrice
pyr
Livre_silo 30 août 2013 16:32 Page 195
es
7 – Pivot de Gauss et résolution de systèmes
195
l
rol
mat50.png mat50inv.png mat50invbis.png
t Ey Figure 7.1
La matrice initiale, son inverse « maison » et celle calculée avec linalg.inv
Le lecteur intrigué par les régularités de la matrice inverse pourra aller consulter [Appel].
gh
Exercice 7.19 * On a vu en section 7.1.5 que l’algorithme du pivot de Gauss conduit à une décomposition
« lower-upper » de certaines matrices. Cet exercice précise ce point.
1 Montrer que si les n mineurs principaux d’une matrice sont non nuls, alors dans l’algorithme du pivot
de Gauss (sans choix du module maximal), on trouve à chaque étape (qu’on appellera k) un élément
non nul en position (k, k) dans la matrice.
2 On suppose qu’à chaque opération sur les lignes de A (la matrice qu’on veut mettre sous la forme LU ),
i
pyr
rieure.
3 On suppose que dans la question précédente B = MN ...M2 M1 , les Mk étant associées à des opéra-
tions sur les lignes. Que vaut alors B −1 ? Et comment faire en sorte de calculer cette matrice B −1 à la
volée (pendant la mise sous forme triangulaire de A) plutôt qu’a posteriori ?
4 Écrire un programme Python prenant en entrée une matrice vérifiant les hypothèses faites plus
haut et renvoyant deux matrices L et U , respectivement triangulaires inférieure et supérieure, telles
que A = LU .
On pourra comparer le résultat avec celui proposé par la fonction lu du sous-module numpy.linalg.
5 Sans l’hypothèse faite sur les mineurs de A, montrer que si A est inversible, alors on peut trouver L
Co
15. Elle ne contient que des zéros, sauf un « 1 » par ligne et par colonne ; bref, on a permuté les lignes (ou
colonnes) de la matrice identité.
Livre_silo 30 août 2013 16:32 Page 196
es
Informatique pour tous
196
l
rol
Exercice 7.20 * Si A est une matrice réelle symétrique définie positive, alors il existe U triangulaire
supérieure telle que A = t U.U : c’est la décomposition de Choleski, qui est un cas particulier de la dé-
composition LU . Elle est unique si on impose aux coefficients diagonaux de U d’être strictement positifs,
ce qu’on va faire dans la suite.
Une façon de construire U consiste à calculer les coefficients ui,j à i croissant, puis (à i fixé) à j croissant.
1 En observant le coefficient (1, 1) du produit t U.U , donner la valeur nécessaire de u1,1 . De même,
Ey
donner les valeurs nécessaires de u1,j pour j ⩾ 2.
2 En continuant le procédé, donner des conditions nécessaires sur tous les ui,j pour avoir t U.U = A.
Vérifier qu’elles sont suffisantes.
3 Déduire de ce qui précède un algorithme calculant la décomposition de Choleski d’une matrice carrée.
4 Évaluer la complexité de cet algorithme en termes d’opérations arithmétiques élémentaires.
5 Programmer en Python la décomposition de Choleski.
6 Tester, comparer avec la fonction linalg.cholesky de numpy.
En changeant peu de choses, cet algorithme de Choleski permet de tester le caractère positif d’une matrice
symétrique... en espérant que la matrice n’ait pas de valeurs propres trop proches de 0.
t
gh
POUR ALLER PLUS LOIN Décomposition QR
Toute matrice carrée ¹⁶réelle peut se décomposer sous la forme QR, avec Q orthogonale
et R triangulaire supérieure. Cette décomposition a beaucoup d’applications théoriques...
mais aussi pratiques ! Par exemple, une fois la décomposition A = QR connue, résoudre
AX = Y se décompose en deux opérations plus simples : d’abord une résolution (qua-
i
dratique) de système triangulaire, puis une multiplication (elle aussi quadratique) par
Q−1 = t Q.
pyr
plication de départ avec une réflexion (symétrie orthogonale par rapport à un hyperplan)
pour que le premier vecteur soit envoyé sur un vecteur colinéaire à lui-même. On conti-
nue ensuite le travail récursivement.
• La méthode de Givens consiste, elle, à composer avec des « rotations ». Cette dernière
méthode est un peu plus coûteuse, mais est plus stable que la méthode de Householder.
Exercice 7.21 * On présente ici la méthode de Householder pour trouver une décomposition QR
de A ∈ Mn (R). Notons e1 le premier vecteur de la base canonique de Rn .
( )
α L
Si Ae1 est colinéaire à e1 , alors on peut écrire par blocs A = , avec A1 ∈ Mn−1 (R). Un
(0) A1
appel récursif fournit Q1 ∈ On−1
( (R) et R)1 ∈ Mn−1 ((R) triangulaire
) supérieure, telles que A1 = Q1 R1 .
1 (0) α L
Il suffit alors de prendre Q = et R = pour répondre au problème.
(0) Q1 (0) Q1
Si Ae1 n’est pas colinéaire à e1 , on va considérer v = Ae1 − ε ∥Ae1 ∥ e1 (avec ε ∈ {−1, 1} du signe de la
première composante de Ae1 ; condition imposée pour la stabilité). La réflexion par rapport à l’orthogonal
16. En fait, une adaptation de cette décomposition existe aussi (et est utile) pour les matrices rectangulaires
quelconques.
Livre_silo 30 août 2013 16:32 Page 197
es
7 – Pivot de Gauss et résolution de systèmes
197
l
rol
de v va alors envoyer Ae1 sur la droite engendrée par e1 . Cette réflexion a pour matrice dans la base
2
canonique S1 = In − v. t v. Ainsi, S1 A possède sa première colonne colinéaire à e1 et on est
∥v∥2
ramené au cas précédent.
1 Formaliser l’algorithme précédent en pseudo-code.
2 Écrire un programme Python implémentant cet algorithme.
Ey
3 Quelle est la complexité de cette méthode, en termes d’opérations arithmétiques ?
Exercice 7.22 ** On s’intéresse ici à la méthode de Jacobi, une méthode itérative de résolution approchée
de Ax = y, sous des conditions assez fortes ¹⁷ sur A.
Soit A ∈∑Mn (R) une matrice dont la diagonale est dominante, c’est-à-dire : pour tout i ∈ J1, nK,
|ai,i | > |ai,j |. Cette condition assure de façon classique le caractère inversible de A, donc le système
j̸=i
Ax = y est de Cramer.
t
On décompose A sous la forme A = D + H, avec D diagonale et H comportant des 0 sur la diagonale.
On a alors Ax = y si et seulement si Dx = −Hx + y, soit encore (les conditions sur A assurent que les
éléments diagonaux de D sont différents de 0) : x = −D−1 Hx + D −1 y. La méthode de Jacobi consiste
gh
alors à choisir un premier vecteur x0 quelconque (par exemple x0 = 0), puis définir une suite (xp )p∈N
par la relation de récurrence xp+1 = −D−1 Hxp + D−1 y. On démontre que sous les hypothèses faites
plus haut, la suite converge vers x, l’unique solution de Ax = y. En pratique, un point délicat consiste à
déterminer une condition raisonnable d’arrêt (calculer une infinité de termes est assez lassant).
1 On admet la formule suivante, qui permet de contrôler l’erreur ∥xp − x∥ à l’aide de la quantité
1 ∑
|ai,j | < 1 :
i
R = maxi
ai,i
j̸=i
pyr
Rp
∥x − xp ∥ ⩽ ∥x1 − x0 ∥
1−R
3 Quelle est la complexité de cet algorithme (en termes d’opérations arithmétiques élémentaires et fonc-
tion de n et ε0 ) ?
4 Programmer une fonction Python jacobi implémentant effectivement cet algorithme. Tester. Comparer
avec la fonction linalg.jacobi de numpy.
5 La matrice de Virginie n’a pas sa diagonale dominante (les inégalités requises sont strictes ¹⁸), mais on
K
peut montrer que le rayon spectral ¹⁹ de D−1 (Vn − D) est de la forme 1 − αn , avec αn ∼ 2 , ce qui
n
assure une convergence avec p bits significatifs en un nombre d’itérations de l’ordre de pn2 .
Quel est le coût d’une résolution de Vn X = Y à pn2 itérations ? On distinguera selon le mode de
représentation choisi : avec une matrice « pleine » (classique), ou en exploitant le caractère creux de Vn .
Programmer et expérimenter la méthode de Jacobi sur ce type d’exemple peut faire l’objet de TP très
riches.
17. Il existe une condition nécessaire et suffisante relativement précise qui assure la convergence, mais on a
choisi de présenter ici une condition suffisante simple.
18. Elle est tout de même faiblement dominante, avec des inégalités larges, au moins une stricte et le caractère
irréductible.
19. Ici : la plus grande valeur propre.
Livre_silo 30 août 2013 16:32 Page 198
es
Informatique pour tous
198
l
rol
POUR ALLER PLUS LOIN Méthode de Gauss-Seidel
La méthode de Jacobi est la plus élémentaire des méthodes itératives. Ces dernières, de
façon générique, partent d’une décomposition A = D + M , avec D « facilement » inver-
sible ²⁰, et on itère l’opération x ← −D−1 M x+D−1 y dans le but de s’approcher d’un point
fixe de l’application Z 7→ −D −1 M Z + D−1 y.
Ey
Pour la méthode de Jacobi, D est constituée uniquement des éléments diagonaux (avec
des zéros en dehors de la diagonale). Une variante est la méthode de Gauss-Seidel : il
s’agit simplement de prendre pour D la matrice constituée des éléments diagonaux et sous-
diagonaux de A. En pratique, cela revient à calculer la i-ème composante de x(k) en utili-
(k+1) (k)
sant les composantes xj déjà calculées pour j < i et les xj pour j ⩾ i. Cette simple
modification améliore la convergence.
La méthode de Gauss-Seidel est beaucoup plus délicate à paralléliser, mais ça reste possible
en pipelinant les calculs. C’est très technique, mais le jeu en vaut la chandelle !
t
gh
Exercice 7.23 * On s’intéresse ici au calcul du polynôme minimal d’une matrice carrée A ∈ Mn (K),
c’est-à-dire le polynôme unitaire P de plus petit degré tel que P (A) = 0. On sait qu’un tel polynôme
existe et est de degré majoré par n.
1 Un algorithme simple pour calculer le polynôme minimal d’une matrice A ∈ Mn (K) consiste à calculer
A2 , ..., An , placer leurs coefficients (ainsi que ceux de In ) dans une matrice B ∈ Mn2 ,n (K), pivoter
sur les colonnes jusqu’à obtenir une colonne nulle, fournissant ainsi une combinaison linéaire nulle non
i
pyr
b) Montrer qu’on obtient ainsi un calcul du polynôme minimal en O(n4 ) opérations élémentaires.
c) Expliquer pourquoi, numériquement, cet algorithme est voué à l’échec. ²¹
2 (Difficile) Proposer un algorithme probabiliste permettant d’obtenir en O(n3 ) opérations arithmétiques
le polynôme minimal d’une matrice « avec une forte probabilité »... en un sens à préciser !
On pourra tirer un vecteur au hasard et calculer le polynôme minimal de A vis-à-vis de ce vecteur.
Exercice 7.24 **** Pour rire.
∑ ∏
n
Le Permanent d’une matrice A ∈ Mn (K) est défini par : ai,σ(i) .
Co
σ∈Sn i=1
Trouver un algorithme calculant le permanent d’une matrice de Mn (K) en temps polynomial en n.
Livre_silo 30 août 2013 16:32 Page 199
l es
rol
8
Ey
Résolution numérique
t
gh
d’équations
sur les réels
i
pyr
Livre_silo 30 août 2013 16:32 Page 200
es
Informatique pour tous
200
l
rol
Deux méthodes vont être présentées :
• La méthode de dichotomie approche une solution avec p bits significatifs en p étapes,
ce qui est déjà très efficace. Ses conditions d’utilisation sont assez simples : on demande
seulement à la fonction d’être continue et de changer de signe. Ceci en fait une méthode
Ey
robuste.
• La méthode de Newton a une vitesse de convergence assez diabolique : à chaque étape
supplémentaire, le nombre de bits (ou décimales) significatifs correct(e)s est multiplié
par deux ! On atteint par exemple une précision de 50 bits en sept étapes. En contre-
partie, elle nécessite un ensemble de conditions d’application parfois délicat à vérifier.
Une bonne compréhension de ces deux méthodes aidera à faire un choix raisonné. Comme
dans le chapitre précédent, on programmera et testera ces algorithmes, puis on les com-
t
parera aux performances des fonctions fournies par la bibliothèque numpy.
gh
8.1 Méthode dichotomique
8.1.1 Principe théorique
i
Lorsque f est une fonction continue sur un intervalle [a, b], à valeurs réelles, avec f (a)
pyr
et f (b) de signe (large) opposé, le théorème des valeurs intermédiaires nous assure que f
s’annule entre a et b. Une démonstration élégante consiste à considérer la borne supérieure
de l’ensemble des x ∈ [a, b] tels que f (x) soit du signe de f (a) : il est assez simple de
montrer que f s’annule en ce point.
Si une telle démonstration est limpide à rédiger, la démonstration dichotomique a le bon
goût d’être constructive, au sens où elle fournit un algorithme pour approcher une solution.
Co
Il s’agit de construire par récurrence une suite de segments emboîtés, qui va « converger »
vers un singleton {l}, et on démontre alors que f (l) = 0. Le point crucial est de préserver
la propriété (ou invariant, si on pense en informaticien) :
f change de signe entre les deux extrémités du segment.
Exemple 1. Le cas de f : x 7→ x2 − 2 est simple et démonstratif.
• f (1) = −1 < 0 < 2 = f (2), donc l’équation f (x) = 0 possède une solution dans [1, 2].
La valeur médiane de cet intervalle est 1.5.
• f (1.5) = 0.25 > 0 > f (1), donc l’équation f (x) = 0 possède une solution
dans [1, 1.5].
• f (1.25) = −0.4675 < 0 < f (1.5), donc l’équation f (x) = 0 possède une solution
dans [1.25, 1.5].
• ...
• f (1.41430664062) ≃ 0.000263273715973 > 0 > f (1.4140625), donc l’équation
f (x) = 0 possède une solution dans [1.4140625, 1.41430664062].
Livre_silo 30 août 2013 16:32 Page 201
es
8 – Résolution numérique d’équations sur les réels
201
l
rol
On peut représenter ceci, en notant [an , bn ] le segment [c, d] après la n-ième étape :
t Ey
dichotomie.pdf
i gh
pyr
√ Figure 8.1
Les cinq premières itérations du calcul approché de 2 par dichotomie
2
On va ainsi mettre en place l’algorithme suivant :
Données : f, a, b, ε
c, d ← a, b
f _de_c, f _de_d ← f (c), f (d)
tant que d − c > 2ε faire
m ← (c + d)/2
f _de_m ← f (m)
si f _de_c × f _de_m ⩽ 0 alors
d←m
f _de_d ← f _de_m
sinon
c←m
f _de_c ← f _de_m
c+d
Résultat :
2
Livre_silo 30 août 2013 16:32 Page 202
es
Informatique pour tous
202
l
rol
c+d
Exercice 8.1 Pourquoi renvoyer plutôt que m ?
2
Ey
y a la fonction f . Cela ne pose pas de problème à un langage tel que Python, pour lequel
une fonction est (presque) un objet comme un autre.
Pour la correction, il faut montrer que le résultat renvoyé est un réel r tel que l’équation
i
f (x) = 0 possède une solution x0 telle que |x0 − r| ⩽ ε. La clé de la démonstration est
pyr
l’invariant suivant :
À chaque itération, on a f (c)f (d) ⩽ 0.
Pour démontrer cet invariant, on note qu’il est bien vérifié par hypothèse avant l’entrée dans
la boucle ¹. Ensuite (induction), on suppose la propriété vraie au début d’une itération. Si
f (c)f (m) ⩽ 0, alors on change la valeur de d en m sans toucher à c, donc on a bien
f (c)f (d) ⩽ 0 à la fin de l’itération (donc au début de la suivante). Si f (c)f (m) > 0, la
Co
valeur de d est inchangée et on remplace c par m. Puisque f (m) a le même signe (strict)
que f (c), le signe de f (c)f (d) ne change pas dans l’affectation c ← m et, comme cette
quantité était négative au début de l’itération, elle l’est encore à la fin de celle-ci.
Ainsi, le réel r renvoyé est le milieu d’un intervalle de longueur majorée par 2ε (condition
de sortie de boucle) et qui contient un zéro x0 de f (grâce à l’invariant de boucle et au
théorème des valeurs intermédiaires : la fonction en jeu était supposée continue). On a
alors bien |x0 − r| ⩽ ε.
Livre_silo 30 août 2013 16:32 Page 203
es
8 – Résolution numérique d’équations sur les réels
203
l
rol
La démonstration de la terminaison donne facilement cette complexité.
( En effet,
) il y a une
b−a b−a
k-ième itération si et seulement si k−1 > 2ε, c’est-à-dire k < ln2 . La boucle
⌈ 2( )⌉ ε
b−a
sera donc exécutée exactement ln2 − 1 fois.
ε
Ey
1
Pour avoir p bits significatifs, si la solution recherchée est de l’ordre de 1, on prend ε =
2p
et le nombre d’itérations requises est alors de l’ordre de p comme annoncé dans le préam-
bule.
c, d = a, b
pyr
Reprenons l’exemple 1. On pourrait définir une fonction def g(x): return x**2-2, mais c’est
inutile : Python offre la possibilité de définir des fonctions anonymes via l’opérateur lambda :
In [1]: math.sqrt(2), dichotomie(lambda x : x**2-2, 1, 2, 0.000001)
Pour le deuxième exemple, on utilise la fonction sin de la bibliothèque math qu’on aura
préalablement importée :
In [2]: math.pi, dichotomie(math.sin, 3, 4, 10**-10)
Livre_silo 30 août 2013 16:32 Page 204
es
Informatique pour tous
204
l
rol
de c et d dont l’image est la plus faible, on fait un appel de plus à f, mais on obtient de
façon presque certaine un résultat dont la précision s’améliorera lorsque ε diminuera (voir
figure 8.2).
t Ey
precision-dicho.pdf
i gh
pyr
Figure 8.2
Précision, en fonction du choix de la valeur renvoyée et du nombre d’itérations
Exercice 8.2 Remplacer la valeur renvoyée par une moyenne de c et d pondérée à l’aide de f (c) et f (d)
(pour privilégier celui dont l’image est la plus faible). Comparer avec les deux autres possibilités discutées.
Co
Livre_silo 30 août 2013 16:32 Page 205
es
8 – Résolution numérique d’équations sur les réels
205
l
rol
( )
1 2 √
exemple étudier la fonction g : x 7→ x+ . Elle stabilise [ 2, +∞[ et, sur cet
2 x √
intervalle, g(x) ⩽ x, avec égalité si et seulement si x = 2 ; etc.
√
Le fait que la dérivée de g en x0 = 2 soit nulle est fondamental : c’est grâce √ à cela
qu’on a une vitesse de convergence élevée. En effet, en posant δn = un − 2, on a
Ey
δn+1 g(un ) − g(x0 )
= −→ g ′ (x0 ), donc plus |g ′ (x0 )| est faible, meilleure est la vi-
δn un − x0 n→+∞
δ2 δ2
tesse de convergence. Ici, on a même plus précisément δn+1 = n ⩽ n ; on dit que la
2xn 2
convergence est quadratique, ou d’ordre 2.
t
i gh
pyr
newton.pdf
Co
√ Figure 8.3
Les deux premières itérations du calcul approché de 2 par la méthode de Newton. Difficile de
visualiser au delà !
1
Exercice 8.3 Avec les majorations précédentes, à partir de quel n est-on certain d’avoir |δn | ⩽ 1000
√ 2
(autrement dit les 1 000 premiers bits significatifs de 2) ?
1 1 1 1
On a δ1 ⩽ , donc 0 ⩽ δ2 ⩽ 2 (on a laissé de côté un facteur ), puis 0 ⩽ δ3 ⩽ 4 , puis par
2 2 2 2
1 1
récurrence immédiate : 0 ⩽ δn ⩽ n−1 pour tout n ⩾ 1. Pour avoir |δn | ⩽ 1000 , il suffit donc
2 2
d’avoir 2n−1 ⩾ 1000, c’est-à-dire n ⩾ 11. Relisez la conclusion : en 11 itérations, on obtient 1 000 bits
significatifs ; spectaculaire, non ?
Livre_silo 30 août 2013 16:32 Page 206
es
Informatique pour tous
206
l
rol
Cet algorithme d’extraction de racine était probablement connu des Babyloniens (voir en
fin de chapitre l’exercice 8.16), même s’il n’était évidemment pas question de dérivée, mais
plutôt d’un raisonnement géométrique.
Ey
L’algorithme général est celui décrit dans le cas particulier de la section précédente : on
part d’une première valeur et on suit la tangente ; on continue avec l’intersection de cette
tangente avec l’axe des abscisses (figure 8.4) jusqu’à ce que la différence entre deux termes
consécutifs soit assez petite. Il est nécessaire (on croise les doigts) de ne jamais rencontrer
de point en lequel la dérivée de f s’annule.
t
i gh
pyr
newton-generique.pdf
Co
Figure 8.4
f (un ) f (un )
Dans la méthode de Newton, f ′ (un ) = , donc un+1 = un − ′ ·
un − un+1 f (un )
Données : f, g, u0 , ε (g représente f ′ )
u ← u0
v ← u − f (u)/g(u)
tant que |v − u| > ε faire
u←v
v ← v − f (v)/g(v)
Résultat : v
Livre_silo 30 août 2013 16:32 Page 207
es
8 – Résolution numérique d’équations sur les réels
207
l
rol
Avant même de parler de complexité d’un algorithme, il faut démontrer sa terminaison et
sa correction, c’est-à-dire ici d’une part qu’il n’y aura pas de division par zéro, et d’autre
part que le résultat renvoyé r sera tel qu’il existe x0 zéro de f tel que |x0 − r| ⩽ ε.
Or, cet algorithme est un cauchemar pour l’informaticien :
Ey
• On peut rencontrer des divisions par zéro.
• La terminaison n’est pas assurée.
• Même si un résultat est renvoyé, il peut être éloigné d’un zéro de f .
En pratique, les programmes réalisant la méthode de Newton sont donc plus complexes.
Voici une condition suffisante simple qui assure la convergence (au moins mathématique, en
faisant abstraction des erreurs de calcul) : si f est de classe C 1 , convexe sur un intervalle I,
t
f possède un point d’annulation sur I et u0 ∈ I est tel que f (u0 ) > 0, alors la méthode
de Newton appliquée depuis le premier terme u0 est convergente vers un zéro de f .
gh
Cette condition peut sembler déraisonnablement compliquée. De fait, il ne faut pas espérer
une vérification automatique d’une telle propriété. Cependant, pour une fonction raison-
nable, la concavité/convexité locale est la règle : si f est de classe C 2 avec f ′′ (x0 ) ̸= 0, alors
f ′′ est de signe strict constant au voisinage de x0 . Ainsi, si on part de u0 assez proche d’un
zéro vérifiant cette condition, alors la méthode de Newton convergera vers ce zéro.
i
Il est à noter enfin qu’on trouve parfois la condition d’arrêt |un+1 − un | ⩽ ε remplacée
pyr
par une condition de la forme |f (un )| ⩽ ε′ , ce qui est une condition de même nature...
du moins si f ′ (x0 ) ̸= 0.
Exercice 8.4 Commenter cette condition d’arrêt, lorsque f ′ (x0 ) = f ′′ (x0 ) = 0 et f (3) (x0 ) ̸= 0.
Livre_silo 30 août 2013 16:32 Page 208
es
Informatique pour tous
208
l
rol
Toutefois, on peut gagner très facilement un ordre : si f est de classe C 3 et f (3) (x0 ) ̸= 0,
alors :
f (x0 + h) − f (x0 − h) f (3) (x0 ) 2
− f ′ (x0 ) ∼ h .
2h 12
Si f (3) (x0 ) = 0 on gagne évidemment encore un ordre. Ceci nous invite à appro-
Ey
cher f ′ (x0 ) via le programme suivant :
def derivee(f, x0, h):
return (f(x0+h) - f(x0-h)) / (2*h)
Dans l’exemple qui suit, on prend pour f la fonction exponentielle et on s’intéresse à des ap-
proximations de sa dérivée en 0, pour des valeurs de h allant de 1 à 2−30 . On représente fi-
f (h) − f (0) f (h) − f (−h)
gure 8.5 l’erreur |∆0 (h) − 1| avec ∆0 (h) = , puis ∆0 (h) = ·
t
h 2h
Les pentes sont bien celles espérées !
i gh
pyr
precision-derivees.pdf
Co
Figure 8.5
Comparaison entre les deux formules de dérivation discrète.
Livre_silo 30 août 2013 16:32 Page 209
es
8 – Résolution numérique d’équations sur les réels
209
l
rol
8.2.4 Mise en œuvre
Pour la version réalisée ici, on ne fait aucune vérification d’hypothèse de convergence, qu’on
laisse à l’utilisateur. La terminaison et la correction ne sont donc pas assurées.
Ey
PROGRAMME 6 Méthode de Newton
def newton(f, fp, x0, epsilon):
u = x0
v = u - f(u)/fp(u)
while abs(v-u) > epsilon:
u, v = v, v - f(v)/fp(v)
return v
t
gh
Dans des conditions réelles, à défaut de pouvoir s’assurer de la convergence, on vérifierait
à la volée que les valeurs prises par u et v ne sont pas trop grandes (ce qui signifierait qu’on
quitte le domaine dans lequel l’action est censée se passer) et/ou que celles prises par f ′ (v)
ne sont pas trop petites.
i
pyr
Out[4]: 1.4142156862745099
Exercice 8.6 Que se passe-t-il si dans le dernier appel on remplace 2. par 2 ? Attention, la réponse dépend
de la version de Python !
Livre_silo 30 août 2013 16:32 Page 210
es
Informatique pour tous
210
l
rol
t Ey
precision-newton.pdf
i gh
Figure 8.6
Vitesse de convergence de la méthode de Newton
pyr
Livre_silo 30 août 2013 16:32 Page 211
es
8 – Résolution numérique d’équations sur les réels
211
l
rol
8.3.2 Utiliser numpy/scipy
La plupart du temps, il est déraisonnable de coder soi-même une méthode de résolution
qui existe déjà et a probablement été plus testée et optimisée qu’une fonction « maison ».
Les bibliothèques numpy et scipy.optimize proposent les fonctions suivantes :
Ey
• La fonction numpy.roots détermine les racines d’un polynôme donné par la liste de ses
coefficients.
In [7]: numpy.roots([1, 2, -1, -2])
Elle fournit même les racines complexes, le nombre imaginaire i étant noté j.
t
In [8]: numpy.roots([1, 0, 0, 1])
• La méthode dichotomique, qualifiée de lente mais sûre (slow but sure), est implémentée
dans scipy.optimize.bisect.
i
In [10]: scipy.optimize.bisect(math.sin, 3, 4)
Out[10]: 3.141592653589214
pyr
Out[11]: 3.141592653589793
Co
Out[12]: 3.141592653589793
• Enfin, pour des problèmes qui ne sont pas scalaires, on peut utiliser la fonction fsolve :
In [13]: scipy.optimize.fsolve(lambda (x,y): (x + y**2, 1 - y + x**2), (0,0))
Lire la documentation de ces fonctions est assez intéressant. Il y est par exemple rappelé
que rien n’est garanti dans le résultat.
Livre_silo 30 août 2013 16:32 Page 212
es
Informatique pour tous
212
l
rol
Enfin, ces fonctions intégrées commentent parfois la qualité supposée de leur résultat :
In [14]: scipy.optimize.fsolve(lambda (x,y):(x+y**2,1+y+x**2),(0,0))
/usr/lib/python2.7/dist-packages/scipy/optimize/minpack.py:152:
RuntimeWarning: The iteration is not making good progress, as
Ey
measured by the improvement from the last ten iterations.
warnings.warn(msg, RuntimeWarning)
Out[14]: array([-0.33145845, -0.75398696])
8.4 Exercices
Exercice 8.7 Comment approcher numériquement f ′′ (x0 ) ? Proposer une formule, puis une majoration
t
du reste dans un cadre raisonnable. Tester la formule proposée sur des exemples simples et vérifier l’ordre
de grandeur de l’erreur.
gh
Dans les différents cas, dans quelle zone se trouvent les pas optimaux ?
Exercice 8.8 Cet exercice est inspiré du chapitre 12 de [Holmgren], qui contient de nombreuses autres
pistes d’expérimentation de la méthode de Newton sur les polynômes de petit degré.
On s’intéresse ici aux valeurs obtenues par itération de la méthode de Newton pour la fonction
f : x 7→ x3 + cx + 1, avec 0 comme valeur initiale.
i
1 Pour c > 0, la méthode de Newton converge pour tout choix de la valeur initiale. Faire un dessin pour
s’en convaincre et démontrer effectivement le résultat à titre d’exercice de mathématiques. Expérimen-
pyr
appartenant à [−0.15, 0.1] : il existe une autre zone, vers u0 = 0.8, dans laquelle on observe le même
schéma de bifurcation.
Exercice 8.9( Dans la méthode
) ( de racines
d’extraction ) du paragraphe 8.2.1, si on remplace la relation
1 2 1 K
un+1 = un + par un+1 = un + où K est un réel positif, alors la suite (un )n∈N
2 √ un 2 un
converge vers K.
1 Appliquer la méthode de Newton à la fonction x 7→ x2 − K, avec K le complexe 1 + i et différentes
valeurs initiales u0 .
Le complexe 1 + i est construit en Python par complex(1,1) : aucune bibliothèque n’est requise.
2 Comparer le résultat à celui renvoyé par complex(1,1)**(0.5).
3 Question de mathématiques : en fonction du premier terme, vers quelle « racine carrée » de z0 ̸= 0
converge la méthode de Newton appliquée à z 7→ z 2 − z0 ?
Livre_silo 30 août 2013 16:32 Page 213
es
8 – Résolution numérique d’équations sur les réels
213
l
rol
Exercice 8.10 * Si A est une matrice symétrique réelle définie positive, alors on peut montrer que la suite
1( )
définie par M0 = In et la relation de récurrence Mp+1 = Mp + AMp−1 est bien définie, et que ses
2
termes sont des matrices symétriques définies positives convergeant vers B qui est la matrice symétrique
réelle définie positive de carré égal à A.
1 Trouver une condition d’arrêt raisonnable pour transformer ce résultat de mathématiques en algorithme
Ey
effectif.
2 Programmer cet algorithme. Évaluer sa complexité.
3 Tester le programme sur des matrices définies positives, telles que celles de Virginie puis de Hilbert,
définies au chapitre précédent.
4 Démontrer, à titre d’exercice de mathématiques, le résultat admis dans l’énoncé.
t
i gh
pyr
bifurcations.jpeg
Co
Figure 8.7
Diagramme de bifurcation : le chaos s’installe...
Livre_silo 30 août 2013 16:32 Page 214
es
Informatique pour tous
214
l
rol
Exercice 8.12 Méthode de la sécante.
Soit f : I → R continue, s’annulant en a ∈ I, concave ou convexe au voisinage de a. On fixe u0 , u1 ∈ I
au voisinage de a et on construit (un )n⩾2 de la façon suivante : pour tout n ⩾ 2, on définit un comme
l’abscisse de l’intersection de l’axe des abscisses avec la sécante au graphe de f passant par les points
d’abscisse un−2 et un−1 .
1 Faire un dessin ; constater qu’il peut y avoir divergence, ou même que un peut ne pas être défini à partir
Ey
d’un certain rang, mais que si u0 et u1 sont assez proches de a, on peut raisonnablement espérer qu’il
y ait convergence de (un )n∈N vers a.
2 Donner une relation liant un−2 , un−1 et un .
3 Proposer un test d’arrêt pour la méthode de la sécante, qui consiste à approcher a en calculant des un
jusqu’à ce qu’une certaine condition soit vérifiée.
4 Programmer et tester la méthode de la sécante.
5 On admet que sous des conditions favorables (mais réalistes si u0 et u1 sont suffisamment proches
√ de a),
φ 1+ 5
alors la distance δn = |un − a| vérifie une relation de la forme δn+1 ⩽ Kδn , avec φ = ·
2
t
Donner alors le nombre d’étapes nécessaires pour approcher a avec une erreur majorée par 2−52 ,
puis 2−1000 . Comparer avec la méthode de Newton.
gh
6 On fait l’hypothèse suivante, très optimiste pour la méthode de Newton : le coût de chaque évaluation
de f ′ est de l’ordre de celui de deux évaluations de f . Comparer alors les coûts des méthodes de
Newton et de la sécante, pour obtenir une précision donnée.
Tout comme la méthode de Newton, pour laquelle on peut remplacer f ′ (un ) par la jacobienne, la mé-
thode de la sécante a un analogue dans Rn : il s’agit de la très belle méthode de Broyden.
Si on applique la méthode de Newton à la fonction x 7→ ax−1 (avec a ∈ R∗+ ), on peut espérer approcher
pyr
l’inverse de a.
1 Expliciter la relation de récurrence mise en place dans la méthode de Newton... et constater qu’elle est
inutilisable !
1
On va plutôt s’intéresser à une autre suite convergeant vers : on fixe x0 (on verra comment plus tard)
a
et on définit par récurrence la suite (xn )n∈N par la relation xn+1 = xn (2 − axn ).
2 Expliquer qualitativement le lien avec la méthode de Newton. Démontrer qu’en cas de convergence, la
1
limite vaut 0 ou ·
a
Co
3 Programmer cette méthode et déterminer les cinq premiers termes de la suite, lorsque a = 2 et
x0 ∈ {0, 0.4, 0.9, 1, 2}.
4 On suppose maintenant que A est une matrice et on considère une suite de matrice (Xn )n∈N vérifiant
la relation Xn+1 = Xn (2In − AXn ).
a) Quel est le coût (en termes d’opérations sur les flottants) de chaque itération ?
b) D’après [Pan et Schreiber], un bon choix de valeur de départ est X0 = α0 tA, avec
1 ∑ ∑
α0 = , où ∥A∥1 = maxj |ai,j | et ∥A∥∞ = maxi |ai,j |.
∥A∥1 ∥A∥∞ i j
Programmer cette méthode d’inversion matricielle, en décidant d’un test d’arrêt raisonnable.
c) Évaluer la complexité de cette méthode (en cas de convergence, et en fonction du nombre d’itérations
finalement effectuées).
d) Tester, comparer avec les méthodes du chapitre précédent.
1 2 3
On représente figure 8.8 l’évolution de
A−1 − Xn
en fonction de n, avec A = 4 5 6 .
1
7 8 10
Livre_silo 30 août 2013 16:32 Page 215
es
8 – Résolution numérique d’équations sur les réels
215
l
rol
t Ey
iterations-pseudo-inverse.pdf
i gh
Figure 8.8
La convergence se fait désirer... puis est très rapide.
pyr
Exercice 8.14 ** On se propose d’approcher de trois façons différentes une solution du système non
linéaire {
exp(x + y 2 ) sin(x + y) = 1
cos x + sin2 y = 1
Plus précisément, on veut approcher l’unique solution appartenant à [0, 1] × [0, 1]. Cet exercice peut
Co
constituer à lui seul une séance de TP complète, modulo quelques explications supplémentaires !
In [15]: scipy.optimize.fsolve(lambda (x,y):(f1(x,y),f2(x,y)),(0.5,0.5))
Dans chaque cas, on s’intéressera au nombre d’évaluations de f nécessaires pour obtenir p bits significa-
tifs.
1 On peut, à x0 fixé, utiliser une méthode de résolution approchant y tel que
2
ex0 +y sin(x0 + y) = 1.
Livre_silo 30 août 2013 16:32 Page 216
es
Informatique pour tous
216
l
rol
jacobienne) en évaluant numériquement quatre dérivées :
∂f
1 (x, y) ∂f1 (x, y)
Jac(F )(x, y) = ∂x ∂y
∂f2 (x, y) ∂f2 (x, y)
∂x ∂y
Ey
À chaque étape, il s’agit donc d’évaluer la jacobienne, puis de résoudre un système linéaire — avec
l’espoir raisonnable qu’il s’agisse d’un système de Cramer.
3 On peut enfin voir que la deuxième équation permet de définir explicitement x comme fonction de y,
ce qui nous ramène à une équation scalaire.
4 Comparer les facilités de mise en œuvre et vitesse d’exécution (en fonction de la précision requise) des
trois méthodes. Laquelle semble la plus facilement généralisable à un système « moins favorable » ? Et
à une dimension strictement plus grande que 2 ?
t
Exercice 8.15 ** Courbes définies par une relation implicite. On souhaite ici représenter un en-
semble C de points de coordonnées (x, y) vérifiant une relation de la forme f (x, y) = 0. Plus précisé-
gh
ment, on cherche à déterminer une suite de points Mk (xk , yk ) proches de C, les xk étant des points
uniformément répartis sur [a, b] fixé (avec donc a = x0 < x1 < · · · < xn = b). On donne également α
et β tels que f (a, α)f (a, β) < 0. L’application f est supposée continue.
Une façon de procéder consiste à résoudre numériquement l’équation f (x0 , y) = 0 (en partant de l’inter-
valle [α, β]), ce qui détermine y0 . Ensuite, de proche en proche, on résout numériquement f (xk+1 , y) = 0
en partant de valeurs de y proches de yk , ce qui fixe yk+1 , etc.
i
pyr
2 En utilisant ce qui précède et la bibliothèque matplotlib (voir chapitre 9), représenter sur un même
graphique les courbes d’équations implicites exp(x + y 2 ) sin(x + y) = 1 et cos x + sin2 y = 1 (tirées
de l’exercice précédent).
Co
intersection.pdf
Figure 8.9
L’intersection se situe vers (0.39, 0.28).
Au voisinage de (0, 0), l’équation f2 (x, y) = 1 pose problème ; pourquoi ?
Livre_silo 30 août 2013 16:32 Page 217
es
8 – Résolution numérique d’équations sur les réels
217
l
rol
√
Exercice 8.16 Les Babyloniens et 2.
√
1 Donner les quatre premiers chiffres significatifs de 2... en base 60.
2 Déchiffrer rapidement la tablette YBC 7289 représentée figure 8.10 et dont l’âge est estimé à plus de
3 000 ans.
3 Constater que les Babyloniens connaissaient un peu de mathématiques !
t Ey7289obv.jpg
i gh
pyr
Figure 8.10
La tablette YBC7289.
On notera un certain manque de soin dans le tracé des lignes droites.
Co
Livre_silo 30 août 2013 16:32 Page 218
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 219
l es
rol
9
Ey
Résolution numérique
t
gh
d’équations
différentielles
i
pyr
Livre_silo 30 août 2013 16:32 Page 220
es
Informatique pour tous
220
l
rol
De nombreux phénomènes physiques se modélisent à l’aide d’équations différentielles pour
lesquelles on ne dispose pas de solutions analytiques : un pendule amorti nous amène à
étudier l’équation θ̈ = −k1 sin θ − k2 θ̇, les problèmes de cinétique chimique conduisent
à des systèmes différentiels non linéaires décrivant les évolutions de différents réactifs au
cours du temps, et les phénomènes qu’on observe en mécanique des fluides sont en partie
Ey
décrits par les équations aux dérivées partielles non linéaires de type Navier-Stokes.
En mathématiques, ces équations ont leur intérêt propre et étudier le comportement qua-
litatif de solutions est nettement plus aisé si on peut visualiser une approximation raison-
nable de celles-ci.
t
9.1 Méthode d’Euler
gh
Le théorème de Cauchy-Lipschitz assure ¹ que sous des conditions raisonnables, il existe
une unique application y de classe C 1 sur [a, b] dont la valeur est imposée en a et qui vérifie
une équation de la forme y ′ (t) = F (t, y(t)) pour tout t ∈ [a, b]. L’objet des schémas numé-
riques est d’obtenir des approximations de ces solutions dont la théorie donne l’existence
de façon non constructive. En pratique, on tente en général d’approcher y en un certain
i
pyr
tk+1 tk+1
y(tk+1 ) − y(tk ) = y ′ (u)du = F (u, y(u))du ≃ hF (tk , y(tk )),
tk tk
alors on obtient la méthode d’Euler : les approximations sont calculées de proche en proche
via la formule suivante : yk+1 = yk + hF (tk , yk ) (voir figure 9.1). On initialise bien
entendu avec y0 = y(a), qui sera la seule valeur « exacte » calculée.
Graphiquement, cela revient à faire des approximations successives de courbes par des
tangentes. On va prendre pour exemple l’équation différentielle y ′ (t) = y(t) (dont les so-
lutions sont les t 7→ Ket ) et on va approcher sur [0, 1] l’unique solution telle que y(0) = 1,
1
autrement dit la fonction exponentielle. Pour n = 3, c’est-à-dire un pas de , on obtient
3
la figure 9.2.
1. Comme vu en cours de mathématiques, c’est évidemment plus compliqué. Par exemple, si F n’est pas
linéaire vis-à-vis de y, on est juste assuré de l’existence d’une solution dont la valeur en a est imposée, mais on
ne sait pas si elle est définie jusqu’en b.
Livre_silo 30 août 2013 16:32 Page 221
es
9 – Résolution numérique d’équations différentielles
221
l
rol
t Ey
euler-generique.pdf
i gh
Figure 9.1
Méthode d’Euler : si y(tk ) ≃ yk , alors y(tk+1 ) ≃ yk + hF (tk , yk ).
pyr
Co
trois-pas.pdf
Figure 9.2
En pointillés : les fonctions exponentielles correspondant aux nouvelles conditions initiales,
après chaque itération. Pour l’étape ultérieure, on suit la tangente.
Livre_silo 30 août 2013 16:32 Page 222
es
Informatique pour tous
222
l
rol
On peut facilement se convaincre que :
• Quand le pas va diminuer, l’approximation va s’améliorer.
• Dans une situation convexe comme ici, la méthode d’Euler conduit irrémédiablement
à s’écarter de la solution au fur et à mesure qu’on s’éloigne de a.
Ey
Le deuxième point sera partiellement amélioré avec des schémas moins élémentaires (dis-
cutés dans les exercices). Pour le premier point, on peut visualiser sur la figure 9.3 les
1 3
solutions de l’équation précédente avec des pas de 1, et sur l’intervalle [0, 3].
t 10 100
gh
exp3.pdf
i
pyr
Co
Figure 9.3
3
Avec h = , il est déjà difficile de discerner la solution réelle de son approximation.
100
De fait, la méthode d’Euler donne des résultats très satisfaisants dans beaucoup de situa-
tions pratiques.
Livre_silo 30 août 2013 16:32 Page 223
es
9 – Résolution numérique d’équations différentielles
223
l
rol
On va supposer qu’on approche une solution y sur un intervalle [a, b] avec une méthode à
(h)
un pas telle que la méthode d’Euler. À h fixé, on note n(h) le nombre de temps ti en
b−a (h) (h) (h)
lesquels y sera approchée (environ ). On aimerait avoir : y(ti ) ≃ yi , avec yi
h
la valeur obtenue dans le schéma de pas h.
Ey
Le premier objectif est d’avoir une méthode convergente au sens suivant :
( )
(h) (h)
max y ti − yi −→ 0.
0⩽i⩽n(h) (y0 ,h)→(y(a),0)
On aura noté que la valeur de départ est supposée tendre vers y(a) ; c’est important, car
(h)
même si en théorie on pose y0 = y(a), on peut s’attendre à avoir en pratique une valeur
t
numérique seulement très proche.
gh
La convergence sera assurée pour peu que le schéma soit :
• stable : en perturbant un peu le schéma, la perturbation résultante est contrôlée linéai-
rement par la perturbation initiale ² ;
• consistant : l’erreur de consistance, définie dans le cas de la méthode d’Euler par :
i
∑
n(h)−1
i=0
tend vers 0 lorsque h tend vers 0. Cela signifie que si à chaque étape on remplace yi
par y(ti ) et si on somme les erreurs provoquées par les approximations locales, alors
l’erreur globale produite reste contrôlée par h.
On dit qu’une méthode est d’ordre p lorsque l’erreur de consistance possède un majorant
Co
de la forme K.hp . Plus précisément, l’ordre d’une méthode sera le maximum des entiers p
pour lesquels on a une majoration de cette forme.
On pourra retenir pour la suite que :
• La méthode d’Euler est d’ordre 1. On parle ici de la méthode d’Euler explicite et non de
sa variante, la méthode d’Euler implicite.
• La méthode de Heun est d’ordre 2 (voir plus loin l’exercice 9.7).
• La méthode dite de Runge-Kutta (ou « Runge-Kutta d’ordre 4 », voir plus loin l’exercice
9.8) est d’ordre... devinez !
Concrètement, on peut visualiser ces ordres en représentant les erreurs de consistance e(h)
en fonction de h avec une échelle doublement logarithmique (voir la figure 9.4).
Livre_silo 30 août 2013 16:32 Page 224
es
Informatique pour tous
224
l
rol
t Ey
erreurs-consistance.pdf
i gh
Figure 9.4
Erreurs de consistance : h en abscisse et e(h) en ordonnée pour y ′ = y et y(0) = 1 sur [0, 1].
pyr
1
Exercice 9.1 Montrer que pour la méthode d’Euler appliquée à y ′ = y sur [0, 1] avec y(0) = 1 et h = ,
n
e−1
on a e(h) ∼ ·
2n
Exercice 9.2 Expliquer le comportement des courbes de la figure 9.4 sachant qu’on a calculé les erreurs
1
Co
Livre_silo 30 août 2013 16:32 Page 225
es
9 – Résolution numérique d’équations différentielles
225
l
rol
(h)
mais ici ce maximum est pris en tn = 1, donc il suffit d’évaluer y(1) − yn .
Les temps sont exprimés en secondes et on donne deux chiffres significatifs :
Ey
Temps 6.7 × 10−4 6.9 × 10−3 6.8 × 10−2 6.7 × 10−1 6.7
Erreur 1.36 × 10−3 1.36 × 10−4 1.36 × 10−5 1.36 × 10−6 1.36 × 10−7
Les résultats sont sans surprise : d’une part, le temps est proportionnel au nombre de
(h)
termes calculés et, d’autre part, on a ici yi = (1 + 1/n)i , donc :
t
e
E(1/n) = e − (1 + 1/n)n ∼ ·
2n
gh
On aura noté que cette erreur est supérieure à celle de consistance, ce qui est bien normal.
Avec la méthode d’Euler, on ne peut obtenir une précision de l’ordre de 10−6 qu’avec un
temps de calcul assez important. Pour ce type de précision (et mieux encore), on préfère
donc les méthodes d’ordre 2 ou plus. On obtient par exemple avec la méthode de Heun
i
pyr
Enfin, avec la méthode de Runge-Kutta d’ordre 4, il n’est pas utile de prendre h très petit
Co
pour avoir une erreur spectaculairement faible avec un temps de calcul très court :
Livre_silo 30 août 2013 16:32 Page 226
es
Informatique pour tous
226
l
rol
POUR ALLER PLUS LOIN Pas adaptatif et méthodes multipas
Les programmes chargés d’intégrer numériquement des équations différentielles (tels que
odeint) adaptent en général leur pas à la fonction F : si F (tk , yk ) est faible, alors y varie peu,
donc on peut faire de plus grands pas. On parle de méthodes à pas adaptatif, ou variable.
Ey
Il existe également des méthodes dites à pas multiples, ou multipas : on calcule yk à l’aide
de yk−1 , yk−2 , ... yk−p . La méthode d’Adams est la plus classique de ces méthodes.
d’étude, la condition initiale y0 et le pas h. Plus précisément : avec ces données, la fonction
1
Un premier essai pour l’équation y ′ = y avec la condition initiale y(0) = 1 et le pas h =
3
donne ce qui suit.
Livre_silo 30 août 2013 16:32 Page 227
es
9 – Résolution numérique d’équations différentielles
227
l
rol
In [1]: euler(lambda t, y :y, 0, 1, 1, 1./3)[1]
Ey
return y
pyr
On peut visualiser figure 9.5 les solutions renvoyées pour les pas 10−k , avec k ∈ J1, 4K.
Co
explosion.pdf
Figure 9.5
Au voisinage de ce type de point, le pas doit être plus petit.
Livre_silo 30 août 2013 16:32 Page 228
es
Informatique pour tous
228
l
rol
9.2.2 Équations scalaires d’ordre 2 ou plus
Une équation scalaire telle que θ̈ = −k sin θ est d’ordre 2 (ici, l’ordre désigne la plus
grande dérivée intervenant dans l’équation, il n’est pas question de l’ordre d’un schéma
numérique). Si on s’intéresse au couple X = (θ, θ̇), alors il vérifie l’équation Ẋ = G(X),
Ey
avec G l’application ³ (α, β) 7→ (β, −k sin α). On ramène par ce procédé de vectorisation
les équations différentielles scalaires d’ordre p à des équations d’ordre 1, mais à valeurs
dans Rp .
Par chance, la méthode d’Euler (mais aussi les autres méthodes numériques usuelles) fonc-
tionne aussi bien pour des fonctions à valeurs vectorielles que réelles ! Il n’y a donc pas
grand-chose à modifier :
t
def euler_vectoriel(F, a, b, y0, h):
y = y0
gh
t = a
les_y = [y0] # la liste des valeurs renvoyées
les_t = [a]
while t+h <= b:
y = y + h * F(t, y)
les_y.append(y)
i
t += h
les_t.append(t)
pyr
Si on choisit intelligemment les types de données, il n’y a donc rien à changer. Attention
cependant. Le mathématicien pense : (1, 2) + (3, 4) = (4, 6). Mais du point de vue de
Co
Out[5]: (1, 2, 4, 5)
Out[6]: [1, 2, 4, 5]
3. L’expérience montre que chez le débutant, ne pas vouloir expliciter la fonction G (en donnant la valeur de
G(α, β)) est souvent une économie de temps très modeste et qui coûte très cher par la suite...
Livre_silo 30 août 2013 16:32 Page 229
es
9 – Résolution numérique d’équations différentielles
229
l
rol
On importe cette dernière, par exemple via from numpy import *. Ensuite, on obtient le ré-
sultat souhaité :
In [7]: 2*array([1,2]) + array([3,4])
Ey
Exercice 9.4 avec corrigé * Si t1 et t2 sont des array, expliquer la différence entre t1 += t2 et
t1 = t1 + t2.
Tester sur un exemple, en définissant d’abord t1 et t2, ainsi que t3 = t1. L’opération t1 += t2 ne va pas
créer un nouveau tableau numpy (array) : elle va modifier le tableau existant, pointé par t1, mais aussi
par t3. Si on exécute t1 = t1 + t2, un nouveau tableau est créé et t1 pointe maintenant dessus, alors
que t3 pointe sur un tableau qui n’a pas changé.
pyr
Co
pendule-Euler.pdf
Figure 9.6
1
Méthode d’Euler pour y ′′ + y = 0 avec y(0) = 0 et y ′ (0) = 1, avec un pas égal à
100
Livre_silo 30 août 2013 16:32 Page 230
es
Informatique pour tous
230
l
rol
1
Sur la figure 9.7, on utilise le schéma de Heun, qui est d’ordre 2, avec le pas ; la
100
différence avec la méthode d’Euler est nette.
t Ey
pendule-Heun.pdf
i gh
pyr
Figure 9.7
1
Méthode de Heun pour y ′′ + y = 0 avec y(0) = 0 et y ′ (0) = 1, avec un pas égal à
100
Quand cela est possible, il faut tester ses programmes sur des données pour lesquelles
le résultat est simple et connu. Cela permet également d’évaluer assez efficacement
les qualités de convergence, puisque la solution théorique est connue.
Livre_silo 30 août 2013 16:32 Page 231
es
9 – Résolution numérique d’équations différentielles
231
l
rol
9.3.1 Intégration des équations différentielles avec odeint
La bibliothèque scipy.integrate contient la fonction odeint, qui résout numériquement des
équations différentielles. On commence donc par la charger :
Ey
from scipy.integrate import odeint
Une utilisation basique sera de la forme : odeint(f, y0, t), pour résoudre l’équation
y ′ (t) = f (y(t), t) sur un intervalle [a, b]. L’aspect déroutant est la nature de t : il s’agit
d’un tableau de temps entre a et b : t est de la forme t=[a,t1,...,b]. La condition initiale
est alors : y(a) = y0 . La valeur renvoyée est un tableau contenant une estimation de la
solution aux différents temps.
t
ATTENTION Ordre des arguments
gh
La fonction donnée en paramètre prend « le temps » comme deuxième argument, et non
en premier comme on a l’habitude de le faire en mathématiques.
Par exemple, pour résoudre sur [0, 1] l’équation y ′ = y avec la condition initiale y(0) = 1
et un pas de 0.5, on peut exécuter :
i
pyr
Out[9]: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
Ici, l’objet renvoyé est une liste qu’on appelle aussi tableau, mais qui n’est pas un array au
sens de numpy, même si cette dernière bibliothèque fait souvent elle-même les conver-
sions.
2 Avec la fonction linspace à laquelle on donne des bornes et un nombre de valeurs atten-
dues :
In [10]: numpy.linspace(0,1,11)
Out[10]: array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])
3 Avec la fonction arange qui est une adaptation de range pour les array. On note que la
borne supérieure de l’intervalle est exclue comme avec range.
In [11]: numpy.arange(0,1.1,0.1)
Out[11]: array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])
Livre_silo 30 août 2013 16:32 Page 232
es
Informatique pour tous
232
l
rol
EN PRATIQUE Dans les versions Python 2.x
Attention, on se souvient qu’en Python 2.x, si k est un entier, alors k/10 va renvoyer le
quotient dans la division euclidienne. D’où l’utilisation de k/10. et non k/10 si on veut que
le code soit correctement interprété en Python 2.
Ey
9.3.2 Représentation de graphes avec plot
Pour représenter une solution, on va utiliser la commande plot de la bi-
bliothèque matplotlib.pyplot. On suppose avoir chargé cette bibliothèque via
import matplotlib.pyplot as pl, ce qui évite d’avoir à taper tout le préfixe à chaque
t
fois. On suppose de même que numpy a été chargé via import numpy as np. L’utilisation de
gh
base de plot est : plot(x,y), où x et y sont des tableaux d’abscisses et d’ordonnées. Par
exemple, pour représenter le graphe de t 7→ et sin t sur [−π, π], on commence par créer le
tableau des abscisses ; ensuite, on crée le tableau des ordonnées en appliquant la fonction f
préalablement définie à chaque élément de x. Pour cela, on va appliquer directement f à
un tableau, ce qui produit le tableau des images.
i
def f(t):
return np.sin(5*t) * np.exp(t)
pyr
Appliquer directement une fonction à un tableau est assez crispant au début, mais on s’y
habitue ! Cela dit, toutes les fonctions ne peuvent pas être appliquées à des tableaux. Par
exemple, si la fonction sin utilisée avait été extraite de la bibliothèque math, on n’aurait
pas pu appliquer f à x. Il y a alors deux possibilités : on utilise la fonction map (solution
Co
naturelle pour l’informaticien) avec y = map(f, x) ou bien on vectorialise f pour en faire une
fonction applicable à un tableau :
fv = np.vectorize(f)
y = fv(x)
On peut alors représenter le graphe : pl.plot(x, y). Le résultat est très décevant !
In [12]: pl.plot(x, y)
Le graphe a effectivement été créé... mais pas affiché ! Pour le voir dans une fenêtre, on
utilise pl.show(). Pour obtenir la figure 9.8, quelques décorations optionnelles ont été ajou-
tées : une grille, un titre, un nom pour les axes, ainsi que le tracé des axes, qui ne se fait
pas par défaut. On peut observer le code complet au programme 8 ci-après.
Signalons enfin que la gestion de l’affichage, via pl.show(), est dépendante de l’environne-
ment (Spyder, Idle...), ce qui est assez pénible. Une façon fiable d’accéder aux graphiques
produits consiste à les sauvegarder (au format de son choix) via pl.savefig('le-nom.pdf').
Livre_silo 30 août 2013 16:32 Page 233
es
9 – Résolution numérique d’équations différentielles
233
l
rol
t Ey
mon-premier-plot.pdf
i gh
Figure 9.8
Graphe d’une fonction, avec un titre, une grille, etc.
pyr
def f(t):
return np.sin(5*t) * np.exp(t)
Livre_silo 30 août 2013 16:32 Page 234
es
Informatique pour tous
234
l
rol
SAVOIR-FAIRE Obtenir une représentation graphique de grandeurs
issues d’un calcul
Ey
Les principes qui guident la construction d’une représentation graphique restent les
mêmes que pour un tracé sur papier. Le choix des axes, de leur échelle, la présence
d’une légende, etc, sont des critères de qualité pour la représentation graphique pro-
duite.
Il ne faut pas se laisser impressionner par la multitude d’options existantes dans
matplotlib : on les découvre progressivement. La documentation en ligne de Python
est bien sûr à consulter sans modération.
t
gh
9.3.3 De jolis graphes
Pour obtenir la figure 9.3, on a calculé trois solutions et, pour chacune, on a fait un appel
à plot. Le dessin n’est réinitialisé qu’à l’appel de clf() (pour CLear Figure), ce qui autorise
les tracés multiples.
i
pyr
Exercice 9.5 Le code réel diffère légèrement de celui présenté ici. Tester les lignes de code précédentes,
expliquer le problème et le réparer !
Pour les systèmes d’ordre 2 ou plus, on va prendre l’exemple du pendule non amorti linéarisé
(y ′′ = −y). Pour obtenir la figure 9.6, on commence par définir la fonction vectorielle qui
va vérifier l’équation linéaire d’ordre 1 :
Co
Livre_silo 30 août 2013 16:32 Page 235
es
9 – Résolution numérique d’équations différentielles
235
l
rol
Comme on voulait extraire la première composante des tableaux rendus (qui repré-
sentent θ), on a écrit :
pl.plot(t, array(theta)[:, 0])
Ey
sur les tableaux de tableaux natifs de Python.
On peut, sur ce même exemple, souhaiter représenter le portrait de phase, c’est-à-dire l’en-
semble des points des couples (y(t), y ′ (t)) pour t ∈ [0, 10]. C’est quelque chose de facile à
faire, grâce à un double-slicing. Le programme 9 ci-après regroupe les commandes qui ont
permis de réaliser la figure 9.9. On voit en particulier une option qui change l’épaisseur des
traits.
t
PROGRAMME 9 Un portrait de phase
gh
def fpendule(t, X):
theta, thetap = X
return array([thetap, -theta])
pyr
portrait-pendule.pdf
Figure 9.9
Le portrait de phase du pendule linéarisé avec un pas grossier (h = 0.1) : Euler vs Heun
Livre_silo 30 août 2013 16:32 Page 236
es
Informatique pour tous
236
l
rol
On va terminer avec le pendule non amorti (et non linéarisé), pour lequel on présente des
portraits de phase calculés avec odeint pour différentes valeurs de la vitesse initiale (mais
toujours θ(0) = 0) : le code ayant produit la figure 9.10 est donné programme 10 ci-après.
Les résultats confirment l’intuition et la théorie :
Ey
• Si l’impulsion initiale est faible, le mouvement est périodique ; on retrouve l’aspect ob-
servé dans le cas linéarisé.
• Si l’impulsion initiale est forte, le mouvement est pseudo-périodique : il existe T > 0
tel que θ(t + T ) = θ(t) + 2π et θ̇(t + T ) = θ̇(t) pour tout( t. )
• Dans le cas limite (qui est fort difficile à expérimenter !), θ(t), θ̇(t) tend vers (π, 0)
lorsque t tend vers +∞. Nous reviendrons sur ce point plus tard.
t
PROGRAMME 10 Portraits de phase avec odeint
gh
def fpendule(X, _):
theta, thetap = X[0], X[1]
return array([thetap, -np.sin(theta)])
pyr
# (...)
pl.xlabel(r'$\theta(t)$', fontsize=18)
# ...
Co
Figure 9.10
Le pendule non amorti, avec
différentes conditions initiales
pendule-odeint.pdf
Livre_silo 30 août 2013 16:32 Page 237
es
9 – Résolution numérique d’équations différentielles
237
l
rol
9.3.4 Où on observe quelques limitations
On reprend le cas limite du pendule non amorti. La figure 9.11 a été obtenue sur la plage
de temps [0, 30] avec quatre méthodes de résolution différentes et les pas utilisés pour les
résolutions hors (odeint valent
) 0.01. Aucune méthode ne renvoie une solution convergeant
Ey
vers π. Lorsque θ(t), θ̇(t) est proche de (π − , 0+ ), les approximations peuvent faire que
ce couple prend une valeur ( de la forme
) (π + ε1 , ε2 ) (ε1 , ε2 > 0) : le pendule bascule de
l’autre côté. Cependant, θ(t), θ̇(t) peut prendre une valeur de la forme (π − ε1 , −ε2 ) et
le pendule revient en arrière ! C’est ce qui se passe lors de la résolution avec la méthode de
Runge-Kutta d’ordre 4.
Finalement, la résolution numérique conduit aux mêmes résultats que l’expérimentation !
t
gh
Figure 9.11
Le pendule non amorti dans le
cas limite : quatre résolutions
différentes
i
pyr
pendule-limite.pdf
Co
Livre_silo 30 août 2013 16:32 Page 238
es
Informatique pour tous
238
l
rol
t Ey
instable-debut.pdf
i gh
Figure 9.12
Jusqu’ici, tout va bien !
pyr
Co
instable-fin.pdf
Figure 9.13
Rien à faire : les solutions numériques divergent forcément.
Livre_silo 30 août 2013 16:32 Page 239
es
9 – Résolution numérique d’équations différentielles
239
l
rol
9.4 Exercices
Pour l’informaticien
Exercice 9.7 La méthode de Heun consiste à remplacer dans celle d’Euler la relation :
Ey
yk+1 = yk + hF (tk , yk )
par :
h
yk+1 = yk + (F (tk , yk ) + F (tk+1 , yk + hF (tk , yk ))) .
2
1 Expliquer qualitativement en quoi cela doit améliorer la précision.
2 Programmer la méthode de Heun.
3 Calculer puis représenter les solutions approchées par la méthode de Heun de y ′ = y avec la condition
initiale y(0) = 1.
t
Le calcul de yk+1 fait intervenir la moyenne des dérivées de y estimées en tk et tk+1 . Pour une fonction
convexe, la première constitue un minorant de l’accroissement de y entre tk et tk+1 , et la seconde en
gh
constitue un majorant. La moyenne des deux est donc encadrée par ces deux bornes.
Pour la réalisation informatique, il n’y a qu’à écrire y = y + h/2 * (F(t, y) + (F(t+h, y+h*F(t,y)))) à
la place de y = y + h * F(t, y). Même avec des pas modestes, l’approximation n’est pas trop mauvaise
(voir figure 9.14), et en tout cas bien meilleure qu’avec la méthode d’Euler.
i
pyr
exp3heun.pdf
Co
Figure 9.14
La méthode de Heun pour y ′ = y avec y(0) = 1, pour h = 1 et h = 0.5
Exercice 9.8 Reprendre l’exercice précédent, mais avec la méthode de Runge-Kutta d’ordre 4. Cette fois,
le calcul de yk+1 à l’aide de yk passe par le calcul de trois valeurs intermédiaires :
h h
αk = y k + F (tk , yk ), βk = yk + F (tk + h/2, αk ), γk = yk + hF (tk + h/2, βk )
2 2
Livre_silo 30 août 2013 16:32 Page 240
es
Informatique pour tous
240
l
rol
et enfin :
h
yk+1 = yk + (F (tk , yk ) + 2F (tk + h/2, αk ) + 2F (tk + h/2, βk ) + F (tk+1 , γk )) .
6
Ey
figure 9.15.
t
gh
exp3RK4.pdf
i
pyr
Figure 9.15
La méthode de Runge-Kutta d’ordre 4 pour y ′ = y avec y(0) = 1, pour h = 1
Co
Exercice 9.9 Si z est la solution exacte d’une équation différentielle sur [a, b] et [y0 , ..., yn ] est la suite
des valeurs approchées de cette solution en a = t0 < · · · < tn = b, on définit l’erreur d’approximation
comme le maximum des |z(tk ) − yk | pour 0 ⩽ k ⩽ n.
1 Écrire une fonction prenant en entrée z, y et t et renvoyant l’erreur d’approximation.
2 Tester sur des cas simples, avec différents schémas (Euler, Heun, Runge-Kutta et enfin le résultat fourni
par odeint).
On vérifiera en particulier avec l’exemple de la fonction exponentielle.
Exercice 9.10 Les flottants Python possèdent 52 bits significatifs. En faisant l’hypothèse que l’erreur
commise après une itération dans la méthode de résolution est de l’ordre de 2−52 puis qu’il n’y a plus
d’erreur d’approximation, évaluer à partir de quel t on a y(t) de l’ordre de 1 (début de l’explosion).
Vérifier ce calcul « brutal » avec l’équation y ′′ = −2y ′ + 3y et des conditions initiales bien choisies :
prévoir le moment de la divergence, puis tester.
Exercice 9.11 avec corrigé * La résolution numérique de l’équation y ′′ = −2y ′ + 3y avec les conditions
initiales y(0) = 1 et y ′ (0) = −3 produit les résultats représentés figure 9.16.
Tenter une explication. Que doit-il se passer avec l’équation translatée y ′′ = −2y ′ + 3y − 1 sous les
4
conditions initiales y(0) = et y ′ (0) = −3 ? Vérifier !
3
Livre_silo 30 août 2013 16:32 Page 241
es
9 – Résolution numérique d’équations différentielles
241
l
rol
Figure 9.16
Une convergence non prévue !
t Ey semi-instable.pdf
i gh
1
La solution mathématique du problème translaté est t 7→ + e−3t . Pourtant, le couple de flottants
pyr
3
′
(y(t), y (t)) ne pourra jamais valoir (1/3, 0)... On voit figure 9.17 que même odeint conduit à une solu-
tion divergente.
Figure 9.17
Cette fois, ça diverge comme
prévu.
Co
semi-instable-bis.pdf
Livre_silo 30 août 2013 16:32 Page 242
es
Informatique pour tous
242
l
rol
Exercice 9.12 avec corrigé * Méthode balistique. Déterminer (avec 5 décimales significatives), une
valeur de α telle que la solution de l’équation y ′′ = 1 + y 3 avec les conditions initiales y(0) = 1 et
y ′ (0) = α vérifie y(1) = 1.
On pourra travailler par dichotomie et même appeler une fonction programmée dans le chapitre précé-
dent ! α = −0.83016 est une solution.
Ey
Exercice 9.13 Le schéma d’Euler implicite consiste à remplacer la relation :
yk+1 = yk + hF (tk , yk )
par :
yk+1 = yk + hF (tk+1 , yk+1 ).
Bien entendu, cette relation ne donne pas explicitement yk+1 en fonction de yk (d’où son nom) et néces-
site donc à chaque étape une résolution d’équation de la forme Φ(yk+1 ) = 0. Puisque yk+1 est censé
t
être proche de yk , on dispose d’une bonne première approximation.
1 Programmer la méthode d’Euler implicite.
2 Tester cette méthode sur l’équation y ′ = −3y sur [0, 10], avec la condition initiale y(0) = 1. Tester des
gh
pas entre 0.1 et 2.
3 Comparer sur ce même exemple avec la méthode d’Euler usuelle (explicite).
Il suffit par exemple de changer y = y + h * F(t, y) en y = fsolve(lambda z : z - y - F(t+h,z),y),
après avoir chargé la fonction fsolve via from scipy.optimize import fsolve.
On peut voir figure 9.18 que cette méthode est plus stable que la méthode explicite (figure 9.19). Elle
reste cependant d’ordre 1.
i
pyr
Co
exp-implicite.pdf
Figure 9.18
Euler implicite. Pour h de l’ordre de 1, l’erreur est assez importante.
Livre_silo 30 août 2013 16:32 Page 243
es
9 – Résolution numérique d’équations différentielles
243
l
rol
t Ey
exp-plicite.pdf
i gh
Figure 9.19
Euler explicite. Cette fois, il faut h plus petit, sans quoi il y a divergence.
pyr
Pour le mathématicien
Exercice 9.14 Montrer que pour la méthode de Heun, l’erreur de consistance dans la résolution de y ′ = y
e−1
sur [0, 1] avec y(0) = 1 est équivalente à ·
6n2
( )
Co
0 −1
Exercice 9.15 Le portrait de phase associé à la matrice A = est représenté figure 9.20 ; il
1 0
est constitué de trajectoires de couples (x, y) vérifiant le système différentiel autonome Z ′ = AZ, avec
( )
x
Z= .
y
Représenter les portraits de phase correspondant aux matrices suivantes :
( ) ( ) ( ) ( )
−0.2 −1 1 0 1 0 1 1
, , ,
1 −0.2 0 2 0 −2 0 1
On pourra par exemple écrire np.dot(A,x) pour faire un produit matriciel. La figure 9.21 montre ce que
l’on pourra obtenir (dans le dernier cas, il faut batailler un peu pour trouver des conditions initiales satis-
faisantes).
Livre_silo 30 août 2013 16:32 Page 244
es
Informatique pour tous
244
l
rol
t Ey
portrait-simple.pdf
gh
Figure 9.20
Un portrait de phase des plus basiques
i
pyr
Co
portrait-multiple.pdf
Figure 9.21
Quatre portraits de phase
Livre_silo 30 août 2013 16:32 Page 245
es
9 – Résolution numérique d’équations différentielles
245
l
rol
Exercice 9.16 Les trois systèmes différentiels ′
( suivants
) correspondent à des équations de la forme Y =
0 −1
Fk (Y ), avec Fk (0) = 0 et Jac(Fk )0 = : ils se linéarisent tous de la même façon.
1 0
{ { {
x′ −y − x(x2 + y 2 ) x′ −y − x
(x2 + y2 ) x′ −y + x
(x2 + y2 )
Ey
= = 10
= 20
y′ = x − y(x2 + y 2 ) y′ = x+ y
10
(x2 + y2 ) y′ = x+ y
20
(x2 + y2 )
Visualiser les courbes intégrales issues de (1, 0) dans les trois cas et conclure quant au danger des linéa-
risations à tout va !
Accessoirement, le lecteur pourra résoudre explicitement les équations via un passage en polaire : le
théorème de relèvement autorise à écrire x(t) + iy(t) = ρ(t)eiθ(t) avec ρ et θ de classe C 1 et qui
vérifient des équations différentielles simples.
t
Les courbes sont présentées figure 9.22.
i gh
pyr
trois-portraits.pdf
Co
Figure 9.22
Trois comportements différents, malgré des linéarisations identiques
Livre_silo 30 août 2013 16:32 Page 246
es
Informatique pour tous
246
l
rol
t vdp.pdf
Ey
i gh
Figure 9.23
Oscillateur de van der Pol : il y a un « cycle limite ».
pyr
Pour le physicien
Exercice 9.18 Pour un pendule amorti, l’angle entre le pendule et la verticale vérifie une équation de la
forme θ̈ = −k1 sin θ − k2 θ̇.
Pour k1 = k2 = 1, calculer les trajectoires issues des conditions initiales θ(0) = 0 et θ̇(0) ∈ {1, 2, 5, 8}.
Représenter à chaque fois la trajectoire (graphe de θ) et la courbe correspondante dans le portrait de
Co
Cette équation est vérifiée tant que N > 0. Quand N s’annule, le glaçon perd le contact avec l’igloo.
On pourra prendre g = 9.8 et m = 1 (un beau glaçon, donc !)
1 Résoudre numériquement cette équation différentielle.
2
2 Vérifier « expérimentalement » que l’angle de décrochage tend vers arccos lorsque la vitesse initiale
3
tend vers 0.
Livre_silo 30 août 2013 16:32 Page 247
es
9 – Résolution numérique d’équations différentielles
247
l
rol
Figure 9.24
Trajectoires des solutions θ pour
le pendule amorti
t Ey pendule-amorti.pdf
i gh
Figure 9.25
pyr
portrait-amorti.pdf
3 On suppose maintenant que le glaçon est soumis à une force de frottement, ce qui conduit à l’équation
(avant décrochage) :
( .. ) ( ) ( )
rθ sin θ −εkN
m .2 = mg + ,
−rθ − cos θ N
Livre_silo 30 août 2013 16:32 Page 248
es
Informatique pour tous
248
l
rol
.
avec ε ∈ {±1} du signe de θ.
4 Résoudre l’équation différentielle dans ce cadre.
.
5 Vérifier que si on fixe par exemple θ (0) = 0.3, alors il existe une valeur seuil de k au-delà de laquelle le
glaçon va s’arrêter.
Pour prendre en compte le décrochage, on peut traiter les solutions après résolution (sans test interne, c’est
Ey
le plus simple), ou bien inclure dans la fonction de l’équation différentielle un test vérifiant le décrochage.
Au moment du décrochage, on peut décider de bloquer la trajectoire. La figure 9.26 montre différents
angles de décrochage en fonction de la vitesse initiale. On voit figure 9.27 que le seuil critique pour k
est de l’ordre de 0.96. Par dichotomie, on pourra obtenir quelques décimales supplémentaires. De façon
automatique si possible !
t
gh
glacon1.pdf
i
pyr
Co
Figure 9.26
On s’approche de arccos(3/2) ≃ 0.84.
Exercice 9.20 avec corrigé : Paraboles (ou autres) de sécurité. On jette un projectile M à partir du
point O avec une vitesse v0 constante, mais faisant un angle variable avec l’horizontale. Si le projectile est
soumis seulement à la gravitation, la trajectoire est connue : ce sera une parabole. On démontre même
de façon classique que l’ensemble des trajectoires est « enveloppé » par une parabole, dite de sécurité.
Cet exercice a pour objet la visualisation de cette parabole.
d2 −−→ →
1 Résoudre numériquement l’équation 2 OM = − g.
dt
2 Représenter une vingtaine de paraboles, avec des angles variables.
3 On suppose maintenant que le projectile est soumis à une force de frottement de la forme −k− →
v.
Modifier les équations et représenter à nouveau quelques trajectoires du projectile.
Ici, on pourra stopper les trajectoires dès que z devient strictement négatif. Sans frottement, on voit la
parabole se dessiner sur la figure 9.28. Sur la figure 9.29, on voit l’effet des frottements, qui raccourcissent
les trajectoires.
Livre_silo 30 août 2013 16:32 Page 249
es
9 – Résolution numérique d’équations différentielles
249
l
rol
t glacon2.pdf
Ey
i gh
Figure 9.27
pommes-libres.pdf
Figure 9.28
v02 g
En gras, la parabole de sécurité, d’équation z = − 2 x2
2g 2v0
Livre_silo 30 août 2013 16:32 Page 250
es
Informatique pour tous
250
l
rol
Figure 9.29
Extension de l’exercice :
déterminer (numériquement)
l’enveloppe des courbes.
t Ey pommes-freinees.pdf
i gh
Exercice 9.21 avec corrigé : Cinétique chimique. On s’intéresse ici aux concentrations de trois produits
(A, B et C) au cours du temps. Deux réactions entrent en jeu : A −→ B d’une part et B −→ C d’autre
pyr
part. Ces réactions sont d’ordre 1 : leur vitesse est proportionnelle à la concentration du réactif. Les
concentrations respectent donc des lois de la forme :
d[A]
−α[A]
=
dt
d[B]
= α[A] − β[B]
dt
Co
d[C]
= β[B]
dt
Livre_silo 30 août 2013 16:32 Page 251
es
9 – Résolution numérique d’équations différentielles
251
l
rol
t Ey
cinetique.pdf
gh
Figure 9.30
i
pyr
Co
cinetique2.pdf
Figure 9.31
Dans ces deux cas, l’une des deux réactions est sensiblement plus rapide que l’autre.
Livre_silo 30 août 2013 16:32 Page 252
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 253
l es
rol
t Ey Quatrième partie
Bases de données
i gh
pyr
le langage SQL.
Livre_silo 30 août 2013 16:32 Page 254
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 255
l es
rol
10
Ey
Algèbre relationnelle
t
i gh
Livre_silo 30 août 2013 16:32 Page 256
es
Informatique pour tous
256
l
rol
10.1 Limites des structures de données plates
pour la recherche d’informations
L’utilité de recourir à un modèle relationnel sera illustrée au fil de l’exemple réaliste sui-
Ey
vant, où on verra que les structures plates telles que les tableaux ne conviennent pas pour
représenter et surtout rechercher certains types d’informations.
• On souhaite représenter l’ensemble des élèves de CPGE d’un lycée tout en sachant qu’ils
sont regroupés par classe.
• Les élèves et les classes ont des attributs propres : le nom d’un élève, le lycée où il a
effectué sa terminale, la filière d’une classe (MPSI, PCSI…), le numéro de la classe au
sein de l’établissement (MPSI 2…).
t
• On veut pouvoir rechercher facilement :
– les élèves appartenant à une même classe ;
gh
– les élèves partageant un attribut commun, par exemple ceux ayant effectué leur ter-
minale dans un lycée donné.
On prend pour exemple les trois classes préparatoires d’un lycée nommé Charles-Le-Petit :
• MPSI 1, élèves :
i
pyr
On peut présenter cette structure par un tableau de classes contenant chacune un tableau
d’élèves :
lycee = [
("MPSI", 1, [
("Évariste", "Lycée Adams"),
("Léa", "Lycée Cleese")
]),
("MPSI", 2, [
("Coralie", "Lycée Adams"),
("Augustin", "Lycée Chapman")
]),
("PCSI", 1, [
("Johanna", "Lycée Cleese"),
("Pierre", "Lycée Adams")
])
]
Livre_silo 30 août 2013 16:32 Page 257
es
10 – Algèbre relationnelle
257
l
rol
Récupérer la liste des élèves d’une classe est très simple. Il suffit de parcourir le tableau des
classes jusqu’à obtenir celle qu’on veut, puis de renvoyer son sous-tableau d’élèves :
def recherche_classe(filiere, numero):
for classe in lycee:
if classe[0] == filiere and classe[1] == numero:
Ey
return classe[2]
return None
Pour obtenir l’ensemble des élèves ayant passé leur terminale dans le lycée Adams, il faut
parcourir chaque classe et filtrer les élèves concernés. Cette recherche est plus complexe que
la précédente ; on constate notamment qu’il faut effectuer deux boucles for imbriquées :
une sur les classes, puis une sur leurs élèves.
def recherche_term(term):
t
eleves = []
for classe in lycee:
gh
for eleve in classe[2]:
if eleve[1] == term:
eleves.append(eleve)
return eleves
On note que la représentation des données est implicitement choisie pour faciliter le type
i
de recherche prévu.
pyr
Si on choisit une autre représentation, en créant un tableau des lycées et en leur associant
les élèves qui y étaient en terminale, c’est la recherche des élèves provenant du même lycée
qui sera facilitée. En revanche, pour trouver l’ensemble des élèves d’une classe, il faudra
parcourir tous les lycées et tous les élèves.
Dans cet exemple, un élève a des liens d’appartenance à la fois avec les classes et avec les
lycées. Or, la représentation du problème sous forme de tableau contraint à privilégier un
de ces liens. De plus, on veut pouvoir effectuer des recherches indépendamment de tout
Co
lien, par exemple rechercher tous les élèves portant le même nom.
Livre_silo 30 août 2013 16:32 Page 258
es
Informatique pour tous
258
l
rol
Chacune de ces représentations est appelée relation, ou table dans le vocabulaire des bases
de données. Cette représentation rappelle la notion de relation binaire vue dans le cadre
du cours de mathématiques.
On considère donné un ensemble fini A, dont les éléments sont appelés des attributs,
Ey
un ensemble D et une application dom de A dans les sous-ensembles de D. Si A ∈ A,
dom(A) est appelé le domaine de A. Cette notion est à rapprocher de la notion de types
dans les langages de programmation.
Dans le cadre de l’exemple précédent, le numéro d’une classe est un attribut dont le do-
maine est constitué des entiers strictement positifs ; la filière d’une classe est un attribut
dont le domaine est l’ensemble des filières de CPGE.
t
On appelle schéma relationnel un n-uplet de la forme S = (A1 , · · · , An ) ∈ An où les Ai
gh
sont distincts deux à deux. On pourra noter S = ((A1 , dom(A1 )) , · · · , (An , dom(An )))
pour rappeler les domaines de ses attributs.
Ainsi, pour la représentation des classes, les attributs sont filière et numéro. Le schéma est
le suivant :
((filière, {MPSI, PCSI…}), (numéro, N))
i
pyr
Livre_silo 30 août 2013 16:32 Page 259
es
10 – Algèbre relationnelle
259
l
rol
Dans la mesure où le nombre de n-uplets est fini, on peut représenter la relation sous la
forme d’un tableau :
classe
Filière Numéro
Ey
MPSI 1
MPSI 2
PCSI 1
On va considérer un autre exemple : on souhaite réaliser la relation associée aux livres dans
une bibliothèque. On pourra par exemple prendre le schéma relationnel suivant :
((Titre, Texte), (Auteur, Texte), (Année-de-publication, Z))
t
où Texte désigne l’ensemble des phrases que l’on peut entrer à l’aide d’un clavier.
gh
Ce schéma pourra être représenté par le diagramme suivant :
Dessins/bdd/inventaire.pdf
i
pyr
Il n’est donc pas possible dans l’exemple précédent d’indiquer que la bibliothèque possède
deux exemplaires d’un même livre. On peut cependant étendre le schéma avec un attribut
nombre-d’exemplaires de domaine N pour résoudre ce problème.
Exercice 10.1 Proposer un domaine approprié pour des attributs représentant :
• une adresse e-mail ;
• une nationalité ;
• un âge.
Livre_silo 30 août 2013 16:32 Page 260
es
Informatique pour tous
260
l
rol
Exercice 10.2 Proposer des schémas relationnels représentant respectivement :
• l’inventaire d’un supermarché ;
• le parc informatique d’une entreprise.
Dans chaque cas, donner un exemple significatif de relation associée au schéma proposé.
Ey
10.3 Opérateurs sur le modèle relationnel
10.3.1 Description des recherches
Une fois les données représentées sous forme relationnelle, il faut pouvoir les exploiter
pour y rechercher des informations précises. Pour cela, on doit tout d’abord être capable
t
d’exprimer ces recherches sous une forme précise. On va donner ici un aperçu de ce qui
gh
sera formellement défini plus loin : le langage de requêtes SQL.
On considère une relation eleve et l’expression suivante :
SELECT nom FROM eleve WHERE prenom = 'Serge' OR prenom = 'Muriel';
Elle correspond à une description précise de la recherche : obtenir les noms de famille de tous
i
pyr
Livre_silo 30 août 2013 16:32 Page 261
es
10 – Algèbre relationnelle
261
l
rol
peuvent être coûteuses en mémoire ou en temps de calcul, alors que d’autres seront très
efficaces. Déterminer une bonne traduction s’appelle optimiser la recherche.
Ey
des images des éléments de E par f . Si E est un ensemble et P une propriété pouvant
être décidée sur les éléments de E, on note { x ∈ E | P (x) } l’ensemble des éléments
de E qui vérifient P . Par exemple, l’ensemble des entiers pairs est { 2p | p ∈ N } =
{ n ∈ N | ∃ p ∈ N, n = 2p }.
pyr
Livre_silo 30 août 2013 16:32 Page 262
es
Informatique pour tous
262
l
rol
livre2
Titre Auteur Tome
Ey
Union
L’union de deux relations R1 (S) et R2 (S) est l’ensemble des valeurs comprises dans R1
ou dans R2 . On la note R1 ∪ R2 de schéma S.
Voici le résultat de l’union des deux relations livre1 et livre2 :
livre1 ∪ livre2
t
Titre Auteur Tome
gh
Madame Bovary Gustave Flaubert 1
Le comte de Monte-Cristo Alexandre Dumas 1
Le comte de Monte-Cristo Alexandre Dumas 2
Madame Bovary Gustave Flaubert 1
Le père Goriot Honoré de Balzac 1
i
Intersection
pyr
L’intersection de deux relations R1 (S) et R2 (S) est l’ensemble des valeurs comprises
dans R1 et dans R2 . On la note R1 ∩ R2 de schéma S.
Voici le résultat de l’intersection des deux relations livre1 et livre2 :
livre1 ∩ livre2
Titre Auteur Tome
Co
Différence
La différence entre deux relations R1 (S) et R2 (S) est l’ensemble des valeurs comprises
dans R1 mais pas dans R2 . On la note R1 − R2 de schéma S.
Voici le résultat de la différence entre les deux relations livre1 et livre2 :
livre1 − livre2
Titre Auteur Tome
Livre_silo 30 août 2013 16:32 Page 263
es
10 – Algèbre relationnelle
263
l
rol
POUR ALLER PLUS LOIN Opérations ensemblistes sur des schémas compatibles
Ici, on a uniquement considéré des relations dont les schémas sont identiques.
En réalité, on peut effectuer ces mêmes opérations ensemblistes dès que les schémas des
relations comportent le même nombre d’attributs et que les domaines de ces derniers sont
Ey
identiques.
On parle dans ce cas de schémas compatibles. Il est à noter que lorsqu’on effectue une
opération ensembliste sur deux schémas compatibles, il faut choisir un schéma, et en par-
ticulier des noms d’attributs, pour la nouvelle relation construite. On prend généralement
la convention d’utiliser les noms d’attributs issus de la première relation.
Une telle opération reste délicate car deux attributs (par exemple Âge et Prix) peuvent avoir
le même domaine N sans avoir la même signification.
t
10.3.3 Projection
gh
Soit R(S) une relation de schéma S et X ⊂ S. On appelle projection de R selon X la
relation :
πX (R) = { e(X) | e ∈ R }
i
élève
Nom Prénom Classe
Meyer MPSI 1
Michel MPSI 1
Benoit PCSI 2
Michel PCSI 1
Une projection ne contient pas forcément autant de valeurs que la relation de départ. En
effet, plusieurs valeurs peuvent être fusionnées. Par exemple, la projection πClasse (élève) ne
contient que trois valeurs, une par classe. L’exercice suivant précise cette remarque.
Exercice 10.3 Soit R(S) une relation et X ⊂ S.
Montrer que #πX (R) ⩽ #R.
Livre_silo 30 août 2013 16:32 Page 264
es
Informatique pour tous
264
l
rol
10.3.4 Sélection
Sélection simple
Si R(S) est une relation de schéma S, A ∈ S et a ∈ dom(A), on appelle sélection de R
Ey
selon A = a la relation obtenue en sélectionnant dans R uniquement les valeurs e telles
que e.A = a. On la note σA=a (R). On a donc :
σA=a (R) = { e ∈ R | e.A = a }
pyr
1 10 3 7
2 7 3 3
3 8 7 1
2 7 3 3
Sélection composée
Grâce aux opérateurs ensemblistes, il est possible d’exprimer des conditions complexes. On
va supposer que l’on a réussi à sélectionner les relations R1 et R2 composées des valeurs
de R satisfaisant respectivement les conditions C1 et C2 .
Livre_silo 30 août 2013 16:32 Page 265
es
10 – Algèbre relationnelle
265
l
rol
• Pour obtenir les valeurs vérifiant C = C1 ET C2 , on calcule R1 ∩ R2 .
• Pour obtenir les valeurs vérifiant C = C1 OU C2 , on calcule R1 ∪ R2 .
• Pour obtenir les valeurs ne vérifiant pas C1 , c’est-à-dire vérifiant C = NON C1 , on
calcule R − R1 .
Ey
On note alors σC (R) la relation obtenue en effectuant cette décomposition d’une condi-
tion complexe à l’aide des opérateurs ensemblistes et des sélections simples.
En reprenant la relation élève vue précédemment, on peut réaliser la sélection complexe
satisfaisant la condition :
« Soit le nom de l’élève est Michel et son prénom n’est pas Zoé, soit il est en PCSI 1. »
Elle se traduit par la relation suivante :
t
R = [σNom=“Michel” (élève) − σPrénom=”Zoé” (σNom=“Michel” (élève))] ∪ σClasse=“PCSI1” (élève)
gh
R
Nom Prénom Classe
et donc :
En effet, si une valeur est dans σC2 (R) − σC1 (σC2 (R)), alors elle vérifie C2 . Comme elle
n’est pas dans σC1 (σC2 (R)), elle ne vérifie pas C1 . Elle est donc bien dans σC2 (R)−σC1 (R).
L’autre inclusion est immédiate.
Livre_silo 30 août 2013 16:32 Page 266
es
Informatique pour tous
266
l
rol
10.3.5 Renommage
Il est possible, souvent pour lever une ambiguïté, de renommer un attribut d’une relation à
l’aide d’un opérateur dit de renommage. La relation obtenue est alors identique à la relation
de départ, mis à part le schéma qui a été changé pour présenter le nouveau nom.
Ey
Soit S = (A1 , · · · , An ) un schéma, i ∈ J1; nK et B un attribut tel que dom(B) =
dom(Ai ). On note :
Dans les valeurs d’une relation, les attributs n’apparaissent que par l’intermédiaire de leur
domaine. Il est donc possible de substituer au schéma de R, un schéma obtenu par renom-
mage.
i
On notera ρA1 ,...,An ←B1 ,...,Bn (R(S)) = R(ρA1 ,...,An ←B1 ,...,Bn (S)). Ainsi, on peut
pyr
considérer l’opération de renommage comme étant définie directement sur les relations.
Dans la relation élève vue précédemment, en renommant Nom en LastName et Prénom en
FirstName, on obtient :
ρNom,Prénom←LastName,FirstName (élève)
LastName FirstName Classe
On remarque qu’effectivement, les valeurs de la relation ne sont pas affectées par ce re-
nommage.
Livre_silo 30 août 2013 16:32 Page 267
es
10 – Algèbre relationnelle
267
l
rol
SAVOIR-FAIRE Traduire dans le langage de l’algèbre relationnelle
des requêtes simples écrites en langage courant
Pour traduire une requête simple, il faut bien entendu identifier sur quelle relation on
Ey
travaille. Il faut ensuite distinguer, parmi les informations que contient cette relation,
celles qui jouent un rôle pour la requête. On peut procéder dans l’ordre suivant :
1 Traduire les critères présents dans la requête sous forme de sélections.
2 Si nécessaire, utiliser des opérations ensemblistes pour combiner les sélections
entre elles.
3 Utiliser des projections pour ne conserver que les informations utiles.
Cet ordre convient pour la plupart des requêtes courantes, mais il n’est pas forcé-
t
ment le plus approprié pour des raisons d’efficacité : si un attribut ne joue aucun rôle
gh
dans la requête, il est plus judicieux de l’éliminer par projection dès le début pour ré-
duire le volume de données à traiter. De manière plus générale, le choix de l’ordre le
plus efficace pour traiter les différentes opérations composant une requête s’appelle
l’optimisation des requêtes.
i
pyr
classe
id Filière Numéro Professeur
1 MPSI 1 Euclide
2 MPSI 2 Turing
3 PCSI 1 Horner
4 PCSI 2 Euler
Co
élève
Nom Prénom Classe Note
Livre_silo 30 août 2013 16:32 Page 268
es
Informatique pour tous
268
l
rol
1 Il suffit d’effectuer une projection sur l’attribut correspondant. Les doublons sont fusionnés :
πFilière (classe)
Ey
σFilière=P CSI (classe)
3 On sélectionne les élèves selon chacune des classes, puis on ne garde que les prénoms :
4 On peut commencer par ne garder que les noms et les notes, puis sélectionner selon cette note :
pyr
quement des relations suivant un schéma relationnel, à définir des valeurs et à interroger
la base pour rechercher des valeurs particulières.
Une base de données est constituée d’un ensemble de relations, ainsi que de leurs schémas
relationnels. Elle est stockée dans un fichier ou un jeu de fichiers. Un gestionnaire de bases
de données va gérer cette base et permettre à des utilisateurs d’y accéder ou de la modifier.
Le gestionnaire joue donc un rôle d’intermédiaire entre la vision idéale, issue du modèle
Co
Livre_silo 30 août 2013 16:32 Page 269
es
10 – Algèbre relationnelle
269
l
rol
Pour faciliter les manipulations, on peut utiliser une interface graphique. Il existe de nom-
breux logiciels de ce type ; dans les travaux pratiques proposés dans l’annexe A, on a choisi
de présenter :
• MySQL comme gestionnaire de bases de données, qui est un logiciel libre très répandu ;
Ey
• phpMyAdmin comme interface graphique, qui est à la fois très puissante et très visuelle.
en présentant la traduction des opérations vues précédemment. Toutes les requêtes de re-
10.4.2 Projection
Pour effectuer la projection πA1 ,··· ,An (R), on évalue la requête :
SELECT A1, ..., An FROM R;
Co
10.4.3 Sélection
En fait, la commande SELECT admet un paramètre supplémentaire, WHERE, qui impose une
condition. La sélection s’écrit donc :
SELECT * FROM R WHERE A=a;
La commande SELECT de SQL réalise donc simultanément une sélection et une projection.
Cela rend les requêtes plus concises car, comme on l’a remarqué dans la section précédente,
ces deux opérations vont souvent de pair.
Livre_silo 30 août 2013 16:32 Page 270
es
Informatique pour tous
270
l
rol
Pour les sélections composées, il est possible d’utiliser directement les opérateurs booléens
dans la condition. Par exemple, la condition :
« Soit le nom de l’élève est Michel et son prénom n’est pas Zoé, soit il est en PCSI 1 »
pourra directement être traduite par la requête :
Ey
SELECT * FROM eleve WHERE (nom = 'Michel' AND prenom != 'Zoé') OR classe = 'PCSI 1';
10.4.5 Renommage
i
pyr
Livre_silo 30 août 2013 16:32 Page 271
es
10 – Algèbre relationnelle
271
l
rol
Exercice 10.5 Traduire en langage SQL les requêtes de l’exercice 10.4.
Ey
3 SELECT prenom FROM eleve WHERE classe=1 OR classe=3;
client qui effectue des demandes auprès du serveur, lequel centralise les informations. La
richesse de ce point de vue vient du fait que plusieurs clients peuvent accéder simultané-
pyr
ment au serveur.
Co
Dessins/bdd/client-serveur.pdf
Ainsi, un client peut créer une relation, pendant qu’un deuxième modifie des valeurs d’une
relation existante et que le troisième effectue une recherche. Une telle utilisation nécessite
cependant de veiller à l’ordre dans lequel sont traitées les demandes. Par exemple si on
effectue deux fois de suite la même recherche, elle peut produire des résultats différents
si quelqu’un d’autre a modifié des valeurs s’y trouvant. La bonne approche consiste alors
à grouper un ensemble de requêtes et à demander au serveur de les traiter de manière
atomique, c’est-à-dire d’une traite. Un tel ensemble est appelé une transaction.
Livre_silo 30 août 2013 16:32 Page 272
es
Informatique pour tous
272
l
rol
La communication entre le client et le serveur passe le plus souvent par une transmission
d’informations sur un réseau, comme le réseau Internet. En effet, deux applications au sein
d’un même ordinateur peuvent également communiquer selon ce mode client-serveur.
Ey
L’architecture client-serveur est rarement mise en pratique telle quelle. En effet, il n’est pas
nécessaire de traduire directement les opérations que l’on souhaite faire sur les données en
termes de modèle relationnel.
On va prendre l’exemple d’un professeur devant rentrer ses notes de devoir surveillé. Il
dispose le plus souvent d’une application fournie par son établissement, dans laquelle il
t
peut créer une correction associée à sa classe et remplir les notes correspondantes. L’élève
pourra, lui, consulter cette même application pour voir sa note. Enfin, l’administration
gh
pourra consulter les notes de l’ensemble des matières afin d’éditer des bulletins.
Ces différents types d’utilisateurs n’ont pas intérêt à accéder directement à la base de don-
nées. D’une part, on ne souhaite pas qu’un élève puisse voir l’ensemble des notes de ses
camarades, ni que le professeur de mathématiques puisse créer un devoir de sciences in-
dustrielles. D’autre part, cela nécessiterait de connaître une grande partie du modèle rela-
i
pyr
• le tiers utilisateur ;
• le tiers applicatif ;
• le tiers base de données.
Pour l’utilisateur, le tiers applicatif joue un rôle d’interface ; pour la base, il joue le rôle
d’un client. La communication entre les tiers passe le plus souvent par un réseau.
Livre_silo 30 août 2013 16:32 Page 273
es
10 – Algèbre relationnelle
273
l
rol
Ey
Dessins/bdd/trois-tiers.pdf
t
gh
10.6 Exercices
Exercice 10.6 Proposer un domaine approprié pour des attributs représentant :
• une date ;
• un mot de passe ;
i
• un classement à un concours ;
• une note de musique.
pyr
4 Soit • une des opérations ensemblistes {∩, ∪, −} et R1 (S), R2 (S) des relations de même schéma.
Montrer que σA=a (R1 • R2 ) = σA=a (R1 ) • σA=a (R2 ).
Livre_silo 30 août 2013 16:32 Page 274
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 275
l es
rol
11
t Ey
Base de données
gh
relationnelle
i
pyr
Livre_silo 30 août 2013 16:32 Page 276
es
Informatique pour tous
276
l
rol
11.1 Clé primaire
Lorsque l’on veut effectuer des rapprochements entre les valeurs de différentes relations, il
est important de disposer d’un mécanisme efficace pour accéder à ces valeurs, tester leur
existence ou encore les trier : c’est le rôle de la notion de clé.
Ey
11.1.1 Clé
Soit R(S) une relation de schéma S et K ⊂ S. On dit que K est une clé pour R si et
seulement si pour toutes valeurs t1 , t2 ∈ R telles que t1 (K) = t2 (K) on a t1 = t2 .
On considère par exemple la relation :
t
élève
gh
Nom Prénom
Meyer Zoé
Michel Nolwenn
Benoit Paul
Michel Zoé
i
On va examiner les différents sous-ensembles de S = {Nom, Prénom}. L’ensemble {Nom}
pyr
n’est pas une clé car les deux valeurs (Michel, Nolwenn) et (Michel, Zoé) ont même valeur
pour cet attribut. De même, {Prénom} n’est pas non plus une clé. En revanche, la paire
{Nom, Prénom} est une clé.
On remarque que le schéma d’une relation constitue toujours une clé pour celle-ci. Ainsi,
une relation possède toujours au moins une clé. On recherche alors une bonne clé, c’est-
à-dire une clé comportant le plus petit nombre possible d’attributs.
Co
πA : R 7→ dom(A)
e 7→ e.A
est injective.
Livre_silo 30 août 2013 16:32 Page 277
es
11 – Base de données relationnelle
277
l
rol
Considérons la relation suivante :
livre
Titre Auteur Année-de-publication
Ey
Traité du calcul différentiel Euler 1755
Lettres à une princesse d’Allemagne Euler 1768
L’attribut titre est une clé primaire, mais pas l’attribut auteur, car il existe deux livres écrits
par Euler. On pourra l’indiquer sur un diagramme par un symbole de clé au niveau des
attributs :
t
gh
Dessins/bdd/livre-cle.pdf
Il ne s’agit que d’une indication, car le schéma seul ne permet pas de garantir qu’un attribut
i
soit une clé primaire. En effet, cela dépend des valeurs de la relation. Si l’on ajoute une
valeur dont le titre est identique à l’un des trois titres déjà présents, Titre ne sera plus une
pyr
clé primaire.
Ainsi, dans la relation élève présentée ci-avant il n’existe pas de clé primaire.
Donc, une relation possède toujours au moins une clé, mais ne possède pas forcément de
clé primaire.
Co
Livre_silo 30 août 2013 16:32 Page 278
es
Informatique pour tous
278
l
rol
POUR ALLER PLUS LOIN Clé étrangère
On peut considérer que le domaine de livre-emprunté est constitué exactement des titres
apparaissant dans la relation livre. On dit alors que livre est le domaine de l’attribut livre-
emprunté et cet attribut est appelé une clé étrangère. On peut alors noter le schéma de
manière plus précise :
Ey
((nom, Texte), (livre-emprunté, livre))
Les liens entre ces deux relations peuvent être représentés dans un diagramme, par une
flèche depuis l’attribut vers la clé primaire.
Dans le cas présent, cela donne le diagramme suivant :
t
gh
Dessins/bdd/livre_emprunteur.pdf
pyr
Exercice 11.2 * La relation élève présentée p. 276 montre que si on représente des personnes dans une
relation, en général l’attribut Nom ne constitue pas à lui seul une clé pour la relation ; il en va de même
pour Prénom.
1 La paire d’attributs {Nom, Prénom} constitue-t-elle toujours une clé pour une relation de ce genre ?
2 Comment peut-on étendre le schéma relationnel pour assurer l’existence d’une clé ? Identifier des so-
lutions utilisées dans des situations réelles et en évaluer les limites.
3 Identifier des situations réelles de relations représentant des personnes dans lesquelles un attribut du
schéma relationnel est prévu pour être une clé primaire. Comment assure-t-on que cette clé sera bien
Co
primaire ?
Livre_silo 30 août 2013 16:32 Page 279
es
11 – Base de données relationnelle
279
l
rol
11.2.1 Produit cartésien et division cartésienne
Comme l’union ou l’intersection, la notion de produit cartésien est issue des opérateurs
ensemblistes.
Si R(S) et R′ (S ′ ) sont deux relations de schémas disjoints, leur produit cartésien est :
Ey
R × R′ = { (v1 , · · · , vn , v1′ , · · · , vm
′
) | (v1 , · · · , vn ) ∈ R et (v1′ , · · · , vm
′
) ∈ R′ }.
élève
pyr
enseignant
Nom-élève
Nom-enseignant
Meyer
Durand
Martin
Petit
Bernard
élève × enseignant
Nom-élève Nom-enseignant
Meyer Durand
Martin Durand
Bernard Durand
Meyer Petit
Martin Petit
Bernard Petit
Livre_silo 30 août 2013 16:32 Page 280
es
Informatique pour tous
280
l
rol
La division cartésienne entre deux relations est alors définie de la même manière que la
division euclidienne est définie par rapport à la multiplication entière.
La relation R ÷ R′ est la plus grande relation, vis-à-vis de l’inclusion, telle qu’il existe une
relation R′′ vérifiant :
[(R ÷ R′ ) × R′ ] ∪ R′′ = R
Ey
[(R ÷ R′ ) × R′ ] ∩ R′′ = ∅
On vérifiera que cette définition est bien fondée dans l’exercice 11.16.
Bernard Petit
pyr
Petit
Durand n’est pas associé à toutes les valeurs d’élèves dans R (l’élève Martin n’a pas Durand
Co
Livre_silo 30 août 2013 16:32 Page 281
es
11 – Base de données relationnelle
281
l
rol
11.2.2 Jointure
Un opérateur de jointure sert à recoller deux relations. On ne considère ici que l’opérateur
de jointure symétrique, qui recolle deux relations de manière simple.
Soient R(S) et R′ (S ′ ) deux relations de schémas disjoints, et A ∈ S, A′ ∈ S ′ tels que
Ey
dom(A) = dom(A′ ). On note :
auteur
pyr
Nom Prénom
Flaubert Gustave
Balzac (de) Honoré
Proust Marcel
On va réaliser la jointure symétrique selon (Nom-auteur, Nom), ce qui donnera une nouvelle
Co
Livre_silo 30 août 2013 16:32 Page 282
es
Informatique pour tous
282
l
rol
Il est à noter que la présence des deux attributs A et A′ est redondante après jointure. On
peut éliminer cette redondance avec une projection :
πTitre,Nom,Prénom (livre [Nom-auteur = Nom] auteur)
Titre Nom Prénom
Ey
Madame Bovary Flaubert Gustave
Le père Goriot Balzac (de) Honoré
Dans cet exemple, l’attribut Nom est une clé primaire pour auteur ; donc, en réalisant la
jointure, on sait que l’on va récupérer un unique auteur. Ce cas est le plus courant, car
il permet effectivement d’éclater un attribut faisant référence à une clé primaire pour lui
t
substituer l’intégralité de la valeur à laquelle elle fait référence. Ici, on remplace le simple
nom de l’auteur par la valeur correspondante dans auteur.
gh
Cependant, rien n’oblige à considérer des clés primaires pour réaliser des jointures. On va
modifier auteur afin que Nom ne soit plus une clé primaire :
auteur’
Nom Prénom
i
Flaubert Gustave
pyr
Flaubert Jean-Claude
Balzac (de) Honoré
Proust Marcel
Ainsi, le titre Madame Bovary se trouve associé aux deux auteurs nommés Flaubert dans
auteur’.
On étend alors logiquement la notion de jointure au cas de recollement de plusieurs couples
d’attributs. On pourra noter par exemple :
Livre_silo 30 août 2013 16:32 Page 283
es
11 – Base de données relationnelle
283
l
rol
SAVOIR-FAIRE Concevoir une base constituée de plusieurs relations et
utiliser les jointures symétriques pour effectuer des requêtes croisées
On conçoit une base de données constituée de plusieurs relations lorsqu’on veut
Ey
isoler des informations concernant différentes entités (les livres et les auteurs dans
l’exemple) ; chaque relation est propre à une de ces entités et ne contient que les in-
formations qui la caractérisent.
Si les valeurs d’une relation R1 doivent faire référence à celles d’une relation R2 , on
prévoit dans R2 un attribut A2 , si possible une clé, auquel la relation R1 pourra faire
référence par le biais d’un de ses propres attributs A1 , qui sera une clé étrangère.
t
La jointure symétrique fait correspondre à chaque valeur de R1 toutes les informa-
tions de la valeur qui lui correspond dans R2 , ce qui autorise à effectuer une sélection
gh
selon ces informations.
Une même base de données contient souvent plus de deux relations, éventuellement
reliées deux à deux par le biais de divers attributs. On peut alors écrire des requêtes
élaborées au moyen de plusieurs jointures symétriques.
i
Exercice 11.3 On va considérer ici un ensemble de relations utiles pour gérer un complexe hôtelier.
pyr
Celui-ci est composé de différents bâtiments, identifiés par leur nom et leur nombre d’étoiles. Ils sont
représentés dans la relation batiment :
batiment
nom etoiles
Rose 3
Jasmin 2
Co
Lys 3
Les chambres comportent chacune un numéro, un nom de bâtiment et un nombre de fenêtres. Elles sont
représentées dans la relation chambre :
chambre
numero batiment fenetres
1 Rose 2
2 Rose 1
3 Rose 1
1 Jasmin 1
2 Jasmin 0
3 Jasmin 1
4 Jasmin 1
1 Lys 3
2 Lys 2
3 Lys 2
Livre_silo 30 août 2013 16:32 Page 284
es
Informatique pour tous
284
l
rol
Certaines chambres possèdent deux lits. On sépare donc les lits dans une autre relation, où ils comportent
chacun un identifiant numérique unique au sein du complexe, un numéro de chambre, ainsi que le bâti-
ment correspondant. Ils sont représentés dans la relation lit :
lit
Ey
idlit chambre batlit
1 1 Rose
2 1 Rose
3 2 Rose
4 3 Rose
5 1 Jasmin
6 2 Jasmin
t
7 2 Jasmin
gh
8 3 Jasmin
9 3 Jasmin
10 4 Jasmin
11 1 Lys
12 2 Lys
i
13 3 Lys
pyr
Enfin, les nuitées sont identifiées par le nom du client, l’identifiant du lit et par la date. Elles sont repré-
sentées dans la relation nuitee :
nuitee
client lit date
Lennon 1 15-08-1969
McCartney 8 18-08-1969
Co
Starr 3 03-07-1969
Harrison 2 01-08-1969
Page 10 05-08-1969
Plant 1 13-08-1969
Jones 11 05-08-1969
Bonham 7 02-08-1969
Townshend 1 08-08-1969
Livre_silo 30 août 2013 16:32 Page 285
es
11 – Base de données relationnelle
285
l
rol
Pour chacune des recherches suivantes, on indiquera une décomposition dans l’algèbre relationnelle, ainsi
que le résultat obtenu.
1 Obtenir le nom des clients ayant séjourné dans le bâtiment Jasmin.
2 Obtenir le nom des clients ayant séjourné dans un bâtiment 3 étoiles.
3 Obtenir le nom des clients ayant séjourné dans une chambre ayant au moins 2 fenêtres.
Ey
Dans tous les cas, il faudra finir par une projection πclient . On omet donc celle-ci, afin de se concentrer
sur les jointures et autres opérations.
1 L’information du bâtiment est accessible depuis la relation lit ; on effectue une jointure entre les relations
nuitee et lit, puis on sélectionne le bâtiment voulu :
σfenetres≥2 (nuitee [lit = idlit] lit [batlit = batiment, chambre = numero] chambre)
pyr
d’une sélection.
Concrètement, ce serait une très mauvaise idée de programmer les jointures par le biais
de ces deux autres opérations : si les relations R et R′ contiennent respectivement n et
n′ valeurs, le produit cartésien R × R′ construit une relation de n × n′ valeurs, qu’il faut
ensuite parcourir pour effectuer la sélection, d’où un coût quadratique.
Avec les tailles courantes des bases de données, un tel coût est impraticable et, de plus, la
relation R×R′ a peu de chances de tenir dans la mémoire vive disponible. Les gestionnaires
de bases de données disposent d’algorithmes efficaces pour effectuer la jointure de deux
tables de taille n avec une complexité en O(n log n).
11.2.3 Agrégation
Le dernier concept qu’on va présenter est assez complexe, mais très expressif. On va ima-
giner que l’on dispose de la relation suivante :
Livre_silo 30 août 2013 16:32 Page 286
es
Informatique pour tous
286
l
rol
relevé
Classe Élève Note
Ey
MPSI Bernard 9,25
PCSI Robert 14,0
PCSI Dubois 11,5
L’agrégation va servir à regrouper les élèves d’une même classe (ce groupe de valeurs est
appelé un agrégat) et à effectuer une opération sur chacun des agrégats. Ici par exemple,
on pourrait calculer la moyenne sur chaque classe, ce qui produit la relation suivante :
t
γ
Classe moyenne(Note) (relevé)
gh
Classe moyenne(Note)
MPSI 13.38
PCSI 11.08
Avant de définir formellement cette opération, il faut définir une notion de fonction pou-
vant être appliquée pour un nombre quelconque d’arguments, indépendamment de leur
i
ordre.
pyr
Livre_silo 30 août 2013 16:32 Page 287
es
11 – Base de données relationnelle
287
l
rol
Soit R(S) une relation, A ∈ S et f une fonction d’agrégation. On note f (R.A) le résultat
de l’application de la fonction f au n-uplet des valeurs de R pour l’attribut A.
Soient R(S) une relation, A1 , · · · , An , B1 , · · · , Bm ∈ S et f1 , · · · , fm des fonctions
d’agrégation. On note A1 ,...,An γ f1 (B1 ),...,fm (Bm ) (R) la relation obtenue :
Ey
• en regroupant les valeurs de R qui sont identiques sur les attributs A1 , · · · , An ;
• et en définissant de nouveaux attributs fi (Bi ) pour ces valeurs regroupées, pour tout
i ∈ J1; mK, par application de la fonction d’agrégation fi sur chacun de ces agrégats sur
l’attribut Bi .
Dans le cas particulier où l’on n’effectue pas de regroupement, on note
γf1 (B1 ),...,fm (Bm ) (R) l’opération.
t
Lorsque l’on n’effectue que le regroupement, on la note A1 ,...,An γ(R).
gh
ATTENTION Sélection en amont et en aval d’une agrégation
On considère une opération de la forme :
pyr
• La sélection σP1 est effectuée avant le regroupement en agrégat et limite ainsi les valeurs
considérées pour l’agrégation.
• La sélection σP2 porte, elle, sur les agrégats décorés des fonctions évaluées.
Livre_silo 30 août 2013 16:32 Page 288
es
Informatique pour tous
288
l
rol
Traduire les requêtes suivantes en opérations de l’algèbre relationnelle.
1 Calculer la moyenne des PCSI et celle des MPSI.
2 Calculer la moyenne de chaque classe.
3 Calculer la moyenne de la PCSI 2 et celle de la MPSI 2.
4 Sélectionner les classes dont la moyenne est supérieure ou égale à douze.
Ey
1
Filière γ moyenne(Note)
Filière moyenne(Note)
MPSI 12.375
PCSI 12.25
2
t
Filière,Numéro γ moyenne(Note)
Filière Numéro moyenne(Note)
gh
MPSI 1 11.33
MPSI 2 15.5
PCSI 1 13.5
PCSI 2 10.6
i
γ moyenne(Note) ◦ σNuméro=2
pyr
Filière,Numéro
Filière Numéro moyenne(Note)
MPSI 2 15.5
PCSI 2 10.6
ou en aval :
σNuméro=2 ◦ Filière,Numéro γ moyenne(Note)
Co
MPSI 2 15.5
PCSI 2 10.6
Il faut toujours privilégier les sélections en amont, car elles limitent le nombre de valeurs à regrouper
pour l’agrégation.
4 Ici, on ne peut effectuer la sélection qu’en aval :
σmoyenne(Note)≥12 ◦ Filière,Numéro γ moyenne(Note)
Filière Numéro moyenne(Note)
MPSI 2 15.5
PCSI 1 13.5
Livre_silo 30 août 2013 16:32 Page 289
es
11 – Base de données relationnelle
289
l
rol
11.2.4 Composition de requêtes complexes
Avec ces opérateurs supplémentaires, on peut exprimer des requêtes plus élaborées, faisant
notamment intervenir plusieurs relations. Leur écriture est alors plus délicate. En parti-
culier, la question de l’ordre dans lequel composer les différentes opérations devient alors
Ey
une vraie question de recherche.
pyr
1 MPSI 1 Euclide
2 MPSI 2 Turing
3 PCSI 1 Horner
Co
4 PCSI 2 Euler
élève
Nom Prénom Classe Note
Livre_silo 30 août 2013 16:32 Page 290
es
Informatique pour tous
290
l
rol
1 Calculer la moyenne des élèves de la classe 3.
2 Obtenir les élèves scolarisés en filière MPSI.
3 Déterminer la classe ayant la meilleure moyenne.
1 On sélectionne les élèves, puis on effectue une agrégation avec la fonction moyenne :
Ey
2 Il faut effectuer une jointure pour connaître la filière de chaque élève :
Il reste alors à sélectionner l’entrée correspondante dans classe, ce qu’on fait au moyen d’une jointure :
On peut terminer par une projection si on ne souhaite pas conserver toutes les informations concernant
cette classe. L’opération globale est donc ici composée de beaucoup d’opérations simples.
Les opérateurs complexes se traduisent également par des requêtes de recherche commen-
çant par la commande SELECT.
11.3.1 Jointure
La jointure simple R [A = B] R′ s’écrit :
SELECT * FROM R JOIN R' ON A=B;
Livre_silo 30 août 2013 16:32 Page 291
es
11 – Base de données relationnelle
291
l
rol
Exercice 11.6 Traduire dans le langage SQL les recherches effectuées dans l’exercice 11.3.
1 SELECT client FROM nuitee JOIN lit ON lit=idlit WHERE batlit = 'Jasmin';
ou encore
SELECT client FROM nuitee, lit WHERE lit=idlit AND batlit = 'Jasmin';
Ey
SELECT client FROM nuitee, lit, batiment
2
WHERE lit=idlit AND batlit=batiment AND etoiles=2;
Le tableau suivant fournit la correspondance entre les fonctions d’agrégation que l’on a
vues et celles de SQL :
Algèbre relationnelle SQL
i
comptage
COUNT
pyr
max MAX
min MIN
somme SUM
moyenne AVG
Il est possible de coupler ce calcul à une projection, comme on l’a vu précédemment, par
Co
exemple :
SELECT max(note) AS note FROM eleve WHERE classe=1;
Le résultat renvoyé par l’agrégation étant de type numérique, il est possible de l’utiliser
dans des comparaisons. Donc, pour obtenir les élèves ayant obtenu plus que la moyenne
de la classe, on écrit :
SELECT * FROM eleves WHERE note >= (SELECT avg(note) FROM eleves);
Exercice 11.7 Traduire en langage SQL les requêtes écrites à l’exercice 10.4.
2 SELECT nom, prenom, classe, note FROM eleve JOIN classe ON classe=id WHERE filiere='MPSI';
3 La première requête est la suivante, dans laquelle on renomme moyenne(Note) pour pouvoir y faire
référence par la suite :
SELECT classe, avg(note) AS moyenne FROM eleve GROUP BY classe;
Livre_silo 30 août 2013 16:32 Page 292
es
Informatique pour tous
292
l
rol
On peut ensuite l’imbriquer dans une autre requête, mais ceci se révèle vite malcommode, surtout dans
ce cas où le résultat de cette requête doit être utilisé deux fois (une fois pour déterminer le maximum,
une autre fois pour retrouver la classe correspondante).
En pratique, on préfère stocker les résultats des requêtes intermédiaires dans des tables temporaires au
moyen de la commande CREATE VIEW pour leur appliquer les requêtes suivantes, mais cela dépasse le
cadre de cet ouvrage.
Ey
11.3.3 Agrégation
Pour calculer A1 ,...,An γ f1 (B1 ),...,fm (Bm ) on écrit :
SELECT A1, ..., An, f1(B1), ..., fm(Bm) FROM R GROUP BY A1, ..., An;
On remarque qu’il est également possible de coupler ce calcul à une projection pour ajouter
t
d’autres attributs.
gh
Pour effectuer une sélection en amont il faut utiliser une condition WHERE avant l’instruc-
tion GROUP BY.
Pour réaliser une sélection en aval, il faut utiliser une syntaxe spécifique de condition en
utilisant l’instruction HAVING après l’instruction GROUP BY.
Ainsi l’expression :
i
pyr
Exercice 11.8 Traduire en langage SQL les requêtes écrites à l’exercice 11.4.
3 SELECT filiere, numero, avg(note) FROM releve WHERE numero = 2, GROUP BY filiere, numero;
Livre_silo 30 août 2013 16:32 Page 293
es
11 – Base de données relationnelle
293
l
rol
11.4 Exercices
Exercice 11.9 Pour chacun des schémas relationnels proposés dans l’exercice 10.7, peut-on espérer qu’il
existe une clé primaire ? Si oui, laquelle ?
Est-il possible d’adapter le schéma relationnel pour qu’il comporte une clé primaire qui ait du sens ?
Ey
Exercice 11.10 Pourquoi n’est-il en général pas judicieux de construire un schéma relationnel dans lequel
plusieurs attributs sont des clés primaires ?
Dans un tel cas, comment peut-on réorganiser la base de données à l’aide de la notion de clé étrangère ?
Exercice 11.11 * Un loueur de voitures et d’utilitaires souhaite informatiser la gestion de son entreprise.
La base de données devra notamment stocker des informations sur :
• les types de véhicules proposés : volume utile, coût d’entretien annuel, tarif de location...
• les véhicules effectivement possédés par l’entreprise : type, marque, immatriculation, est-il en cours de
location ou non...
t
• les clients : coordonnées, véhicule loué, dates de location...
Proposer un ensemble de schémas relationnels, de clés primaires et étrangères permettant de réaliser la
gh
gestion de cette entreprise.
Exercice 11.12 Opérations ensemblistes et clés. Soit R(S) et R′ (S) deux relations de même schéma,
et K ⊂ S une clé pour R et pour R′ .
Montrer que S est une clé pour R ∩ R′ . Déterminer un contre-exemple afin d’affirmer que K n’est pas
nécessairement une clé pour R ∪ R′ .
i
Exercice 11.13 Application d’opérations sur des relations. On considère ici les relations suivantes :
R1
pyr
id Nombre Premier
1 2 1
2 3 1
3 4 0
4 5 1
Co
5 8 0
6 11 1
7 15 0
8 19 1
R2
Diviseur Multiple
2 4
3 6
4 8
5 10
6 12
Livre_silo 30 août 2013 16:32 Page 294
es
Informatique pour tous
294
l
rol
1 Déterminer les résultats des opérations suivantes :
• σPremier=1 (R1 )
• R1 [Nombre = Diviseur] R2
• somme(R1 .Nombre)
• πPremier (R1 )
2 Écrire les opérations, éventuellement composées, permettant d’obtenir les résultats suivants :
Ey
• la somme des nombres premiers (ceux dont l’attribut Premier vaut 1) ;
• la somme des nombres présents dans R2 dont l’attribut Diviseur est premier ;
• la vérification qu’un nombre indiqué comme premier n’est pas une valeur de l’attribut Multiple de R2 .
3 Traduire dans le langage SQL chacune des opérations vues dans les questions précédentes.
1 François Ier 0
2 Henri II 1
3 François II 2
4 Charles IX 2
i
5 Henri III 2
pyr
avec id une clé primaire pour Valois et Parent un attribut définissant un lien de Valois vers elle-même.
On convient qu’une valeur Parent de 0 exprime le fait qu’elle n’a pas de parent dans la relation.
Cette relation représente la généalogie de quelques rois de France, ce que l’on représente usuellement
par l’arbre généalogique suivant :
Co
Dessins/bdd/valois.pdf
Livre_silo 30 août 2013 16:32 Page 295
es
11 – Base de données relationnelle
295
l
rol
Exercice 11.15 ** Arbres à parcours précalculé. On considère ici une relation similaire à celle de l’exer-
cice précédent. Elle correspond à l’arbre généalogique abstrait suivant :
Ey
Dessins/bdd/mptt1.pdf
t
Cependant, on va ajouter à chaque valeur deux entiers bas et haut correspondant au premier et au dernier
gh
moment où l’on rencontre une personne en effectuant le parcours suivant :
1 On commence par la racine comme personne courante.
2 Si la personne courante a des enfants, on désigne successivement chacun d’eux comme personne
courante et on continue le parcours à partir de celui-ci.
3 Sinon on s’arrête.
Un tel parcours est représenté sur le diagramme suivant :
i
pyr
Dessins/bdd/mptt2.pdf
Co
Sur celui-ci sont indiquées les deux valeurs bas (à gauche) et haut (à droite) :
Dessins/bdd/mptt3.pdf
Livre_silo 30 août 2013 16:32 Page 296
es
Informatique pour tous
296
l
rol
Cet arbre enrichi de ces deux nouvelles informations est représenté dans la relation suivante :
R
id Nom Parent bas haut
1 A 0 1 20
Ey
2 B 1 2 13
3 C 1 14 19
4 D 2 3 8
5 E 2 9 12
6 F 3 15 16
7 G 3 17 18
t
8 H 4 4 5
9 I 4 6 7
gh
10 J 5 10 11
1 Montrer que dans une telle représentation, il est possible d’obtenir les descendants d’une valeur avec
une seule sélection.
2 Même question pour les ancêtres.
3 Parmi les ancêtres obtenus, comment peut-on distinguer le père ? En déduire que l’attribut Parent est
i
redondant.
4 Indiquer comment on peut modifier la relation pour ajouter un fils à la valeur E tout en préservant les
pyr
Exercice 11.16 ** On démontre que la division cartésienne de deux relations R et R′ est bien définie. Si
Q est une relation, on appelle P la propriété « il existe une relation R′′ vérifiant (Q × R′ ) ∪ R′′ = R et
(Q × R′ ) ∩ R′′ = ∅ ».
Démontrer que :
1 Il existe une relation Q vérifiant la propriété P .
Co
Livre_silo 30 août 2013 16:32 Page 297
l es
rol
t Ey Cinquième partie
Algorithmique et
gh
programmation avancées
i
pyr
Cette partie couvre, avec la section sur les fonctions récursives du chapitre 5,
le programme de deuxième année. Nous y montrons qu’il existe d’autres
structures de données telles que la pile (chapitre 12) et nous y comparons
Co
Livre_silo 30 août 2013 16:32 Page 298
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 299
l es
rol
12
t Ey
Structure de pile
i gh
données. Pour traiter efficacement ces dernières, il faut les ranger de ma-
nière adéquate, de même que l’on met de l’ordre dans ses affaires pour
s’y retrouver plus facilement. L’objet informatique qui stocke des valeurs
en mémoire s’appelle une structure de données, qui est caractérisée par les
opérations qu’elle permet et le coût de ces opérations. On peut ainsi vou-
Co
Livre_silo 30 août 2013 16:32 Page 300
es
Informatique pour tous
300
l
rol
Dans ce chapitre, on présente la structure de pile. Elle correspond exactement à l’image
traditionnelle d’une pile de cartes ou d’assiettes posée sur une table. En particulier, on ne
peut accéder qu’au dernier élément ajouté, qu’on appelle le sommet de la pile. Ainsi, si on
a ajouté successivement A, puis B, puis C dans une pile, on se retrouve dans la situation
suivante :
Ey
↓↑
C
B
A
C est empilé sur B, lui-même empilé sur A. On peut soit retirer C de la pile (on dit qu’on
t
« dépile » C), soit ajouter un quatrième élément D (on dit qu’on « empile » D). Si on veut
gh
accéder à l’élément A, il faut commencer par dépiler C, puis B. L’image associée à une
pile est donc « dernier arrivé, premier sorti » (en anglais last in, first out, parfois abrégé en
LIFO).
On peut bien évidemment aussi imaginer une structure où, comme dans une file d’attente,
pyr
les éléments sortent dans leur ordre d’arrivée, le premier élément sorti correspondant à
l’élément le plus anciennement arrivé (en anglais first in, first out, parfois abrégé en FIFO).
On parle alors de structure de file. De mise en œuvre légèrement plus délicate que celle
de la structure de pile présentée dans ce chapitre, la structure de file ne figure pas au
programme.
Co
Les opérations depiler et empiler modifient le contenu de la pile passée en argument. Voici
une illustration de l’utilisation de ces trois opérations :
Livre_silo 30 août 2013 16:32 Page 301
es
12 – Structure de pile
301
l
rol
p = creer_pile(10)
A
empiler(p, A)
C
empiler(p, B)
Ey
B
empiler(p, C)
A
B
depiler(p)
A
t
D’autres opérations sont disponibles, mais ne modifient pas la pile :
• renvoie le nombre d’éléments contenus dans la pile p.
gh
taille(p)
• est_vide(p) indique si la pile p est vide.
• sommet(p) renvoie le sommet de la pile p, sans modifier p.
à résoudre
pyr
liser un tableau.
• Pour cela, il est préférable de connaître au moins un majorant du nombre d’éléments
à stocker.
Exercice 12.1 Quelle structure de données choisir pour chacune de ces tâches ?
1 Représenter un répertoire téléphonique.
2 Stocker l’historique des actions effectuées dans un logiciel et disposer d’une commande Annuler (ou
Undo).
3 Comptabiliser les pièces ramassées et dépensées par un personnage dans un jeu.
4 Ranger des dossiers à traiter sur un bureau.
1 Le nombre d’entrées du répertoire varie au cours du temps, mais on veut pouvoir accéder à n’importe
quel élément. On doit donc utiliser un tableau, quitte à réserver trop de place dans celui-ci.
2 La commande Annuler n’a besoin que de connaître la dernière action effectuée. Une fois celle-ci annu-
lée, on peut annuler l’avant-dernière, etc. Une pile est donc tout à fait appropriée.
Livre_silo 30 août 2013 16:32 Page 302
es
Informatique pour tous
302
l
rol
3 La seule chose qui compte est la valeur totale des pièces ; leur valeur individuelle ou l’ordre dans lequel
on les ramasse et dépense n’a pas d’importance. Il suffit ici d’un entier pour garder trace de la somme
dont on dispose.
4 Si on utilise une pile, on traitera toujours en premier le dernier dossier arrivé et on risque de faire attendre
longtemps les dossiers situés au bas de la pile. Pour bien faire, il faut ici tenir compte des priorités des
différents dossiers, ce qui demande de les ranger dans un tableau ordonné.
t Ey
12.2 Réalisation d’une structure de pile
12.2.1 Piles à capacité finie
La manière la plus simple de réaliser une pile consiste à utiliser un tableau de taille N ,
avec N suffisamment grand, c’est-à-dire au moins égal au nombre maximal d’éléments qui
gh
seront stockés dans la pile. Les éléments sont rangés dans l’ordre où ils ont été empilés.
Pour pouvoir empiler et dépiler, il faut connaître la position du sommet de la pile dans le
tableau. Pour cela, le plus simple est de stocker le nombre d’éléments n de la pile dans la
case 0 du tableau, puis les n éléments de la pile dans les cases 1 à n. On a donc la structure
suivante :
i
0 1 ... n N
pyr
0 1 2 3 ...
p = creer_pile(10) 0 ...
empiler(p, A) 1 A ...
Co
empiler(p, B) 2 A B ...
empiler(p, C) 3 A B C ...
depiler(p) 2 A B C ...
Les éléments colorés sont ceux qui sont réellement dans la pile. Les autres, par exemple
C à la dernière ligne, ne sont plus accessibles et seront écrasés lorsqu’on en empilera de
nouveaux.
Livre_silo 30 août 2013 16:32 Page 303
es
12 – Structure de pile
303
l
rol
On choisit d’initialiser les cases avec None, de manière arbitraire, ce qui n’a de toute façon
aucune importance puisque le contenu initial du tableau sera écrasé lors des appels à empiler.
Il ne reste plus qu’à stocker le nombre d’éléments (0 pour une pile vide) dans la case 0 de p
et à renvoyer le tableau.
p[0] = 0
Ey
return p
Dépiler un élément
Pour dépiler le sommet d’une pile p, on commence par récupérer son nombre d’éléments n
dans la première case du tableau.
def depiler(p):
t
n = p[0]
gh
On s’assure que n n’est pas nul, c’est-à-dire que la pile contient au moins un élément. Si ce
n’est pas le cas, on fait échouer le programme.
assert n > 0
On laisse donc au programmeur le soin de s’assurer que taille(p) est strictement positif
avant d’appeler depiler(p).
i
Le sommet de la pile se trouve dans p[n]. Avant de le renvoyer, on prend soin de décré-
pyr
Empiler un élément
Pour empiler un élément v dans une pile p, on commence par tester s’il y a de la place pour
Co
Ici, on fait délibérément échouer le programme avec assert si la pile est pleine. Décider
de ne rien faire serait une mauvaise idée : cela obligerait le programmeur à tester systé-
matiquement la taille de la pile avant d’appeler empiler, au risque d’oublier de le faire et de
chercher longtemps son erreur.
Si, en revanche, il y a de la place, alors on incrémente le nombre d’éléments et on stocke v
au (nouveau) sommet de la pile.
n = n + 1
p[0] = n
p[n] = v
Livre_silo 30 août 2013 16:32 Page 304
es
Informatique pour tous
304
l
rol
Le code complet est donné programme 11 ci-après. Il contient également les opérations
taille, est_vide et sommet.
Ey
Le premier élément du tableau contient le nombre d’éléments n de la pile. Les cases d’in-
dices 1 à n du tableau contiennent alors les éléments de la pile, le sommet de la pile se
trouvant à l’indice n.
def creer_pile(c):
p = (c + 1) * [None]
p[0] = 0
return p
t
def depiler(p):
n = p[0]
gh
assert n > 0
p[0] = n - 1
return p[n]
n = n + 1
p[0] = n
pyr
p[n] = v
def taille(p):
return p[0]
def est_vide(p):
return taille(p) == 0
Co
def sommet(p):
assert taille(p) > 0
return p[p[0]]
1. Il s’agit en fait de temps constant amorti ; voir plus loin l’encadré à ce propos.
Livre_silo 30 août 2013 16:32 Page 305
es
12 – Structure de pile
305
l
rol
Étant donné un tableau p de taille n, on peut lui ajouter un (n+1)-ième élément v à droite
avec p.append(v). Inversement, on peut récupérer le n-ième élément de p et le supprimer
avec p.pop(), le tableau p prenant alors la taille n − 1. De manière évidente, ces deux opéra-
tions correspondent exactement à empiler(p, v) et depiler(p). Le programme 12 ci-dessous
contient une réalisation de piles non bornées à l’aide de ces deux opérations.
Ey
PROGRAMME 12 Piles non bornées
Cette réalisation exploite les méthodes append et pop des tableaux de Python. On note que
l’argument c de creer_pile n’est pas utilisé (mais conservé afin de garder la même interface).
def creer_pile(c):
return []
t
def depiler(p):
gh
assert len(p) > 0
return p.pop()
def sommet(p):
i
pyr
def taille(p):
return len(p)
def est_vide(p):
return taille(p) == 0
Co
Livre_silo 30 août 2013 16:32 Page 306
es
Informatique pour tous
306
l
rol
Dit autrement, l’ensemble des n opérations d’incrémentation de la taille n’a qu’un coût
total proportionnel à n, comme si chaque opération avait eu un coût constant (même si,
en réalité, certaines sont plus coûteuses que d’autres). On parle de complexité constante
amortie.
12.3 Applications
Ey
On va maintenant présenter plusieurs programmes utilisant une pile. Ces programmes
fonctionnent indifféremment avec l’une ou l’autre des réalisations présentées ci-avant.
t
12.3.1 Analyse des mots bien parenthésés
gh
Comme première application des piles, on considère le problème suivant : étant donnée
une chaîne de caractères ne contenant que des caractères '(' et ')', déterminer s’il s’agit
d’un mot bien parenthésé. Un mot bien parenthésé est soit le mot vide, soit la concaténation
de deux mots bien parenthésés, soit un mot bien parenthésé mis entre parenthèses. Ainsi,
i
les trois mots '', '()()' et '(())()' sont bien parenthésés. À l’inverse, les mots '(()', '())'
pyr
ou encore ')(' ne le sont pas. On se propose de plus d’indiquer, pour chaque parenthèse
ouvrante, la position de la parenthèse fermante correspondante. Ainsi, pour le mot '(())()',
on donnera les couples d’indices (0, 3), (1, 2) et (4, 5).
L’idée consiste à parcourir le mot de la gauche vers la droite et à utiliser une pile pour
indiquer les indices de toutes les parenthèses ouvertes — et non encore fermées — vues
jusqu’à présent. On commence donc par créer une telle pile p :
Co
def parentheses(s):
p = creer_pile(len(s))
La capacité maximale de la pile est ici la longueur du mot len(s) puisque, dans le pire des
cas, on aura un mot composé uniquement de parenthèses ouvrantes (on rappelle qu’avec
les piles non bornées, la capacité passée n’est pas significative). On parcourt alors tous les
caractères du mot, de la gauche vers la droite, avec une boucle for :
for i in range(len(s)):
Livre_silo 30 août 2013 16:32 Page 307
es
12 – Structure de pile
307
l
rol
Sinon, c’est qu’il s’agit d’une parenthèse fermante ². Si la pile est vide, c’est que le mot
n’est pas bien parenthésé, car on vient de trouver une parenthèse fermante à laquelle ne
correspond aucune parenthèse ouvrante. On le signale en renvoyant immédiatement False :
else:
if est_vide(p):
Ey
return False
En effet, le mot pourrait contenir plus de parenthèses ouvrantes que de parenthèses fer-
i
mantes, comme '((', et il faut alors signaler que le mot n’est pas bien parenthésé.
pyr
if s[i] == '(':
empiler(p, i)
else:
if est_vide(p):
return False
j = depiler(p)
print((j, i))
return est_vide(p)
2. On a supposé ici que le mot ne contenait que des parenthèses. Le programme pourrait être plus défensif
et se prémunir contre l’éventuelle occurrence d’autres caractères.
Livre_silo 30 août 2013 16:32 Page 308
es
Informatique pour tous
308
l
rol
On vérifie son résultat sur un exemple :
In [1]: parentheses('()(()())')
(0, 1)
(3, 4)
(5, 6)
Ey
(2, 7)
Out[1]: True
Exercice 12.2 * Supposons que l’on ne souhaite pas afficher les indices des parenthèses se correspon-
dant, mais seulement renvoyer un booléen indiquant s’il s’agit d’un mot bien parenthésé. Simplifier le
programme précédent en conséquence. La structure de pile est-elle toujours nécessaire ?
Exercice 12.3 Adapter le programme 13 pour qu’il traite des mots constitués de plusieurs couples diffé-
rents de symboles ouvrants et fermants, par exemple '(' et ')', mais aussi '[' et ']' ou '{' et '}'.
t
Un mot est alors bien parenthésé si le symbole fermant qui correspond à chaque symbole ouvrant est du
même type : le mot '{()}[]' est bien parenthésé mais '[(])' ne l’est pas.
gh
Exercice 12.4 Adapter le programme 13 pour qu’il traite des mots constitués de parenthèses et d’autres
caractères, ces derniers n’interférant pas avec les parenthèses. Ainsi le mot '3+(4*(6-1)-2)' est bien
parenthésé, mais '(2+6)*3)' ne l’est pas.
Exercice 12.5 * Démontrer qu’un mot est bien parenthésé si et seulement s’il contient autant de pa-
renthèses ouvrantes que de parenthèses fermantes et chacun de ses préfixes contient au moins autant
de parenthèses ouvrantes que de parenthèses fermantes. On pourra procéder par récurrence forte sur la
i
taille du mot.
Interpréter cette caractérisation en termes de comportement de la pile au cours de l’exécution de la
pyr
fonction parentheses.
Exercice 12.6 Écrire une fonction qui prend un entier n en argument et renvoie le mot (n )n , c’est-à-dire
le mot constitué de n parenthèses ouvrantes suivies de n parenthèses fermantes.
Exercice 12.7 Écrire une version récursive de la fonction parentheses. Que se passe-t-il quand on l’exé-
cute sur le mot bien parenthésé imbriquant 1 000 paires de parenthèses (construit à l’aide de l’exercice
précédent) ? La fonction parentheses a-t-elle ce défaut ?
Co
L’évaluation d’une expression en NPI nécessite une pile. L’idée consiste à parcourir le ta-
bleau de la gauche vers la droite et à empiler chaque nombre rencontré. Lorsque l’élément
Livre_silo 30 août 2013 16:32 Page 309
es
12 – Structure de pile
309
l
rol
courant est un opérateur, on dépile les deux opérandes, on effectue le calcul et on empile
le résultat.
La solution que l’on propose ici fait l’hypothèse que les expressions arithmétiques
contiennent uniquement les opérateurs + et ∗. Il est très facile de l’étendre à d’autres
Ey
opérateurs.
On commence donc par créer une pile p :
def eval_npi(exp):
p = creer_pile(len(exp))
pyr
Quand on sort de la boucle for, il ne reste plus qu’à dépiler la valeur finale v de l’expression
et à vérifier que la pile p est bien vide :
v = depiler(p)
Co
assert est_vide(p)
return v
Livre_silo 30 août 2013 16:32 Page 310
es
Informatique pour tous
310
l
rol
On vérifie son résultat sur quelques exemples :
In [2]: eval_npi([1, 2, '+', 3, '*'])
Out[2]: 9
Ey
In [3]: eval_npi([1, 2, '*', 3, '+'])
Out[3]: 5
Out[4]: 6
pyr
Dessins/12-piles/maze50.png
Co
On peut considérer que l’entrée est en haut à gauche et la sortie en bas à droite, mais on
aura compris qu’on peut tout aussi bien choisir arbitrairement deux autres points sur le
bord.
On commence par se donner la dimension n du labyrinthe (en supposant qu’il soit carré,
de taille n×n) :
n = 50
Livre_silo 30 août 2013 16:32 Page 311
es
12 – Structure de pile
311
l
rol
Il faut également une matrice (n, n) de booléens indiquant, pour chaque case, si elle a déjà
été atteinte par un chemin (initialement False) :
atteinte = [[False] * n for i in range(n)]
Ey
ter le contenu de la matrice atteinte :
def visiter(c):
(x,y) = c
if x < 0 or x >= n or y < 0 or y >= n:
return
atteinte[x][y] = True
def est_atteinte(c):
t
(x,y) = c
if x < 0 or x >= n or y < 0 or y >= n:
gh
return True
return atteinte[x][y]
On écrit maintenant une fonction choix qui, étant donnée une position (x,y), détermine les
positions adjacentes non encore visitées. Le résultat est renvoyé sous la forme d’un tableau
pyr
ajouter((x+1, y))
ajouter((x, y-1))
ajouter((x, y+1))
return r
Le tableau résultat r est rempli par la fonction locale ajouter. Ainsi, on factorise l’appel
à est_atteinte. On note qu’on utilise ici la méthode append, exactement comme on l’a fait
pour écrire la fonction empiler.
L’étape suivante consiste en une fonction qui prend un élément au hasard dans un tableau.
Elle servira à choisir aléatoirement parmi les directions possibles renvoyées par la fonction
choix précédente :
def tirage(L):
n = len(L)
assert n > 0
return L[random.randint(0, n-1)]
Cette fonction suppose que le tableau n’est pas vide (d’où le assert) et utilise la fonction de
bibliothèque random.randint pour choisir un élément.
Livre_silo 30 août 2013 16:32 Page 312
es
Informatique pour tous
312
l
rol
Enfin, on écrit la fonction de construction du labyrinthe, labyrinthe. Elle utilise une pile
nommée pile contenant les emplacements à partir desquels on est susceptible de se dépla-
cer. Initialement, on y place la case (0, 0) et on la marque comme visitée :
def labyrinthe():
pile = creer_pile(n*n)
Ey
empiler(pile, (0,0))
visiter((0,0))
Tant que cette pile n’est pas vide, on en extrait le sommet, cellule :
while not est_vide(pile):
cellule = depiler(pile)
On examine alors les déplacements encore possibles à partir de cellule, donnés par la fonc-
t
tion choix. S’il en existe au moins un, on en choisit un au hasard avec tirage :
c = choix(cellule)
gh
if len(c) > 0:
suivante = tirage(c)
On relie alors les cases cellule et suivante, par exemple en effectuant un tracé dans une
fenêtre graphique. Puis on marque la case suivante comme étant atteinte, avec la fonction
visiter :
i
pyr
visiter(suivante)
Enfin, on remet cellule dans la pile, puis on ajoute suivante. Ainsi, le parcours reprendra à
partir de suivante dès l’itération suivante de la boucle :
empiler(pile, cellule)
empiler(pile, suivante)
Une très légère optimisation consisterait à ne pas remettre cellule dans la pile si elle n’avait
Co
maintenant plus de voisins c’est-à-dire si c ne contenait qu’un seul élément. Toutefois, c’est
inutilement compliqué : la prochaine fois que cellule sortira de la pile, on se contentera de
ne rien faire.
Le code complet est donné programme 15 ci-contre.
Exercice 12.10 * Compléter le programme 15 pour effectivement créer l’image du labyrinthe (voir an-
nexe B.2 pour la création d’une image dans un fichier).
Exercice 12.11 ** Compléter le programme 15 pour construire à la volée le chemin qui mène de l’entrée
(0, 0) à la sortie (49, 49). Puisqu’il existe un unique chemin entre toute paire de points, il suffit pour cela
de mémoriser quelle case a permis d’arriver à chaque endroit à partir de l’entrée, puis de remonter le long
de ces cases à partir de la sortie.
Livre_silo 30 août 2013 16:32 Page 313
es
12 – Structure de pile
313
l
rol
PROGRAMME 15 Construction d’un labyrinthe parfait
n = 50
atteinte = [[False] * n for i in range(n)]
Ey
def visiter(c):
(x,y) = c
if x < 0 or x >= n or y < 0 or y >= n:
return
atteinte[x][y] = True
def est_atteinte(c):
(x,y) = c
if x < 0 or x >= n or y < 0 or y >= n:
t
return True
return atteinte[x][y]
gh
def choix(c):
(x,y) = c
r = []
def ajouter(p):
if not est_atteinte(p): r.append(p)
i
ajouter((x-1, y))
ajouter((x+1, y))
pyr
ajouter((x, y-1))
ajouter((x, y+1))
return r
def tirage(L):
n = len(L)
assert n > 0
return L[random.randint(0, n-1)]
Co
def labyrinthe():
pile = creer_pile(n*n)
empiler(pile, (0,0))
visiter((0,0))
while not est_vide(pile):
cellule = depiler(pile)
print(cellule)
c = choix(cellule)
if len(c) > 0:
suivante = tirage(c)
# c'est ici qu'on relie les cases cellule et suivante
visiter(suivante)
empiler(pile, cellule)
empiler(pile, suivante)
Livre_silo 30 août 2013 16:32 Page 314
es
Informatique pour tous
314
l
rol
12.4 Exercices
Dans tous les exercices proposés ici, on veillera à n’utiliser que l’interface fournie par les
piles, et pas les opérateurs spécifiques aux tableaux.
Ey
Exercice 12.12 Écrire une fonction qui intervertit les deux éléments situés au sommet d’une pile de taille
au moins égale à 2.
Exercice 12.13 Écrire une fonction qui dépile et renvoie le troisième élément d’une pile de taille au moins
égale à 3. Les premier et deuxième éléments devront rester au sommet de la pile.
Exercice 12.14 Écrire une fonction qui lit le n-ième élément d’une pile. On s’assurera que la pile, en sortie,
contient toujours les mêmes éléments. (Indication : on pourra utiliser une deuxième pile.) On prévoira le
cas où la pile n’est pas de taille suffisante pour qu’un tel élément existe.
t
Exercice 12.15 Programmer les fonctions sommet et taille uniquement à l’aide de empiler, depiler et
est_vide, indépendamment de la réalisation de pile choisie.
gh
Que peut-on dire de la complexité en temps et en espace de cette fonction taille ?
Exercice 12.16 Écrire une fonction qui prend une pile non vide en argument et place l’élément situé à
son sommet tout au fond de la pile, en conservant l’ordre des autres éléments.
Quelle est sa complexité en temps et en espace ?
Exercice 12.17 Écrire une fonction similaire à reversed, qui prend une pile en argument et renvoie une
autre pile constituée des mêmes éléments placés dans l’ordre inverse. On s’autorise à vider la pile fournie
i
pyr
Exercice 12.18 * Tester dans différentes situations le comportement des boutons proposés par un navi-
gateur Internet : visiter une nouvelle page, revenir d’une page en arrière, aller une page en avant.
Écrire un ensemble de fonctions simulant ces boutons. On pourra pour cet exercice utiliser deux piles.
Exercice 12.19 Écrire une fonction couper qui prend une pile et la coupe en enlevant de son sommet un
certain nombre d’éléments (tiré au hasard) qui sont renvoyés dans une seconde pile. Exemple : si la pile
initiale est [1, 2, 3, 4, 5] et si le nombre d’éléments retirés vaut 2, alors la pile ne contient plus que
[1, 2, 3] et la pile renvoyée contient [5, 4].
Co
Exercice 12.20 * Mélange de cartes. Écrire une fonction melange qui prend en arguments deux piles
et qui mélange leurs éléments dans une troisième pile de la façon suivante : tant qu’une pile au moins
n’est pas vide, on retire aléatoirement un élément au sommet d’une des deux piles et on l’empile sur la
pile résultat. Exemple : un mélange possible des piles [1, 2, 3] et [5, 4] est [3, 2, 4, 1, 5]. Note : à
l’issue du mélange, les deux piles de départ sont donc vides.
Exercice 12.21 * Tour de magie de Gilbreath. Construire un paquet de cartes en empilant n fois
les mêmes k cartes (par exemple, pour un paquet de 32 cartes, on empile n = 16 paquets de paires
rouge/noir). Couper alors le paquet avec la fonction couper ci-dessus, puis mélanger les deux paquets
obtenus à l’aide de la fonction melange. On observe alors que le paquet final contient toujours n blocs
des mêmes k cartes (même si ces dernières peuvent apparaître dans un ordre différent au sein de chaque
bloc). Sur l’exemple des 16 paquets rouge/noir, on obtient toujours 16 paquets rouge/noir ou noir/rouge.
Exercice 12.22 ** Écrire une réalisation des tableaux redimensionnables en suivant l’idée décrite dans
l’encadré Pour aller plus loin page 305.
Livre_silo 30 août 2013 16:32 Page 315
l es
rol
13
t Ey
Algorithmes de tri
i gh
Nous avons montré que la recherche d’un élément dans un tableau était
pyr
Livre_silo 30 août 2013 16:32 Page 316
es
Informatique pour tous
316
l
rol
Dans tout ce chapitre, on suppose que les éléments à trier sont des entiers, mais les al-
gorithmes présentés sont valables pour n’importe quel type d’éléments, pourvu qu’il soit
muni d’un ordre total. On suppose qu’on trie des tableaux, dans l’ordre croissant. On note
N le nombre d’éléments à trier.
Ey
Pour chaque tri présenté, on indique sa complexité en nombre de comparaisons et d’affec-
tations effectuées, dans le meilleur et dans le pire des cas. La complexité en moyenne est
également donnée, à titre de comparaison, mais son calcul n’est pas détaillé (la complexité
en moyenne n’est pas au programme). Il est bon de savoir que la complexité optimale d’un
tri effectuant uniquement des comparaisons d’éléments est en O(N log N ). On en trou-
vera une démonstration à la fin du chapitre.
t
13.1 Tri par insertion
gh
Le tri par insertion est sans doute le plus naturel. Il consiste à insérer successivement
chaque élément dans l’ensemble des éléments déjà triés. C’est souvent ce que l’on fait quand
on trie un jeu de cartes ou un paquet de copies.
i
Le tri par insertion d’un tableau a s’effectue en place, c’est-à-dire qu’il ne demande pas
d’autre tableau que celui que l’on trie. Son coût en mémoire est donc constant si on ne
pyr
compte pas la place occupée par les données. Il consiste à insérer successivement chaque
élément a[i] dans la portion du tableau a[0:i] déjà triée. Illustrons cette idée sur un tableau
de cinq entiers contenant initialement [5,2,3,1,4]. Au départ, a[0:1] = 5 est déjà trié.
1 2 3 4 5
13.1.1 Réalisation
De manière générale, chaque étape du tri par insertion correspond à la situation suivante :
0 i-1
Livre_silo 30 août 2013 16:32 Page 317
es
13 – Algorithmes de tri
317
l
rol
On commence par une boucle for pour parcourir le tableau :
def tri_insertion(a):
for i, v in enumerate(a):
Pour insérer l’élément a[i] à la bonne place, on utilise alors une boucle while qui décale vers
Ey
la droite les éléments tant qu’ils sont supérieurs à a[i] :
j = i
while 0 < j and v < a[j-1]:
a[j] = a[j-1]
j = j-1
pyr
j = j-1
a[j] = v
13.1.2 Complexité
Co
comparaisons N N 2 /4 N 2 /2
affectations N N 2 /4 N 2 /2
Exercice 13.1 Dérouler à la main l’algorithme de tri par insertion sur le tableau [15,4,2,9,55,16,0,1].
Exercice 13.2 * Démontrer la correction du tri par insertion.
Exercice 13.3 Proposer un exemple de tableau sur lequel le tri par insertion a un coût linéaire (meilleur
cas). Proposer également un exemple de tableau sur lequel il a un coût quadratique (pire cas).
Livre_silo 30 août 2013 16:32 Page 318
es
Informatique pour tous
318
l
rol
13.2 Tri rapide
Le tri rapide consiste à appliquer la méthode diviser pour régner : on partage les éléments
à trier en deux sous-ensembles, les éléments du premier étant plus petits que les éléments
du second, puis on trie récursivement chaque sous-ensemble. En pratique, on réalise le
Ey
partage à l’aide d’un élément p arbitraire de l’ensemble à trier, appelé pivot. Les deux
sous-ensembles sont alors respectivement les éléments plus petits et plus grands que p.
Le tri rapide d’un tableau s’effectue en place. On va illustrer le tri rapide sur le tableau
[7,6,3,5,4,2,1].
On obtient finalement : 1 2 3 4 5 6 7
13.2.1 Réalisation
Pour réaliser ce tri, on écrit deux fonctions. Une fonction de partition organise les éléments
autour d’un pivot et renvoie la position de ce dernier. Une autre fonction trie récursivement
les deux portions du tableau à gauche et à droite du pivot. Les fonctions de partition et de
tri prennent en arguments le tableau et deux indices délimitant la portion à considérer.
Livre_silo 30 août 2013 16:32 Page 319
es
13 – Algorithmes de tri
319
l
rol
On commence par écrire une fonction echange pour échanger les éléments a[i] et a[j] d’un
tableau a :
def echange(a, i, j):
a[i], a[j] = a[j], a[i]
Ey
La fonction partition prend le tableau a et deux indices g et d en arguments, avec la conven-
tion que g est inclus et d exclu. On suppose qu’il y a au moins un élément dans ce segment,
ce que l’on vérifie avec assert :
def partition(a, g, d):
assert g < d
g m i d
v <v ⩾v ?
i
m = g
for i in range(g+1, d):
Si a[i] est supérieur ou égal à v, il n’y a rien à faire. Dans le cas contraire, pour conserver
l’invariant de boucle, il suffit d’incrémenter m et d’échanger a[i] et a[m] :
if a[i] < v:
m = m+1
Co
echange(a, i, m)
On écrit ensuite la partie récursive du tri rapide sous la forme d’une fonction tri_rapide_rec
qui prend les mêmes arguments que la fonction partition. Si g ⩾ d − 1, il y a au plus un
élément à trier et il n’y a donc rien à faire, ce qui assure au passage que l’on n’appelle pas
partition avec g ⩾ d :
def tri_rapide_rec(a, g, d):
if g >= d-1: return
Livre_silo 30 août 2013 16:32 Page 320
es
Informatique pour tous
320
l
rol
Après cet appel, le pivot a[m] se retrouve à sa place définitive. On effectue alors deux appels
récursifs pour trier a[g..m[ et a[m+1..d[ :
tri_rapide_rec(a, g, m)
tri_rapide_rec(a, m+1, d)
Ey
Pour trier un tableau, il suffit d’appeler tri_rapide_rec sur la totalité de ses éléments :
def tri_rapide(a):
tri_rapide_rec(a, 0, len(a))
Tel qu’il est écrit, ce code présente deux inconvénients. D’une part, il atteint très vite le
nombre maximal d’appels récursifs (1 000 par défaut en Python). D’autre part, il peut
exhiber une complexité quadratique, notamment dans le cas d’un tableau déjà trié. On va
remédier à ces deux problèmes.
t
Pour rester sous la limite des 1 000 appels récursifs de Python, on va tout d’abord supprimer
gh
l’un des deux appels récursifs au profit d’une boucle.
def tri_rapide_rec(a, g, d):
while g < d-1:
m = partition(a, g, d)
tri_rapide_rec(a, g, m)
g = m+1
i
Ici, le second appel a été remplacé par l’affectation g = m+1 et le tout a été placé dans une
pyr
boucle while, pour que le calcul soit répété tant que le segment contient au moins deux
éléments. Cela ne suffit pas pour autant, car l’appel récursif restant peut être répété un
grand nombre de fois, par exemple si le pivot se trouve plus souvent dans la moitié droite
que gauche. D’où l’idée d’utiliser l’appel récursif pour le plus petit segment et la boucle
pour le plus grand :
if m-g < d-m-1:
Co
tri_rapide_rec(a, g, m)
g = m+1
else:
tri_rapide_rec(a, m+1, d)
d = m
Pour ce qui est de la complexité quadratique dans le cas d’un tableau déjà trié (ou trié en
ordre inverse par exemple), une solution simple consiste à ne pas choisir systématiquement
le premier élément du segment comme pivot, mais plutôt un élément au hasard. Une façon
très simple de réaliser cette idée consiste à démarrer la fonction partition par un échange
aléatoire :
def partition(a, g, d):
echange(a, g, random.randint(g, d-1))
...
Le reste du code est alors inchangé. Le code complet est donné ci-après.
Livre_silo 30 août 2013 16:32 Page 321
es
13 – Algorithmes de tri
321
l
rol
PROGRAMME 17 Tri rapide
def echange(a, i, j):
a[i], a[j] = a[j], a[i]
Ey
def partition(a, g, d):
assert g < d
echange(a, g, random.randint(g, d-1))
v = a[g]
m = g
for i in range(g+1, d):
if a[i] < v:
m = m+1
echange(a, i, m)
t
if m != g:
echange(a, g, m)
gh
return m
g = m+1
else:
pyr
tri_rapide_rec(a, m+1, d)
d = m
def tri_rapide(a):
tri_rapide_rec(a, 0, len(a))
Co
13.2.2 Complexité
La fonction partition fait toujours exactement d − g − 1 comparaisons. Si la fonction
partition détermine un segment de longueur K et un autre de longueur N −1−K, la fonc-
tion tri_rapide_rec va donc effectuer N − 1 comparaisons par l’intermédiaire de partition,
puis d’autres comparaisons par l’intermédiaire des deux appels récursifs à tri_rapide_rec (la
fonction réécrite avec un seul appel récursif a la même complexité en temps). Le pire des
cas correspond à K = 0, ce qui donne, en notant C(N ) la complexité du tri d’un tableau
de longueur N , l’équation de récurrence suivante :
C(N ) = N − 1 + 2C(N/2).
Livre_silo 30 août 2013 16:32 Page 322
es
Informatique pour tous
322
l
rol
On en déduit facilement C(N ) ∼ N log N .
En ce qui concerne le nombre d’affectations, on note que la fonction partition effectue
un appel à echange initial, autant d’appels à echange que d’incrémentations de m, et éven-
tuellement un dernier appel lorsque m != g. Le meilleur des cas est atteint lorsque le pivot
Ey
est toujours à sa place. Il y a alors un seul appel à echange, soit deux affectations. Il est
important de noter que ce cas ne correspond pas à la meilleure complexité en termes de
comparaisons (qui est alors quadratique). Dans le pire des cas, le pivot se retrouve toujours
à la position r-1. La fonction partition effectue alors 2(d − g) affectations, d’où un total de
N 2 affectations.
meilleur cas moyenne pire cas
t
comparaisons N log N 2N log N N 2 /2
gh
affectations 2N 2N log N N2
Exercice 13.4 Dérouler à la main l’algorithme de tri rapide sur le tableau [15,4,2,8,17,23,0,1].
Exercice 13.5 * Proposer un exemple de tableau sur lequel le tri rapide a un coût en O(N log N ) (meilleur
cas). Proposer également un exemple de tableau sur lequel il a un coût quadratique (pire cas).
i
pyr
Livre_silo 30 août 2013 16:32 Page 323
es
13 – Algorithmes de tri
323
l
rol
• Pour trier a[4:6], on trie a[4:5] et a[5:6]. 4 2
Ey
• On fusionne a[6:7] et a[7:8]. 1 8
réaliser en place. Le plus simple est d’utiliser un second tableau, alloué une et une seule
fois au début du tri.
pyr
On commence par écrire la fonction fusion. Elle prend en arguments deux tableaux, a1
et a2, et les trois indices g, m et d. Les portions a1[g..m[ et a1[m..d[ sont supposées triées.
L’objectif est de les fusionner dans a2[g..d[. Pour cela, on va parcourir les deux portions
de a1 avec deux variables i et j et la portion de a2 à remplir avec une boucle for :
def fusion(a1, a2, g, m, d):
i, j = g, m
Co
g m d
a1 trié trié
↑i ↑j
a2 trié
↑k
Il faut alors déterminer la prochaine valeur à placer en a2[k]. Il s’agit de la plus petite des
deux valeurs a1[i] et a1[j]. Il convient cependant de traiter correctement le cas où il n’y a
plus d’élément dans l’une des deux moitiés. On détermine si l’élément doit être pris dans
la moitié gauche avec le test suivant :
if i < m and (j == d or a1[i] <= a1[j]):
Livre_silo 30 août 2013 16:32 Page 324
es
Informatique pour tous
324
l
rol
Dans les deux cas, on copie l’élément dans a2[k] et on incrémente l’indice correspondant.
a2[k] = a1[i]
i = i+1
else:
a2[k] = a1[j]
Ey
j = j+1
La partie récursive du tri fusion est matérialisée par une fonction récursive locale
tri_fusion_rec qui prend en arguments les indices g et d délimitant la portion à trier :
t
def tri_fusion_rec(g, d):
gh
Si le segment contient au plus un élément, c’est-à-dire si g ⩾ d − 1, il n’y a rien à faire :
if g >= d-1: return
Sinon, on partage l’intervalle en deux moitiés égales : on calcule l’élément médian m, puis
on trie récursivement a[g..m[ et a[m..d[ :
i
m = (g+d)//2
tri_fusion_rec(g, m)
pyr
tri_fusion_rec(m, d)
Il reste à effectuer la fusion. Pour cela, on copie toute la portion a[g..d[ dans le tableau tmp,
puis on appelle la fonction fusion, qui fusionne le tout dans a :
tmp[g:d] = a[g:d]
fusion(tmp, a, g, m, d)
Enfin, on trie le tableau a tout entier en appelant tri_fusion_rec sur la totalité de ses élé-
Co
ments :
tri_fusion_rec(0, len(a))
Livre_silo 30 août 2013 16:32 Page 325
es
13 – Algorithmes de tri
325
l
rol
def tri_fusion(a):
tmp = a[:]
def tri_fusion_rec(g, d):
if g >= d-1: return
m = (g+d)//2
Ey
tri_fusion_rec(g, m)
tri_fusion_rec(m, d)
tmp[g:d] = a[g:d]
fusion(tmp, a, g, m, d)
tri_fusion_rec(0, len(a))
t
13.3.2 Complexité
gh
Si on note C(N ) (resp. f (N )) le nombre total de comparaisons effectuées par tri_fusion
(resp. fusion) pour trier un tableau de longueur N , on a l’équation de récurrence suivante :
C(N ) = 2C(N/2) + f (N )
i
En effet, les deux appels récursifs se font sur deux segments de même longueur N/2. Dans
le meilleur des cas, la fonction fusion n’examine que les éléments de l’un des deux segments
pyr
car ils sont tous plus petits que ceux de l’autre segment. Dans ce cas, f (N ) = N/2 et donc
C(N ) ∼ 12 N log N . Dans le pire des cas, tous les éléments sont examinés par fusion et
donc f (N ) = N − 1, d’où C(N ) ∼ N log N .
Le nombre d’affectations est le même dans tous les cas : N affectations dans la fonction
fusion(chaque élément est copié de a1 vers a2) et N affectations effectuées par la copie
de a vers tmp. Si on note A(N ) le nombre total d’affectations pour trier un tableau de
Co
longueur N , on a donc :
A(N ) = 2A(N/2) + 2N,
d’où un total de 2N log N affectations.
On note que, dans tous les cas, la complexité du tri fusion est la même. Cette complexité
est optimale.
Livre_silo 30 août 2013 16:32 Page 326
es
Informatique pour tous
326
l
rol
SAVOIR-FAIRE Distinguer par leurs complexités deux algorithmes
résolvant un même problème
Il convient tout d’abord de s’assurer que les algorithmes résolvent bien le même pro-
Ey
blème : il n’est pas rare que leurs conditions d’utilisation soient différentes, ce qui rend
moins pertinente une comparaison de complexité.
• On s’intéressera d’abord à la complexité en temps dans le pire des cas, qui est
souvent la plus représentative.
• On discutera cependant si ce pire cas a des chances de se présenter dans des situa-
tions réelles.
• On n’oubliera pas d’étudier la complexité en espace, qui peut départager des algo-
t
rithmes de performances par ailleurs similaires.
gh
Exercice 13.6 Quelles sont les différences entre les algorithmes de tri rapide et de tri fusion du point de
vue de la complexité en temps et en espace ?
Quelles conséquences cela a-t-il pour leur utilisation ?
On rappelle tout d’abord que ces deux tris fonctionnent sur les mêmes entrées sans conditions particu-
i
lières.
• Le tri rapide a une complexité en temps quadratique dans le pire cas. Si on veut être certain que ce cas
pyr
ne se présente pas, on choisira plutôt le tri fusion, qui est au pire en O(n log n).
• Ces deux tris ont une complexité moyenne en O(n log n), ce qui signifie qu’en général ils auront des
performances comparables, en particulier si la répartition des données dans le tableau à trier n’est pas
trop particulière.
• Le tri fusion a une complexité en espace légèrement supérieure à celle du tri rapide puisque la fusion ne
s’opère pas en place. Dans un cas où la mémoire est une ressource critique, on évitera donc de choisir
le tri fusion.
Co
Exercice 13.7 Dérouler à la main l’algorithme de tri fusion sur le tableau [5,40,2,18,17,3,0,1,14].
Livre_silo 30 août 2013 16:32 Page 327
es
13 – Algorithmes de tri
327
l
rol
13.4 Exercices
Exercice 13.8 ** Le tri par sélection consiste, comme le tri par insertion, à maintenir à chaque itération i
une portion du tableau a[0:i] déjà triée. En revanche, au lieu de chercher à insérer a[i] dans cette portion,
on recherche le minimum des éléments de a[i:n] et on échange ce minimum avec a[i]. La portion du
tableau a[0:i+1] est alors triée.
Ey
1 Écrire une fonction qui réalise le tri par sélection.
2 Démontrer la correction de ce tri.
3 Évaluer sa complexité en temps, en espace, dans le meilleur et dans le pire des cas.
Exercice 13.9 * Montrer que, dans le cas du tri rapide du programme 17 page 321, le nombre maximal
d’appels imbriqués à la fonction tri_rapide_rec ne peut excéder log N .
Exercice 13.10 ** Étant donné un entier k, on dit qu’un tableau est k-presque trié :
• si chacun de ses éléments est à au plus k indices de la position où il devrait être ;
t
• ou bien si au plus k de ses éléments ne sont pas à leur place.
Démontrer qu’à k fixé, le tri par insertion a une complexité en O(n) sur les tableaux k-presque triés.
gh
Exercice 13.11 * Une idée classique pour accélérer un algorithme de tri consiste à effectuer un tri par
insertion quand le nombre d’éléments à trier est petit, c’est-à-dire devient inférieur à une constante fixée à
l’avance (par exemple 5). Modifier le tri rapide de tableaux pour prendre en compte cette idée. On pourra
reprendre la fonction tri_insertion et la généraliser en lui passant deux indices g et d pour délimiter la
portion du tableau à trier.
Exercice 13.12 ** Calcul rapide de la médiane. L’algorithme que l’on écrit ici permet de déterminer la
i
médiane, et même plus généralement le k-ième élément d’un tableau, sans le trier intégralement. Il est
1 Écrire une fonction qui prend en argument un tableau de cinq éléments et calcule sa médiane.
2 Écrire une fonction qui prend en argument un tableau quelconque, le divise en groupes de cinq éléments
et construit le tableau des médianes de chaque groupe de cinq.
3 Modifier la fonction précédente pour qu’elle s’appelle récursivement sur le « tableau des médianes »
construit.
4 Enfin, écrire une fonction qui effectue une partition du tableau de départ avec pour pivot la « médiane
des médianes » calculée précédemment.
Pour trouver le k-ième élément du tableau, où doit-on le chercher en fonction des tailles des deux
Co
Exercice 13.15 * Comme pour le tri rapide, on peut terminer le tri fusion par un tri par insertion lorsque
le nombre d’éléments à trier devient petit.
Livre_silo 30 août 2013 16:32 Page 328
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 329
l es
rol
t Ey Annexe
Travaux pratiques
gh
et compléments
i
pyr
Cette annexe doit amener l’étudiant à goûter les implications concrètes des
différentes parties du cours, d’abord à travers des travaux pratiques — lu-
diques pour certains, allant de la dissection d’un ordinateur pour en mani-
Co
Livre_silo 30 août 2013 16:32 Page 330
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 331
l es
rol
A
t Ey
Travaux pratiques
i gh
pyr
en standard un compilateur d’un langage appelé C, ce qui n’est pas le cas des systèmes
Microsoft. L’installation d’un tel outil sous MS-Windows sortant du cadre de cet ouvrage,
on se contentera de décrire ce qu’il convient de faire sous un système Unix ¹. L’étude du
langage C étant hors du cadre de cet ouvrage, on se contentera d’un programme très simple.
Il convient tout d’abord de créer, en utilisant la fenêtre d’édition de Spyder, un fichier
hello.c contenant le texte du programme :
#include <stdio.h>
int main() {
printf("Hello\n") ;
}
1. Il peut arriver que le compilateur ne soit pas installé par défaut sous votre système. Cependant sous
tout système GNU/Linux, il suffira d’installer le paquet des outils de développement pour que le compila-
teur C soit installé. Sous toute distribution dérivée de la Debian, en particulier sous Ubuntu, installer le paquet
build-essential est suffisant.
Livre_silo 30 août 2013 16:32 Page 332
es
Informatique pour tous
332
l
rol
Enregistrer ce fichier. On supposera dans la suite qu’il est enregistré dans le répertoire
/home/jdupont/TP. Il conviendra de remplacer ce nom par le nom du répertoire où ce
fichier a été enregistré.
Lancer alors un émulateur de terminal et taper la commande suivante, suivie de la touche
Ey
Entrée :
cc -o /home/jdupont/TP/hello /home/jdupont/TP/hello.c
pyr
Devrait alors s’afficher quelque chose dont le début ressemble à ceci (si votre système est
un PC dont le système est en 64 bits) :
/home/jdupont/TP/hello : file format elf64-x86-64
00000000004003b8 <_init> :
Co
Livre_silo 30 août 2013 16:32 Page 333
es
A – Travaux pratiques
333
l
rol
A.1.2 Exécution autonome d’un programme Python
On explique à présent comment un programme créé avec Spyder peut être exécuté de façon
autonome, ou plus précisément sans Spyder.
Créer à l’aide de Spyder un fichier hellotk.py avec le contenu suivant :
Ey
#! /usr/bin/python3
taper la commande
pyr
python /home/jdupont/TP/hellotk.py
Il existe cependant une deuxième méthode plus simple, à condition d’effectuer une petite
manipulation préalable :
Co
2. Sous Unix, Python est en général installé dans /usr/bin/python, mais il peut être nécessaire d’adapter
cette ligne suivant votre installation. Si /usr/bin/python ne fonctionne pas, essayer avec /usr/bin/env python
qui demande au système d’exécuter la commande env, laquelle se chargera de chercher elle-même la commande
python et devrait la trouver si votre système est configuré raisonnablement.
Livre_silo 30 août 2013 16:32 Page 334
es
Informatique pour tous
334
l
rol
Avec cette modification, ce script peut désormais être exécuté par simple (ou double) clic
dans l’explorateur de fichiers du système. On peut aussi l’envoyer à une autre personne, qui
pourra l’exécuter sans avoir à regarder son contenu, ni même à installer l’environnement
de développement Spyder, dès lors que Python est installé sur sa machine. C’est le cas
par défaut sur tous les systèmes GNU/Linux ainsi que sous Mac OS X, mais pas sur les
Ey
systèmes MS-Windows. Pour ces derniers, l’extension de Python py2exe permet, à partir
d’un programme, de produire un exécutable qui tourne même sans installation de Python.
virtuelle. C’est un mécanisme par lequel le système fait croire aux applications qu’elles
disposent d’une plus grande mémoire vive qu’il n’en existe physiquement.
pyr
Pour avoir une idée de ce que fait alors le système, on imagine qu’un secrétaire se met à
son bureau pour étudier des dossiers. Son plan de travail est encombré d’une montagne
de papiers (d’autres dossiers en cours), mais heureusement il dispose d’un elfe de maison,
à qui il a donné l’ordre de libérer une place suffisante sur son bureau pour travailler. C’est
pourquoi, dès que le secrétaire a besoin d’un peu de place, l’elfe prend une partie des dossiers
encombrant le bureau et va les ranger dans une armoire pour libérer de la place. Dès que le
Co
secrétaire s’apprête à travailler de nouveau sur un dossier que l’elfe a rangé, celui-ci lit dans
ses pensées et remet le dossier sur le bureau, après avoir au besoin rangé un autre dossier
dans l’armoire pour faire de la place si nécessaire.
Si de plus, l’elfe hypnotise le secrétaire, d’une part pour le faire patienter le temps qu’il
effectue ses manipulations et d’autre part pour qu’il croie être à chaque fois en train de
travailler sur une portion de bureau différente, il ne s’apercevra de rien.
C’est exactement ce qui se passe dans un ordinateur, avec le système d’exploitation dans
le rôle de l’elfe, les applications dans celui du secrétaire, la mémoire vive dans le rôle du
bureau et le disque dur dans celui de l’armoire : lorsqu’une application réclame plus de
mémoire qu’il n’en existe physiquement, le système d’exploitation enregistre une portion
de mémoire vive sur une partie du disque dur réservée à cet effet et la donne à l’application
pour qu’elle puisse travailler.
Livre_silo 30 août 2013 16:32 Page 335
es
A – Travaux pratiques
335
l
rol
Si la portion de mémoire enregistrée est de nouveau requise, le système d’exploitation la
recharge en mémoire vive, après avoir au besoin enregistré une autre portion de mémoire
sur le disque pour faire de la place (il y a donc un ballet d’échanges de données entre disque
dur et mémoire vive ; la partie du disque dur est pour cela appelée espace ou fichier d’échange,
ou swap en anglais). Du point de vue des applications, comme de celui de l’utilisateur, tout
Ey
se passe comme s’il y avait davantage de mémoire vive qu’il n’y en a matériellement...
Ou presque. Il y a en effet une différence notable : le temps d’accès à des données sur
disque est beaucoup plus important que le temps d’accès à des données en mémoire vive
(d’un facteur en général supérieur à 103 ). Lorsque la quantité de mémoire virtuelle utilisée
est faible, les données ou applications transférées sur le disque sont celles dont l’utilisa-
teur se sert le moins à ce moment, mais si elle devient plus importante, des données plus
t
fréquemment utilisées sont transférées sur disque et doivent y être fréquemment relues.
Pendant ces relectures, l’application concernée est contrainte d’attendre. Les performances
gh
globales de l’ordinateur s’en ressentent fatalement.
EN PRATIQUE Ça rame...
Tout le monde a déjà eu affaire à un ordinateur qui semblait fonctionner au ralenti, où la
moindre ouverture ou minimisation d’une fenêtre demandait un temps considérable. La
i
cause en est souvent la même : un manque de RAM par rapport à ce que demandent le
système d’exploitation et les applications ³. Le système d’exploitation doit alors sans cesse
pyr
échanger des données entre mémoire vive et disque dur pour répondre aux sollicitations
des applications.
Pour résoudre le problème, il n’y a que deux possibilités :
• Diminuer l’usage de la mémoire vive. Pour cela, il convient de fermer les applications
inutilisées, d’utiliser des applications moins gourmandes, voire de changer de système
d’exploitation (certaines distributions GNU/Linux comme Toutou Linux se contentent de
128 Mo de mémoire vive, là où d’autres systèmes réclament au minimum 1 Go).
• Lorsque c’est possible, augmenter la mémoire vive en ouvrant l’ordinateur pour ajou-
Co
ter des barrettes de mémoire ou remplacer les barrettes en place par d’autres de plus
grande capacité. En pratique, c’est difficile à faire au bout de quelques années, car d’une
part les formats de barrettes de mémoire vive changent souvent et d’autre part, il y a
des limitations à la quantité de mémoire vive que la carte mère d’un PC peut accepter.
Il convient donc d’être attentif au moment de l’achat d’un PC : les étiquettes vantent
surtout la fréquence du processeur, il vaut mieux s’intéresser d’abord à la quantité de
mémoire vive.
3. Cela arrive typiquement avec un ordinateur ancien sur lequel on veut mettre un système d’exploitation ou
des applications récentes.
Livre_silo 30 août 2013 16:32 Page 336
es
Informatique pour tous
336
l
rol
Ce TP consiste à lancer un programme créant n mots de données dans la mémoire (vive
ou virtuelle), puis accédant au hasard à N endroits dans les données créées. Si les données
sont toutes dans la mémoire vive, l’accès est rapide. Si elles sont en quantité tellement
importante qu’une partie a dû être stockée sur le disque dur, l’accès est plus lent.
Ey
1 Télécharger le programme mem_virtuelle.py sur le site de cet ouvrage ⁴ et l’ouvrir dans
un IDE Python. L’entier N y est fixé à 1000 et n peut être arbitrairement choisi.
2 Faire exécuter le code par l’IDE ; cela ne produit en principe aucun résultat. Ensuite,
dans le même interpréteur interactif, lancer la fonction teste avec pour argument l’en-
tier n.
3 Si on lance par exemple teste(100000000), combien de mots mémoire seront-ils créés ?
Combien de Mo cela représente-t-il sur des systèmes respectivement 32 et 64 bits ?
t
4 La fonction teste renvoie alors le temps, en secondes, nécessaire pour accéder à N de
ces mots mémoire au hasard parmi les n. Elle effectue en fait plusieurs fois le test avant
gh
de rendre la valeur la plus faible (qui est la plus représentative de ce qui se passe dans le
meilleur des cas).
Tester différentes valeurs de n et essayer de déterminer comment évolue la valeur ren-
voyée par le programme en fonction de n. Pour n trop grand, le programme s’arrêtera
brutalement car il n’y aura tout simplement pas assez de mémoire totale (même avec la
i
5 Établir un lien entre les résultats observés et les quantités de mémoire vive et de mémoire
pyr
accès).
On donne figure A.2 des temps relevés par ce programme pour un PC sous une distribution
GNU/Linux 32 bits avec 2 Go de mémoire physique plus 2 Go de mémoire virtuelle. Les
valeurs de n ont été choisies aléatoirement de façon uniforme entre 400×106 et 700×106
mots-mémoire. D’autres programmes étaient ouverts sur ce PC, mais globalement, seul le
programme de test était réellement actif. Résultat : les données et programmes non actifs
ont été mis dans l’espace d’échange et les temps d’accès ne sont dégradés sensiblement
que pour une quantité de données supérieure à 500 millions de mots, soit environ 2 Go
(précisément la taille de la mémoire vive).
4. http://informatique-en-prepas.fr
Livre_silo 30 août 2013 16:32 Page 337
es
A – Travaux pratiques
337
l
rol
t
3- .
.
.
Ey
.
.
. .
. .
.
.
.
.
..
. . . .
. . .
.
. .
.
.
. .
. .
. . .
. . .
. . . .
. . .
.
. . .
. .
. . .
. . . . .
.
.
. . .
. . .
.
. .
. .
. .
. .
. .
. . .
. . .
. . . .
. . . ..
. . . .
.
. .. .
. . .
. .
2-
. . .
. . . . .
.
. .. .
.
. . . . . .
. .. . .
. . . .
. .
. .
. .
.. .
. .
. . .
. . . . . .
.
. .
. . . .
. . . .
.
. . . .
. . . . .
. . . .
. . .
. . .
. .
. . . . . .
. . .
. . .
. .
. . . ..
. . .
.
. .
. ..
..
. ..
. . .
. . .
.
. . .
. . .
. . . .
. .
. . .
. . .
. .
.
. .
..
. .
. .
. ..
. .
.
. .. .
.. .
. .
. . .
. . .
. . .
. .
. ..
.
.
. .
. ..
. .
. .
. . . . . .
. .
. . . . .. . . .
.. .
.
.
1-
. . .
.
. . . . . .
. .
.
.
. .
. . . .
. . . ..
.
. .
.
. .
.
. . .
. .
. . .
. ..
. .
. .
..
.
. . . .
. .
. .
. . . .
.
. ..
. . . .
.
.
.
. .
. .
. .
.
. .
. . . .
.
. . .
. ..
t . .
.
.
. .
.
..
..
..
.
.
.
.
.
.
.
.
. .
.
.
. . .
. .
. ..
. .
.
.
. . .
. .
.
.
. .
. .. .
. . .
. .. .
.
.
. .
. . . .
.
. ..
.
.
. . .
0 -.
. .
gh
. ..
. .. .
.
. .
.
.. .
. .
. .
.
-
-
. . .
. .
. .... .. . . .. . ... . . .. . .. . . . . . . .. ...... . .. .. .. . . . . .. . . .... . .... .. ...... .. .... . ... . . .. . . . ... .. .. . .. .. . . ... ...... .. .. . . ... ... . .. . . . .. . . . .. .. ... .. . .... . ... .... . . ... . . .. .. . . . .... . .. .. .. ... . .. . .. . ... ... . .... ... . . . .. . . . .. ... . . . .... .... . ... .. .. .. ... ..... . . .. . . . . . . . . . . . .. .. . .. ... ... .. . . . .. . .. . . .. .. . .. .. . . .... . . . . . . . . . ... ...... . ... . . . .. .. . ... .. ... . .. .... .... .. . . .... ... . .... .. . . .. . .. . . ... . . . .... . . ... . .. . . . . . . . .. ... . . .... ..... . . . . . . .... .. .. . ... ... . .. .... .. . ... . ... . ... ... . ... . .... . .. . ... .... . . .. .. .... . . . .
n
100 200 300 400 500 600 700
Figure A.2
Temps relevés par mem-virtuelle.py
pyr
bureau et de voir comment changer un disque dur, une carte d’extension ou le processeur.
A.3.1 Sécurité
Livre_silo 30 août 2013 16:32 Page 338
es
Informatique pour tous
338
l
rol
Votre sécurité
Pour votre sécurité, évitez de vous exposer à toute source d’énergie trop importante. Il y
en a essentiellement deux :
• L’alimentation électrique de votre PC. Débranchez avant toute chose le(s) câble(s)
Ey
d’alimentation du PC et de l’écran.
• Les condensateurs des différents équipements du PC et de l’écran. Ne cherchez pas à
démonter l’écran (particulièrement les écrans cathodiques), ni le bloc transformateur
du PC.
Par ailleurs, le démontage d’un PC est déconseillé si vous êtes allergique à la poussière.
La sécurité de l’ordinateur
t
Pour la sécurité de l’ordinateur, il convient de se rappeler les points suivants :
gh
• Les composants sont pour la plupart fragiles. Ils doivent être manipulés avec précaution,
sans jamais forcer. Quand ça coince, il faut comprendre pourquoi. Il est évidemment
préférable de se faire la main sur un PC en panne.
• Les composants sont pour la plupart sensibles à l’électricité statique. Lorsqu’on porte
certains vêtements propices à l’accumulation d’électricité statique, on peut ressentir une
i
étincelle électrique lorsqu’on touche le sol, un radiateur ou même simplement une autre
pyr
personne. Ce genre d’étincelle est sans danger pour les humains, mais est fatale à de
nombreux composants électroniques. Il convient donc, dans la mesure du possible, d’une
part de diminuer les risques d’accumulation (pas de vêtements propices, pas de mo-
quette) et d’autre part, de se décharger de son électricité statique avant de manipuler
le PC (toucher n’importe quelle partie métallique, non recouverte de peinture de la car-
casse du PC avant chaque manipulation). Les composants enlevés de la machine seront
déposés sur une table propre.
Co
Livre_silo 30 août 2013 16:32 Page 339
es
A – Travaux pratiques
339
l
rol
t Ey
face-carte-mere.jpg
gh
Figure A.3
i
pyr
Co
dos-carte-mere.jpg
Figure A.4
Carte mère, vue de dessous
Livre_silo 30 août 2013 16:32 Page 340
es
Informatique pour tous
340
l
rol
Sur cette carte mère, on distingue notamment le processeur (figure A.5) et la mémoire
(ROM) sur laquelle se trouve le BIOS, programme régissant le comportement basique de
l’ordinateur (figure A.6). On voit à l’avant-plan trois fiches pour connecter des nappes de
disques dur (de trois couleurs différentes) et, immédiatement derrière, deux fiches pour les
cartes de mémoire vive (figure A.7). Un peu plus à gauche se trouve l’emplacement de la
Ey
pile bouton (utilisée pour alimenter l’horloge du PC lorsqu’il est éteint, voir figure A.8).
À gauche, trois connecteurs sont disponibles pour brancher des cartes d’extensions (fi-
gure A.9).
Figure A.5
Processeur, en place sur la
t
carte mère
gh
processeur-carte-mere.jpg
i
pyr
Co
Figure A.6
Puce contenant le BIOS
bios-carte-mere.jpg
Livre_silo 30 août 2013 16:32 Page 341
es
A – Travaux pratiques
341
l
rol
t Ey
ide-et-ram-carte-mere.jpg
gh
Figure A.7
i
Fiches de connexion pour les nappes de disques dur (colorées), fiches pour les barrettes de RAM
(munies de loquets latéraux blancs)
pyr
Co
emplacement-pile-bouton.jpg
Figure A.8
Emplacement de la pile bouton
Livre_silo 30 août 2013 16:32 Page 342
es
Informatique pour tous
342
l
rol
t Ey
pci-carte-mere.jpg
gh
Figure A.9
Connecteurs pour les cartes d’extension
i
pyr
Retrait du processeur
Le processeur peut être enlevé. Pour cela, il convient de manœuvrer le loquet qui le bloque
sur une fiche appelée socket et qui sert à réaliser les contacts électriques entre le processeur
et la carte mère. Une fois le loquet relevé, on peut enlever le processeur (voir figure A.10).
On constate que celui-ci est muni d’un grand nombre de petites pattes (figure A.11), qui
permettent la communication avec la carte mère.
Co
On peut également remarquer que le motif des trous sur le socket n’est pas tout à fait symé-
trique. C’est délibéré : cela assure qu’il n’y a qu’une seule façon d’y enficher le processeur :
la bonne (on dit qu’il y a un détrompeur).
Livre_silo 30 août 2013 16:32 Page 343
es
A – Travaux pratiques
343
l
rol
t Ey
socket-carte-mere.jpg
gh
Figure A.10
i
pyr
Co
pattes-processeur.jpg
Figure A.11
Processeur, sorti de son socket
Livre_silo 30 août 2013 16:32 Page 344
es
Informatique pour tous
344
l
rol
A.3.3 Mise en œuvre
Passons maintenant à un vrai PC. Une fois le côté du PC ouvert, on trouve quelque chose
ressemblant à la figure A.12.
Ey
Figure A.12
Vue globale d’une unité centrale après ouverture du panneau latéral. L’arrière de l’unité se trouve à gauche.
t
i gh
pyr
Co
pc-vincent-vue-globale.jpg
Livre_silo 30 août 2013 16:32 Page 345
es
A – Travaux pratiques
345
l
rol
Repérage
On peut repérer :
• Le bloc d’alimentation. Il est en haut à gauche et il en sort un faisceau de fils électriques.
Une partie de ces câbles est reliée à la carte mère (ici à gauche du gros ventilateur).
Ey
• Des ventilateurs. Un est situé sur la carcasse à l’arrière de l’unité. Quelques fils d’ali-
mentation de commande viennent de la carte mère jusqu’à lui. Un autre, énorme, est sur
la carte mère, au-dessus d’un bloc métallique ressemblant à un radiateur. Il s’agit effecti-
vement d’un radiateur, destiné à dissiper la chaleur du processeur, aidé par le ventilateur
qui assure une circulation d’air.
• Trois connecteurs d’extension. Un seul, celui situé le plus bas, est occupé par un circuit
imprimé.
t
• La RAM. Elle se présente sous forme de barrettes enfichables. Ici, on voit deux empla-
cements pour des barrettes mémoire (verticaux). Seul celui de gauche est occupé, l’autre
gh
est libre, ce qui laisse penser qu’on pourrait augmenter la capacité de ce PC en mémoire
vive par l’ajout d’une autre barrette.
• La pile bouton. Où est-elle ?
• Des sortes de ruban. Ils partent de la carte mère en bas à droite et la relient au lecteur
de DVD et au graveur de CD situés en haut à droite, ainsi qu’au disque dur (exactement
i
pyr
figure A.14). Il est fixé au boîtier dans un berceau métallique par des vis sur son côté.
Changer le disque dur est extrêmement simple : il suffit de débrancher l’alimentation ainsi
que la nappe, de dévisser les vis qui le retiennent, d’enlever le disque dur et de le remplacer
par un autre.
Livre_silo 30 août 2013 16:32 Page 346
es
Informatique pour tous
346
l
rol
t Ey
disque-dur.jpg
gh
Figure A.13
i
pyr
Co
nappe-carte-mere.jpg
Figure A.14
Le branchement des nappes sur la carte mère (la nappe visible est celle du lecteur de DVD et
cache presque totalement celle du disque dur)
Livre_silo 30 août 2013 16:32 Page 347
es
A – Travaux pratiques
347
l
rol
Changement d’une carte d’extension
Les cartes d’extension servent à apporter de nouvelles fonctionnalités matérielles au PC.
Le plus souvent, il s’agit de cartes réseau (cartes ethernet), qui sont maintenant intégrées
sur la carte mère. Il arrive cependant qu’on veuille ajouter une deuxième carte réseau ou
Ey
simplement qu’on veuille pouvoir de nouveau avoir une connexion ethernet lorsque la carte
intégrée est en panne.
Ici, la carte du PC (voir figure A.15) est une carte MODEM, permettant de se connecter
au réseau téléphonique pour émettre des appels, envoyer/recevoir des fax ou se connecter à
Internet (ou un autre réseau) par le réseau téléphonique. Pour la retirer, il suffit de dévisser
la vis qui la maintient au boîtier (voir figure A.16) puis de la sortir de son logement.
t
EN PRATIQUE Manipuler une carte d’extension
gh
Il ne faut jamais toucher les composants sur la carte d’extension, et toujours préférer la
tenir par les côtés.
i
pyr
Co
carte-modem.jpg
Figure A.15
Carte MODEM en place dans l’ordinateur.
Livre_silo 30 août 2013 16:32 Page 348
es
Informatique pour tous
348
l
rol
t Ey
carte-modem-seule.jpg
gh
Figure A.16
Carte MODEM, retirée.
i
pyr
Accès au processeur
On tente maintenant d’aller retirer le processeur. Pour tout travail à l’intérieur de l’unité
centrale, il est préférable de la coucher.
Il convient d’abord d’enlever le ventilateur. Il est en général fixé sur le radiateur. Ici, il est
en fait clipsé sur une pièce plastique qui semble difficilement séparable du processeur. Voir
figure A.18 ce que ça donne après retrait du ventilateur.
Livre_silo 30 août 2013 16:32 Page 349
es
A – Travaux pratiques
349
l
rol
t Ey
barette-ram.jpg
gh
Figure A.17
Barrette de RAM (on voit distinctement les 8 puces constituant autant de modules mémoire)
i
pyr
Co
radiateur-en-place.jpg
Figure A.18
Le radiateur du processeur
Livre_silo 30 août 2013 16:32 Page 350
es
Informatique pour tous
350
l
rol
EN PRATIQUE Que de poussières !
On constate que le ventilateur figure A.18 est plein de poussière. Le propriétaire de ce PC
oublierait-il de faire le ménage chez lui ? Non, l’explication est tout autre.
Les ventilateurs brassant de l’air en permanence, les poussières ambiantes circulent dans
Ey
l’unité centrale et s’y accumulent au fil du temps, en particulier au niveau des ventilateurs
et du radiateur, réduisant leur efficacité. L’augmentation de température qui en résulte fait
vieillir plus vite les composants, conduit les capteurs de température à faire tourner plus
vite les ventilateurs (le niveau de bruit peut augmenter sensiblement) voire à couper l’ali-
mentation en cas de surchauffe trop importante. Enfin, les poussières peuvent être sources
de courts-circuits qui empêchent complètement la machine de fonctionner.
C’est donc une bonne idée de nettoyer l’intérieur d’un PC de bureau. Attention cependant :
il ne faut pas toucher à la carte mère, car cela risque de l’endommager. Il suffira de souffler
dessus, soit avec un appareil adapté (un sèche-cheveux est inefficace), soit à la bouche,
t
auquel cas mieux vaut fermer les yeux et ne pas craindre la poussière (les auteurs déclinent
toute responsabilité). On peut en revanche gratter la poussière déposée sur les ventilateurs
gh
et le radiateur sans danger. Pour atteindre le radiateur du processeur, il faudra cependant
démonter le ventilateur.
Il n’est pas rare que souffler sur l’intérieur d’une unité centrale ramène le PC à la vie, mais
il n’y a rien de magique là-dedans…
Dernière chose : le problème est le même pour un ordinateur portable, mais le démontage
et le remontage en sont beaucoup plus délicats que pour un PC de bureau…
i
pyr
Il faut maintenant enlever le radiateur. Il est en général fixé sur le socket par un clip mé-
tallique. Il est difficile d’y accéder. On y parvient en général à l’aide d’un tournevis. Il faut
une certaine force pour appuyer sur le clip, mais éviter absolument de déraper : un coup de
tournevis sur la carte mère ne lui ferait pas de bien. Ici, l’accès semble plus facile du côté
du bloc d’alimentation (figure A.19)
Ça y est, le radiateur est retiré (figure A.20). Attention, sous le radiateur on trouvera pro-
Co
Livre_silo 30 août 2013 16:32 Page 351
es
A – Travaux pratiques
351
l
rol
t Ey
retrait-clip-radiateur.jpg
gh
Figure A.19
i
Retrait du clip du radiateur. Les picots (en blanc) du socket étaient enfichés dans les trous de la
fiche métallique du radiateur.
pyr
Co
radiateur.jpg
Figure A.20
Radiateur du PC, avant nettoyage.
Livre_silo 30 août 2013 16:32 Page 352
es
Informatique pour tous
352
l
rol
A.4 Résolution d’une équation du second degré
avec gestion de la comparaison à zéro
1 Écrire un programme qui, étant donnée une équation du second degré à coefficients
Ey
entiers, détermine le nombre de ses solutions et leurs valeurs éventuelles.
2 Tester ce programme pour l’équation x2 + 6x + 9 = 0.
3 Adapter ce programme pour qu’il accepte des coefficients non entiers.
4 Tester ce second programme sur les équations x2 + 10−10 = 0 et x2 − 10−10 = 0.
Montrer qu’une infime variation sur l’un des coefficients fait franchir la ligne qui sépare
les cas où l’équation a des solutions des cas où elle n’en a pas.
5 Tester le second programme sur l’équation 0, 1x2 + 0, 6x + 0, 9 = 0. Comparer les
t
résultats avec ceux obtenus à la question 2. Que doit-on en penser ? Comment explique-
t-on ce phénomène ?
gh
6 Tester ce second programme sur l’équation x2 + (1 + 2−50 )x + 0, 25 + 2−51 = 0
et comparer les résultats obtenus avec ceux que prévoit la résolution exacte de cette
équation. Expliquer encore une fois les différences constatées.
7 On travaille pour cette question sous l’hypothèse (raisonnable vus les calculs effectués)
que la norme IEEE 754 assure les propriétés suivantes des arrondis :
i
• Si le discriminant est strictement positif, alors sa valeur approchée calculée est positive
pyr
ou nulle.
• De même, si le discriminant est strictement négatif, alors sa valeur approchée calculée
est négative ou nulle.
• Enfin, si le discriminant est nul, sa valeur approchée calculée peut être quelconque.
Adapter alors ce programme pour qu’il n’affiche que des affirmations certaines à propos
des polynômes qui lui sont fournis.
8 Enfin, on peut quantifier l’erreur commise sur le calcul du discriminant. On a vu dans
Co
l’exercice 2.54 que l’erreur relative introduite par une multiplication est de 2−52 : quelle
est donc l’erreur relative maximale commise dans le calcul du discriminant ? En-dessous
de quel seuil doit-on considérer que le discriminant pourrait être nul ? Adapter le pro-
gramme en conséquence.
Livre_silo 30 août 2013 16:32 Page 353
es
A – Travaux pratiques
353
l
rol
2 Afin d’obtenir une représentation en mémoire plus efficace, les constructeurs de calcula-
trices regroupent parfois trois chiffres décimaux sur un même groupe de bits. Expliquer
pourquoi ce choix est a priori meilleur que celui de la question précédente. Imaginer
également les problèmes que cela peut poser.
3 Les dépassements arithmétiques sur les entiers existent-ils sur votre calculatrice ? Pour
Ey
le vérifier, on pourra par exemple essayer de calculer et d’afficher les puissances de 2
successives : au bout d’un certain nombre d’itérations, la façon dont le nombre calculé
s’affiche est différente. Que peut-on en conclure quant à la représentation des nombres
dans la mémoire des calculatrices ⁵ ?
4 On va maintenant étudier la représentation en machine des nombres à virgule flottante.
a) Dans un premier temps, on peut déterminer la taille de la mantisse. Pour cela, pro-
grammer ou exécuter à la main l’algorithme suivant, en prenant bien soin d’écrire 1.0
t
à la première instruction pour assurer que a sera un nombre à virgule flottante :
gh
a ← 1.0
tant que a + 1.0 − a = 1.0 faire
a ← 2.0 ∗ a
Résultat : a
Expliquer pourquoi la condition de la boucle tant que finit par être fausse et déduire
i
pyr
b) Ensuite, on peut évaluer le nombre de chiffres sur lequel l’exposant est représenté.
Pour cela, reprendre l’algorithme précédent avec une boucle tant que dont la condi-
tion est 2.0∗a ̸= a. Pour quelle raison cet algorithme finit-il également par s’arrêter ?
Déduire de la dernière valeur prise par a la taille de l’exposant en mémoire. Vérifier
qu’une limite similaire existe pour les exposants négatifs.
c) Les calculatrices possèdent pour la plupart des fonctions qui permettent de vérifier
l’état de la mémoire. À l’aide de ces fonctions, déterminer la place utilisée par la va-
Co
riable a en mémoire : si cette valeur est cohérente avec les résultats des deux questions
précédentes, quelle est l’unité de taille mémoire utilisée ici ?
Sur les calculatrices permettant d’écrire des entiers longs, on vérifiera également la
croissance de la taille mémoire utilisée par un entier en fonction de sa valeur.
5. Certaines calculatrices permettent de faire des calculs symboliques ; elles disposent alors d’entiers longs
similaires à ceux de Python. Une lecture approfondie de la documentation révèle tout de même que le nombre
de chiffres autorisé est borné, par exemple 614 chiffres sur les TI 89 et 92. Un algorithme similaire à celui de la
prochaine question avec des valeurs entières met en lumière cette limitation.
Livre_silo 30 août 2013 16:32 Page 354
es
Informatique pour tous
354
l
rol
A.6 Arithmétique et cryptographie
A.6.1 Algorithme d’Euclide
Algorithme d’Euclide originel
Ey
Soient u, v ∈ N. L’algorithme suivant, dit algorithme d’Euclide, calcule le plus grand divi-
seur commun (PGCD) de u et v :
A1 Si v = 0 alors la réponse est u.
A2 Faire (u, v) ← (v, u mod v). Retourner en A1.
Écrire une fonction euclide implantant cet algorithme.
t
Complexité
gh
La complexité de l’algorithme d’Euclide est donnée par le résultat suivant :
éorème (G. Lamé, 1845). Si 0 ⩽ u, v < N , le nombre de divisions dans l’algorithme
√ √
d’Euclide appliqué à u et v est au plus ⌈logϕ ( 5N )⌉ − 2, où ϕ est le nombre d’or 1+2 5 .
Soient u, v ∈ N. L’algorithme d’Euclide peut être adapté pour calculer, en même temps
pyr
Livre_silo 30 août 2013 16:32 Page 355
es
A – Travaux pratiques
355
l
rol
A.6.2 Décomposition en facteurs premiers
Étant donné n ∈ N, n s’écrit de manière unique sous la forme :
n = p1 p2 . . . pk avec p1 ⩽ p2 ⩽ . . . ⩽ pk et pi premier
Ey
On se propose ici de déterminer les facteurs pi .
√ √
qui inclut tous les nombres premiers ⩽ n et au moins un élément dk ⩾ n. Alors,
pyr
A6 t ← t + 1, pt ← n. C’est terminé.
Écrire une fonction decomp prenant en argument l’entier n, une suite (dk ) pour n, et ren-
voyant la suite croissante des facteurs premiers pi de n.
On pourrait prendre pour (dk ) la suite 2, 3, 5, 7, . . ., c’est-à-dire 2 puis tous les impairs
à partir de 3. On prendra plutôt la suite 2, 3, 5, 7, 11, 13, 17, 19, 23, 25, . . ., c’est-à-dire
ajouter alternativement 2 et 4 à partir de 5 (on supprime ainsi tous les multiples de 2 et 3).
On peut gagner encore 20 % sur cette suite en supprimant les entiers de la forme 30m ± 5,
et encore 14 % en supprimant les multiples de 7, etc. Si n est petit, on peut utiliser une
table des nombres premiers (pour n ⩽ 106 , il n’y a que 168 nombres premiers).
Livre_silo 30 août 2013 16:32 Page 356
es
Informatique pour tous
356
l
rol
Complexité
Déterminer la complexité de cet algorithme est un problème très difficile, mais en pratique
l’algorithme n’est plus guère utilisable au-delà de 106 .
Méthode de Fermat
Ey
On se propose ici de réaliser un autre algorithme de décomposition en facteurs premiers,
dû à Pierre de Fermat (1643), plus adapté à la recherche de grands facteurs premiers.
Soit N impair s’écrivant sous la forme uv, avec u ⩽ v. On définit alors :
x = (u + v)/2, y = (v − u)/2
t
et on a :
N = x2 − y 2 , 0⩽y<x⩽N
gh
La méthode de Fermat consiste à rechercher des valeurs de x et y satisfaisant les conditions
ci-dessus. Étant donné N √ impair, l’algorithme suivant détermine le plus grand facteur
de N inférieur ou égal à N :
√ √
A1 Initialiser : x′ ← 2⌊ N ⌋ + 1, y ′ ← 1 et r ← ⌊ N ⌋2 − N
i
(dans la suite, x′ correspond à 2x + 1, y ′ à 2y + 1 et r à x2 + y 2 − N ).
pyr
Livre_silo 30 août 2013 16:32 Page 357
es
A – Travaux pratiques
357
l
rol
Test de Fermat
Soit n un entier. On dit que n passe le test de Fermat de base a si an−1 ≡ 1[n]. On sait que
si n est premier, alors pour tout entier a ∈]0, n[, n passe le test de Fermat de base a. Donc,
par contraposée, si n ne passe pas le test de Fermat de base a, alors n n’est pas premier.
Ey
On dit dans ce cas que a est un témoin de non-primalité de n pour le test de Fermat. Si
n passe le test de Fermat de base a mais n’est pas un nombre premier, on dit que n est un
pseudo-premier de Fermat de base a.
Écrire une fonction passe_fermat(a,n) à valeur booléenne indiquant si n passe le test de
Fermat de base a.
Y a-t-il des entiers compris entre 1 et 1 000 qui soient des pseudo-premiers de Fermat de
t
base 2 (on les appelle aussi nombres de Poulet) ? Lesquels ?
Y a-t-il des entiers n compris entre 1 et 1 000 qui soient des pseudo-premiers de Fermat
gh
de base a pour tout a ? Pour tout a appartenant à ]0, n[ ?
Pour tester si an−1 est congru à 1 modulo n, on peut calculer a**(n-1) % n. Combien de
temps ce calcul prend-il pour n = 100 000 000 et a = 2 ? Python propose une fonction
spécialisée pour effectuer ce calcul : pow. On peut calculer pow(a, n-1, n). Comparer avec le
i
pyr
Les nombres premiers sont très utiles en cryptographie. Le logiciel de chiffrement PGP
utilise ainsi les tests de Fermat de bases 2, 3, 5 et 7 pour décider si un nombre est premier ⁶.
Écrire une fonction premier_PGP(n) « testant » si un nombre est premier avec la méthode
utilisée par PGP.
En considérant que ce test est parfait, écrire une fonction premier_suivant(n) rendant le plus
petit nombre premier strictement supérieur à n.
Co
Test de Miller-Rabin
On propose maintenant un autre test, appelé test de Miller-Rabin. Il fonctionne de la
façon suivante. Pour tester si un entier n est premier, on commence par écrire n − 1 sous
la forme 2s × m, où m est impair. Soit a ∈ J1, n − 1K. Le test repose sur le résultat
suivant : si n est premier, alors ou bien am ≡ 1[n], ou bien il existe d ∈ J0, s − 1K vérifiant
d
a2 .m ≡ −1[n]. On peut démontrer ce résultat en utilisant le petit théorème de Fermat,
le fait que si n est premier Z/nZ est un corps et le fait que dans tout anneau intègre,
l’équation x2 = 1 a au plus deux solutions ⁷ (1 et −1).
6. Le risque de choisir accidentellement un nombre non premier est apparemment très faible pour les plages
de nombres testées par PGP.
7. Dans certains anneaux intègres, il n’y en a qu’une, par exemple dans Z/2Z, où −1 = 1.
Livre_silo 30 août 2013 16:32 Page 358
es
Informatique pour tous
358
l
rol
On dira que a est un menteur fort s’il vérifie les conditions précédentes et si n n’est pas
premier.
On peut montrer qu’au plus un quart des valeurs de J1, n−1K sont des menteurs forts ⁸. Le
test de Miller-Rabin s’effectue donc en pratique de la façon suivante : prendre une valeur a
au hasard comprise entre 1 et n − 1 et effectuer le test avec cette base a. Si a respecte les
Ey
conditions données ci-avant, n est probablement premier ; dans ce cas, on recommence
avec un nouveau a pris au hasard. Si au bout de 40 essais, on n’a pas trouvé de a violant
les conditions données plus haut, on considérera que le nombre donné était premier.
Si le nombre testé est premier, quelle est la probabilité que le test de Miller-Rabin le déclare
non premier ? S’il n’est pas premier, majorer la probabilité que le test de Miller-Rabin le
déclare premier.
t
Pour effectuer le test aussi vite que possible, on calculera le reste de am modulo n
gh
et on effectuera ensuite des mises au carré modulo n successives pour calculer a2m ,
s−1
a4m ,…,a2 m . De plus, on n’est pas obligé de calculer toutes ces valeurs : on s’arrête
dès le début si am ≡ 1[n], on s’arrête également dès qu’on trouve la valeur −1 et enfin, on
peut aussi s’arrêter si l’on trouve la valeur 1. Pourquoi ?
Mettre en œuvre ce test pour vérifier si les grands nombres trouvés avec le test PGP sont
i
bien premiers.
pyr
seule connaît. Lorsqu’on veut envoyer un message M à une personne A, on le code avec la
clé publique PA du destinataire. Ce dernier le décode alors avec sa clé secrète SA et peut
le lire.
Un tel système fonctionne donc si on a les propriétés suivantes :
1 S(P (M )) = M pour tout message M .
2 Les paires (S, P ) sont toutes distinctes.
3 Découvrir la clé secrète S à partir de la clé publique P est aussi difficile que déchiffrer
le message codé.
4 Une paire (S, P ) peut se calculer facilement.
La méthode RSA fonctionne de la manière suivante : soient x, y et s trois grands nombres
premiers, tels que x, y ⩽ s. Soient N = xy et p tel que ps mod (x − 1)(y − 1) = 1. On
peut alors montrer que pour tout M , on a M ps = M (mod N ).
8. Voir sur Wikipédia en anglais l’article Miller–Rabin primality test et notamment l’article de René Schoof
mentionné dans les références (article consulté le 22 mars 2013).
Livre_silo 30 août 2013 16:32 Page 359
es
A – Travaux pratiques
359
l
rol
La clé publique est alors le couple (N, p) et la clé secrète le couple (N, s). Pour coder un
message, on commence par le découper en entiers inférieurs à N et on élève alors ces entiers
à la puissance p modulo N . Pour le décoder, on élève les entiers composant le message à
la puissance s modulo N .
Ey
En supposant que l’on dispose d’un générateur de grands nombres premiers, comment
engendrer un couple clé publique-clé secrète pour la méthode RSA ?
Un message est codé de la manière suivante. Chaque caractère est d’abord remplacé par
son code ASCII (65 pour A, 66 pour B, etc.). Le message est ensuite chiffré en utilisant la
clé publique (N, p) où N = 49808911 et p = 5685669. Le résultat est le suivant :
49583279, 4553592, 17767401, 16172223, 33062955, 33599637,
t
17767401, 11607763, 17767401, 9275561, 11607763, 35959722, 17767401,
gh
44022065, 3148857, 17767401, 40136246, 13922222, 16172223, 17767401,
4553592, 11607763, 16172223, 25708244, 11607763, 25708244, 3148857,
36260425, 17767401, 35959722, 3148857, 29735027, 4553592, 3148857,
29538442, 3148857, 16172223, 31299930, 17767401, 44022065, 3148857,
17767401, 40136246, 13922222, 16172223, 35959722, 17767401, 9275561,
i
pyr
« Craquer » ce codage RSA pour découvrir le message en clair. On rappelle que la fonction
chr renvoie le caractère dont on donne le code ASCII et que l’on peut afficher le caractère c
sans qu’il soit suivi d’un retour chariot avec print(c, end="").
Donner une idée des tailles de nombres premiers qu’il faut choisir pour ne plus pouvoir
casser ce codage par factorisation de N . Il est à noter qu’il existe d’autres méthodes pour
Co
casser le codage choisi ici sans factoriser N . En pratique la mise en œuvre de RSA est donc
plus complexe.
Livre_silo 30 août 2013 16:32 Page 360
es
Informatique pour tous
360
l
rol
cercle.pdf cercle4.pdf cercle5.pdf
t Ey Figure A.21
Représentation d’une image par une matrice de bits
gh
Cette méthode n’est bien sûr pas spécifique au cercle et fonctionne avec un tracé quel-
conque. On est donc en mesure de remplacer une image arbitraire par un ensemble fini de
pixels (10 × 10 dans cet exemple). Pour une image en noir et blanc, cet ensemble de pixels
se traduit directement en une matrice de bits, 0 pour noir et 1 pour blanc par exemple. On
parle donc de représentation bitmap des images.
i
Si on veut introduire plus de nuances, on peut commencer par attribuer à chaque pixel
pyr
non plus la couleur noir ou blanc, mais un niveau de gris représenté par un entier entre
0 (pour noir) et 255 (pour blanc). On choisit cette échelle car un pixel se représente alors
exactement sur un octet et cela suffit à exprimer des nuances quasi indiscernables à l’œil
nu. On verra en fin de TP comment cette représentation s’étend à des pixels colorés.
Les images seront lues et écrites dans des fichiers au moyen des commandes présentées en
annexe B.2. On travaille pour l’instant en niveaux de gris : si un objet im de type Image est
Co
On peut aussi vouloir créer le négatif (au sens photographique) de l’image. Cela consiste
à inverser le blanc et le noir, le gris clair et le gris foncé, etc. Une simple fonction affine de
coefficient directeur négatif appliquée à la valeur des pixels fait l’affaire :
Livre_silo 30 août 2013 16:32 Page 361
es
A – Travaux pratiques
361
l
rol
def negatif(image):
for ligne in image:
for i in range(len(ligne)):
ligne[i] = 255 - ligne[i]
Il apparaît vite que beaucoup des fonctions de traitement de l’image auront cette structure.
Ey
On peut donc commencer par rendre leur écriture plus efficace.
1 Écrire une fonction traitement qui prend pour arguments une image et une fonction,
puis applique cette fonction sur la valeur de chacun des pixels de l’image.
2 Redéfinir effacer et negatif à l’aide de traitement. L’utilisation d’une fonction anonyme
est recommandée.
3 On joue maintenant sur la luminosité de l’image : puisque les valeurs les plus hautes
t
sont les plus claires, il suffit d’augmenter les valeurs des pixels pour éclaircir l’image
et de les diminuer pour l’assombrir. On est alors tenté d’appeler traitement avec une
gh
fonction comme lambda x: x + 50.
On s’aperçoit vite que l’effet obtenu n’est pas celui escompté : tous les pixels dont la
valeur était supérieure à 205 reçoivent une valeur entre 255 et 305. Comme les valeurs
des pixels sont comptées modulo 255, ces pixels deviennent en fait presque noirs.
Écrire une fonction qui éclaircit une image en s’assurant par un test que la valeur des
i
4 La solution précédente n’est pas satisfaisante car elle crée de grands aplats blancs dans
pyr
les zones les plus claires (on pourra augmenter la valeur des pixels de 100 ou 150 pour
s’en convaincre).
À l’aide de la fonction racine carrée, construire une bijection de [0; 255] dans [0; 255].
Programmer cette fonction pour qu’elle manipule et renvoie des entiers (ce n’est alors
évidemment plus une bijection).
Passer cette fonction en paramètre à traitement et vérifier le résultat obtenu ⁹.
Co
9. Cette méthode se généralise aux fonctions de la forme x 7→ xγ avec γ > 0. C’est la fameuse correction
gamma proposée dans les logiciels de retouche photographique.
Livre_silo 30 août 2013 16:32 Page 362
es
Informatique pour tous
362
l
rol
On obtient différents degrés de floutage selon qu’on considère les 9 pixels immédiate-
ment adjacents, ou bien un carré de 5 voire 7 pixels de côté centré sur le pixel à calculer.
2 À l’inverse, on peut chercher des contours dans une image ; il s’agit de détecter les en-
droits où les pixels changent brutalement de couleur. Pour cela, on calcule pour chaque
pixel une moyenne pondérée des pixels qui l’entourent, par exemple avec les coefficients :
Ey
−1 −1 −1
−1 8 −1
−1 −1 −1
Quel est le résultat de ce calcul pour un pixel qui est entouré d’autres pixels d’une couleur
t
proche ? Et si au contraire il est d’une couleur nettement différente de celle d’une partie
de ses voisins ?
gh
Programmer cette méthode de détection des contours.
3 Expérimenter avec d’autres coefficients. On retrouve ainsi encore d’autres fonctionna-
lités des logiciels de traitement d’image.
pyr
A.7.4 En couleurs
Pour représenter une couleur, on peut la décomposer en trois intensités de bleu, de vert et
de rouge ; c’est d’ailleurs la façon dont l’œil humain perçoit les couleurs. Numériquement,
cela signifie qu’on peut conserver le même principe de représentation, en donnant pour
chaque pixel trois nombres entre 0 et 255. Il faut donc trois octets pour représenter un
pixel de couleur.
Dans les matrices créées par le module Image pour représenter des images en couleur, cela
se traduit par le fait que chaque pixel est représenté non plus par un entier mais par un
tableau de trois entiers.
Livre_silo 30 août 2013 16:32 Page 363
es
A – Travaux pratiques
363
l
rol
Les algorithmes écrits aux questions précédentes restent pour la plupart valables sur des
images en couleur, en les appliquant composante par composante.
Il est également possible d’appliquer un traitement différent sur chaque composante, par
exemple accentuer le niveau de rouge et atténuer celui de bleu et de vert pour obtenir des
Ey
couleurs plus chaudes.
pyr
relation table
attribut colonne
valeur ligne
domaine type
Co
Livre_silo 30 août 2013 16:32 Page 364
es
Informatique pour tous
364
l
rol
La première chose que l’on remarque ici, c’est que l’interface propose bien plus de possi-
bilités que la simple définition d’un nom et d’un type. En effet, le modèle relationnel est
épuré par rapport aux gestionnaires de bases de données.
Pour le moment, on ne remplit que le nom et le type des colonnes et on laisse les autres
Ey
champs avec leur valeur par défaut.
Pour choisir le type d’une colonne, l’interface propose un menu déroulant avec un choix
intimidant (figure A.25). Voici un descriptif des principaux types :
INT Le type des entiers relatifs. La plus grande et la plus petite valeur pouvant être stockées dépendent du
système (voir chapitre 2).
t
TEXT Le type des chaînes de caractères de longueur quelconque. Ce type est difficile à manipuler par le
gh
gestionnaire de bases de données et on lui préférera le type suivant dans la majorité des cas.
VARCHAR Le type des chaînes de caractères de longueur maximale fixée (espaces compris). Cette longueur est
fournie dans le champ suivant de l’interface. Cette contrainte aide le gestionnaire de bases de données
à gérer au mieux ces valeurs.
DECIMAL Le type des nombres décimaux. C’est la représentation à privilégier pour des données financières, car
i
pyr
Figure A.22
L’écran d’accueil de
phpMyAdmin
Co
cap002.pdf
Livre_silo 30 août 2013 16:32 Page 365
es
A – Travaux pratiques
365
l
rol
Figure A.23
Création d’une nouvelle table
Ey
cap003.pdf
t
gh
Figure A.24
Définition des colonnes
i
pyr
cap004.pdf
Co
Figure A.25
Types des colonnes
cap006.pdf
Livre_silo 30 août 2013 16:32 Page 366
es
Informatique pour tous
366
l
rol
Le schéma de la table livre est présenté figure A.26. Une fois ces champs remplis, un appui
sur Sauvegarder crée la table.
On est alors renvoyé vers une page de gestion de la table. On remarque que l’onglet Afficher
est grisé car la table est vide pour le moment (figure A.27).
Ey
Figure A.26
Le schéma de la table livre
cap005.pdf
t
gh
Figure A.27
La table nouvellement créée est vide. cap007.pdf
i
En cliquant sur l’onglet Insérer, on ouvre la boîte de dialogue de création de valeurs. Il suffit
de remplir les valeurs associées à chaque colonne et de cliquer sur Exécuter (figure A.28).
À chaque fois, phpMyAdmin présente la requête qui lui a servi pour insérer la nouvelle valeur
(figure A.29). Une partie de ce langage de requête appelé SQL est présentée au chapitre 11.
Co
La table n’étant plus vide, on peut cliquer sur l’onglet Afficher (figure A.30). Tout d’abord,
un bandeau d’avertissement indique que, pour l’instant, la table fonctionne dans un mode
limité car une colonne unique n’a pas été définie. Ici, on peut remplacer « colonne unique »
par « clé primaire » pour simplifier : une table sans clé primaire est considérée comme une
anomalie par le système. On verra dans le prochain TP comment définir des clés primaires ;
pour l’instant on ignore cet avertissement.
Figure A.28
Insertion d’une valeur
cap008.pdf
Livre_silo 30 août 2013 16:32 Page 367
es
A – Travaux pratiques
367
l
rol
Figure A.29
Après l’insertion
Ey
cap009.pdf
t
Figure A.30
gh
Affichage de la table
cap010.pdf
i
pyr
1 Insérer des valeurs pour obtenir l’affichage de la table correspondant à la figure A.31.
Co
2 Créer une nouvelle table vol modélisant les liaisons aériennes au départ d’un aéroport.
Chaque vol aura pour attributs un numéro, un horaire et une destination.
3 Créer une nouvelle table voyageur modélisant les voyageurs qui se présentent à cet aé-
roport, avec au moins quatre attributs bien choisis.
4 Insérer quelques valeurs représentatives dans la table des vols et dans la table des voya-
geurs.
Figure A.31
Affichage de la table livre remplie
cap011.pdf
Livre_silo 30 août 2013 16:32 Page 368
es
Informatique pour tous
368
l
rol
SAVOIR-FAIRE Créer et alimenter une base de données simple à l’aide
d’une interface graphique
Les noms des menus sont explicites :
Ey
• Au niveau de la base, Structure permet d’accéder aux tables existantes et d’en créer
une nouvelle (pour cela, on définit son schéma relationnel — noms et types des
attributs — au moyen du formulaire proposé).
• Ensuite, au niveau d’une table, Structure sert à modifier son schéma relationnel au
besoin. C’est également dans cet onglet qu’on peut déclarer une clé primaire.
• Pour alimenter une table, on clique sur Insérer et on remplit un formulaire.
t
gh
A.9 Clés primaires et clés étrangères
Dans ce TP, on reprend la table livre du TP précédent. Il conviendra donc de la créer si
ce n’est pas encore fait.
i
La définition d’une clé primaire est très simple, il suffit de se rendre dans l’onglet Structure
de la table et de cliquer sur le bouton Clé primaire pour la colonne en question (figure A.32).
Dans un premier temps, on essaye de déclarer auteur comme étant une clé primaire de la re-
lation livre. Une boîte de dialogue présente la commande qui sera effectivement transmise
au gestionnaire de base de données (figure A.33). Après avoir appuyé sur OK, un message
Co
d’erreur indique que la valeur “Gustave Flaubert” est dupliquée (figure A.34). Ainsi, la
colonne auteur n’est pas une clé primaire. Cela signifie que le gestionnaire de bases de don-
nées ne se contente pas de considérer n’importe quelle colonne comme une clé primaire ;
il vérifie que c’est le cas.
La colonne titre est ici une clé primaire ; on reprend donc les étapes précédentes avec cette
colonne. L’ajout s’effectue sans erreur et on voit dans l’arborescence des tables à gauche de la
fenêtre, qu’une entrée Index a été ajoutée et qu’elle contient la clé primaire (figure A.35).
Dans l’onglet Structure, il est aussi possible de cliquer sur le lien Index pour voir la clé
primaire et la manipuler (figure A.36). L’encadré ci-après fournit des compléments sur
cette notion d’index.
Figure A.32
Bouton Clé primaire cap012.pdf
Livre_silo 30 août 2013 16:32 Page 369
es
A – Travaux pratiques
369
l
rol
Figure A.33
Confirmation de la déclaration en tant que clé primaire
cap013.pdf
t Ey Figure A.34
Erreur car la clé primaire est invalide
cap014.pdf
i gh
Figure A.35
Arborescence de la base
pyr
cap016.pdf
Co
Figure A.36
Affichage des index
cap017.pdf
Livre_silo 30 août 2013 16:32 Page 370
es
Informatique pour tous
370
l
rol
Si on essaye d’effectuer une insertion avec le titre d’un livre existant (figure A.37), le ges-
tionnaire renvoie une erreur (figure A.38). Ainsi, le système s’assure que la colonne reste
une clé primaire à chaque insertion ou modification.
Ey
Figure A.37
Insertion avec un titre
existant
t cap018.pdf
gh
Figure A.38
Erreur de violation de la clé primaire
i
pyr
cap019.pdf
Co
Livre_silo 30 août 2013 16:32 Page 371
es
A – Travaux pratiques
371
l
rol
MySQL propose trois types d’index :
• Les clés primaires. Elles identifient uniquement les valeurs d’une table. On ne peut définir
qu’une seule clé primaire par table.
• Les index uniques. Il s’agit de clés primaires optionnelles. Une colonne peut être un index
unique si toutes ses valeurs différentes de NULL sont uniques. Une table peut comporter
Ey
plusieurs index uniques.
• Les index. Il s’agit de simples correspondances sans condition d’unicité. C’est le cas de
l’index de ce livre : une notion peut être associée à plusieurs pages.
Les index accélèrent les recherches, mais ralentissent les insertions et les modifications.
Ainsi, il n’est pas raisonnable d’indexer toutes les colonnes, de même que dans l’index d’un
livre, il n’est pas judicieux de mentionner tous les termes utilisés dans l’ouvrage. Choisir les
bonnes colonnes à indexer dépend d’une analyse fine des besoins.
t
A.9.2 Clé primaire auto-incrémentée
gh
MySQL, comme beaucoup d’autres gestionnaires de bases de données, dispose d’un mé-
canisme de création automatique de clé primaire. Il s’agit d’attribuer à chaque valeur un
identifiant numérique unique et, lors d’une insertion, d’affecter le numéro suivant. On
parle de clé primaire auto-incrémentée.
i
Comme il n’est pas possible d’avoir deux clés primaires pour une même table, on commence
pyr
par supprimer l’ancienne clé primaire. Dans la boîte de dialogue de la figure A.36, on clique
sur Supprimer et on confirme.
On ajoute maintenant une nouvelle colonne en début de table dans l’onglet Structure (fi-
gure A.39).
Pour ajouter une clé primaire auto-incrémentée, il faut définir une colonne de type INT.
Dans la liste Index, on sélectionne PRIMARY et on coche la case A.I., pour AUTO_INCREMENT
Co
(figure A.40).
Dessins/bdd/captures/cap020.pdf
Figure A.39
Ajout d’une colonne en début de table
Dessins/bdd/captures/cap021.pdf
Figure A.40
Champ pour la création d’une clé primaire auto-incrémentée
Livre_silo 30 août 2013 16:32 Page 372
es
Informatique pour tous
372
l
rol
On peut alors constater dans l’onglet Structure qu’une nouvelle colonne a été ajoutée en
début de table et, dans le champ EXTRA, on peut lire AUTO_INCREMENT (figure A.41).
En cliquant sur l’onglet Afficher, on voit que les lignes de cette table s’affichent dans l’ordre
croissant de la nouvelle clé primaire (figure A.42).
t Ey
Dessins/bdd/captures/cap022.pdf
Figure A.41
La nouvelle structure de la table livre avec la colonne id
gh
Figure A.42
Valeurs de la table livre modifiées par l’ajout de la colonne id
i
cap023.pdf
pyr
1 Créer une table emprunteur dont le schéma est donné figure A.43. La colonne livre_id
représente une clé étrangère vers la table livre munie de la clé primaire id.
2 Insérer des valeurs pour que le contenu de la table soit celui donné figure A.44.
3 Effectuer une opération qui affiche un message similaire à celui de la figure A.38.
4 Vérifier qu’il est possible d’insérer une valeur dont le livre_id soit invalide, c’est-à-dire
non compris dans les valeurs de la clé primaire de livre.
Dans la majorité des systèmes modernes de gestion de bases de données, il existe des mé-
canismes assurant qu’une clé étrangère ne compte que des valeurs valides tout au long des
manipulations de la table. De tels mécanismes sortent cependant du cadre de cet ouvrage.
Figure A.43
Schéma de la table emprunteur cap024.pdf
Livre_silo 30 août 2013 16:32 Page 373
es
A – Travaux pratiques
373
l
rol
Figure A.44
Contenu de la table emprunteur cap025.pdf
Ey
Pour lancer une requête sur une base, il suffit d’aller dans l’onglet SQL et d’y taper la
requête en langage SQL avec la syntaxe présentée au chapitre 11. Le résultat de la requête
s’affiche sous forme de table ; si on s’aperçoit qu’on a commis une erreur dans la requête, il
t
est possible de la corriger puis de la relancer.
gh
L’onglet Rechercher permet par ailleurs d’effectuer des projections et des sélections élaborées
sans avoir à écrire de conditions booléennes, mais il se limite à ce type de requête.
1 Reprendre (ou créer) les tables vol et voyageur. Créer une nouvelle table avion avec
une clé primaire auto-incrémentée modélisant la flotte d’avions qui transitent par cet
aéroport.
i
2 De quelle table faut-il modifier le schéma relationnel pour affecter un avion à chaque
pyr
vol ? Le faire.
3 Écrire une requête qui détermine la destination de chaque avion.
4 Écrire une requête qui détermine l’identifiant de l’avion que prendra chaque passager.
5 Écrire une requête qui compte combien de passagers embarqueront sur chaque vol.
6 Écrire une requête qui vérifie que le même avion n’est pas affecté à deux vols distincts.
Co
Livre_silo 30 août 2013 16:32 Page 374
esl
rol
t Ey
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 375
l es
rol
B
Compléments
t Ey
gh
sur les entrées/sorties
i
pyr
Cette annexe décrit trois façons de produire des programmes qui utilisent ou produisent
des données volumineuses ou non numériques.
Co
Livre_silo 30 août 2013 16:32 Page 376
es
Informatique pour tous
376
l
rol
En pratique :
• L’ouverture du fichier consiste à associer (via la fonction open) un objet de type file à
un fichier existant. On peut voir cet objet comme quelque chose qui pointe au début
du fichier et auquel on peut demander de lire des lignes et d’en donner le résultat. Le
Ey
principe n’est pas d’aller voir à un endroit arbitraire, contrairement à ce que l’on ferait dans
un tableau.
• La lecture des lignes peut se faire soit pas à pas via la méthode readline, qui lit la ligne
courante et passe à la suivante, soit globalement via la méthode readlines, qui crée l’itéra-
teur de toutes les lignes et permet ainsi de traiter ces dernières dans une boucle. Chaque
ligne est en fait une chaîne de caractères (string).
• On ferme le fichier via la méthode close : il est conseillé d’écrire cette instruction aussitôt
t
après avoir écrit l’instruction open. Si on l’oublie, la plupart du temps on ne verra pas
la différence. Néanmoins, c’est une bonne habitude à prendre car on libère ainsi des
gh
ressources dont on n’a plus l’utilité.
Dans l’exemple suivant, on ouvre un fichier dont le nom est tagada.txt, on affiche la
première ligne lue, puis on calcule la somme des longueurs de toutes les lignes suivantes.
mon_fichier = open('tagada.txt','r') # 'r' pour lecture
print(mon_fichier.readline())
i
s = 0
for L in mon_fichier.readlines():
pyr
s += len(L)
mon_fichier.close()
Une façon encore plus simple de parcourir les lignes d’un fichier consiste à écrire :
for L in mon_fichier. Cela ressemble à la méthode readlines, à une subtile amélioration près.
La séquence des lignes, qui peut être grande, n’est pas créée avant d’être parcourue : les
lignes sont lues à la volée, dès que la précédente a été traitée.
Co
Exercice B.1 Exécuter le programme précédent sur un petit fichier de quelques lignes que vous aurez
préalablement créé. Comparer le résultat avec celui attendu, puis expliquer.
Chaque ligne L est terminée par un caractère spécial de retour à la ligne que l’on note 'n'. Il est affiché
par print et compté par len. On peut le visualiser en observant la valeur de L ou bien en la transformant
en liste.
Livre_silo 30 août 2013 16:32 Page 377
es
B – Compléments sur les entrées/sorties
377
l
rol
Si un fichier regroupant quelques lignes de ce type a été écrit, la lecture va réserver une
mauvaise surprise :
In [2]: f = open('tennis.txt')
Ey
In [3]: f.readline()
Out[3]: 'Sharapova,2,Russie\n'
Pour enlever les espaces et caractères d’échappement (ici, un retour à la ligne) en début et
fin de ligne, on dispose de la méthode strip, déclinable à droite et à gauche (resp. rstrip et
lstrip) :
Dans l’exemple suivant, on lit un fichier contenant des noms, classements et nationalités
gh
de joueuses de tennis :
f = open('tennis.txt','r')
for L in f:
nom, classement, nation = L.rstrip().split(',')
print(nom, nation, int(classement))
i
f.close()
pyr
Le résultat est :
('Sharapova', 'Russie', 2)
('Williams', 'USA', 1)
...
Co
1. On peut choisir d’écrire à la fin du fichier préexistant, en prenant comme option 'a' (pour « append »)
plutôt que 'w'.
Livre_silo 30 août 2013 16:32 Page 378
es
Informatique pour tous
378
l
rol
Dans l’exemple suivant, on reprend le fichier contenant des données sur des joueuses de
tennis, on récupère la liste des lignes et on ferme le fichier aussitôt après. On ouvre ensuite
un nouveau fichier dans lequel on écrit les mêmes données, mais formatées différemment
(les données sont séparées par des tabulations) :
f = open('tennis.txt','r')
Ey
liste = f.readlines()
f.close()
f = open('tennis-bis.txt','w')
for joueuse in liste: # joueuse est une chaine de caractères
tab = joueuse.strip().split(',') # tab est un tableau
f.write(tab[0]+'\t'+tab[1]+'\t'+tab[2]+'\n')
f.close()
t
Exercice B.2 Si c est une chaîne de caractères, c.upper() renvoie une chaîne où toutes les minuscules
de c ont été remplacées par des majuscules :
gh
In [5]: 'Hop'.upper()
Out[5]: 'HOP'
Écrire une suite d’instructions copiant un fichier en changeant au passage toutes les minuscules en ma-
juscules.
On pourra appliquer ceci au fichier .py contenant ces instructions !
i
pyr
Exercice B.4 On s’intéresse maintenant à l’écriture et à la lecture de matrices d’entiers sous différents
formats. Dans chaque cas, il est demandé d’écrire une fonction écrivant (resp. lisant) une matrice dans un
fichier.
1 On commence par écrire les dimensions de la matrice sur la première ligne (4*12), puis on écrit une
autre ligne contenant toutes les données séparées par des virgules (en commençant par les éléments
de la première ligne par exemple).
2 On écrit les dimensions sur la première ligne, puis chaque ligne de la matrice est écrite dans une ligne
du fichier.
3 On se contente d’écrire les lignes de la matrice sur des lignes différentes du fichier, en séparant les
éléments d’une ligne par des tabulations.
4 On écrit la matrice sur une seule ligne, au format [[a(1,1),...,a(1,n)],...,[a(m,1),...,a(m,n)]].
Comment modifier les différentes fonctions pour autoriser la manipulation de matrices de flottants ?
Exercice B.5 On trouve sans trop de mal le texte original de Hamlet sur Internet.
1 Récupérer et enregistrer la version française de ce texte dans un fichier .txt.
2 À l’aide de Python, compter le nombre d’interventions des différents protagonistes. Faire des statistiques
sur le nombre de mots, de lettres, lesquels sont le plus utilisés, etc.
3 Recommencer avec la version originale.
Livre_silo 30 août 2013 16:32 Page 379
es
B – Compléments sur les entrées/sorties
379
l
rol
B.2 Lecture et écriture dans des images
Le module Image fournit un ensemble de fonctions pour ouvrir un fichier d’image, en ré-
cupérer le contenu sous un format manipulable par un programme Python et enregistrer
le résultat dans un nouveau fichier image. Il ne fait pas partie de la bibliothèque standard,
Ey
mais il est souvent inclus dans les distributions Python.
• Ses dimensions sont données par l’attribut size (sans parenthèses, il ne s’agit pas d’une
pyr
méthode) :
In [8]: mb.size
• L’attribut mode vaut souvent 'RGB' pour les images en couleur et 'L' pour celles en niveaux
de gris.
Co
B.2.2 Traitement
Avant d’effectuer un traitement sur l’image, il faut décomposer cette dernière en pixels.
Pour cela, avec le module numpy, on transforme directement l’image en tableau :
In [9]: mbtab = numpy.array(mb)
Chaque pixel est représenté par un entier entre 0 et 255 pour une image en niveaux de gris,
ou par un tableau de trois entiers entre 0 et 255 pour une image en couleurs. Le TP A.7
donne des informations plus approfondies sur les raisons de cette représentation et sur les
manipulations qu’elle permet.
On se contente ici d’appliquer un traitement simple sur les couleurs :
for lignes in mbtab:
for m in lignes:
m[0],m[1] = m[1],m[0]
Livre_silo 30 août 2013 16:32 Page 380
es
Informatique pour tous
380
l
rol
B.2.3 Écriture dans une image
On dispose ensuite d’une fonction qui permet de créer une image à partir du tableau de
ses pixels :
Ey
In [10]: mbnew = Image.fromarray(mbtab)
Enfin, pour enregistrer une image, on utilise la méthode save. L’extension donnée au nom
du fichier détermine le format d’enregistrement, qui n’est pas forcément le même que celui
de l’image d’origine :
In [11]: mbnew.save('luigi.jpg')
vertical.
Même question en faisant subir cette fois à l’image une rotation de 90°.
pyr
Exercice B.9 Écrire une fonction qui prend pour arguments deux entiers n et p et crée l’image d’un
damier de dimensions n × p : les pixels sont alternativement noirs et blancs le long des lignes comme le
long des colonnes.
Le module turtle sert à réaliser des tracés géométriques à l’aide d’un curseur appelé tor-
tue, comme dans le célèbre langage Logo mis au point en 1967 par Wally Feurzeig et
Seymour Papert pour la première utilisation d’ordinateurs à des fins éducatives.
La tortue est symbolisée par une flèche, dispose d’un crayon et d’une « tête » qui fixe sa
direction. Partout où elle passe, la tortue trace un trait si son crayon est baissé. Elle effectue
les tracés suffisamment lentement pour que le programmeur puisse suivre la suite d’ins-
tructions au fur et à mesure qu’elles s’exécutent. On dispose d’un ensemble d’instructions
élémentaires pour déplacer la tortue ou tourner sa tête :
Livre_silo 30 août 2013 16:32 Page 381
es
B – Compléments sur les entrées/sorties
381
l
rol
Les fonctions du module turtle (tortue Logo)
Ey
forward(distance) Avancer d’une distance donnée
Pour que la fenêtre de tracé ne se bloque pas en fin d’exécution, il faut terminer le pro-
t.down()
t.goto(0,-60)
t.mainloop()
Figure B.1
Un tracé à l’aide du module turtle
tortue.png
Livre_silo 30 août 2013 16:32 Page 382
es
Informatique pour tous
382
l
rol
Exercice B.10 Écrire un programme qui trace une lettre H sans utiliser la fonction up().
Exercice B.11 Écrire une fonction qui trace en pointillés une ligne de longueur a donnée en argument
dans la direction pointée par la tortue.
Adapter la fonction pour que la longueur des pointillés puisse être aussi donnée en argument.
Ey
Exercice B.12 Écrire une fonction qui trace le segment reliant les points de coordonnées (x1 , y1 ) et
(x2 , y2 ) fournies en arguments, sans effectuer d’autre tracé et quelle que soit la position initiale de la
tortue.
Exercice B.13 Écrire une fonction qui trace un carré de côté a donné en argument. On considère qu’au
moment de l’appel la tortue est placée sur un des sommets du carré et regarde dans la direction d’un des
côtés.
Même question pour un triangle de côté donné.
Généraliser cette fonction pour qu’elle prenne en argument le nombre de côtés du polygone à tracer.
t
i gh
pyr
Co
Livre_silo 30 août 2013 16:32 Page 383
l es
rol
Ey
t Références
gh
Système
A. S. T. Systèmes d’exploitation, Pearson, 2008.
i
J.-M. M. Arithmétique des Ordinateurs, Masson, 1989. Épuisé, disponible sur http:
//prunel.ccsd.cnrs.fr/ensl-00086707
Co
Structures de données
R. D. N. Data Structures and Algorithms Using Python, John Wiley & Sons Ltd,
2011.
Algorithmique et complexité
T. C, C. L, R. R. Introduction à l’algorithmique, Dunod, 1994.
M. L. H. Python Algorithms : Mastering Basic Algorithms in the Python Language,
APress, 2010.
D. E. K. e Art of Computer Programming. Volume 1 : Fundamental Algorithms,
third edition, Addison-Wesley, 1997. Volume 2 : Seminumerical Algorithms, third edition,
Livre_silo 30 août 2013 16:32 Page 384
es
Informatique pour tous
384
l
rol
Addison-Wesley, 1997. Volume 3 : Sorting and Searching, second edition, Addison-Wesley,
1998. Volume 4A : Combinatorial Algorithms, Part 1, Addison-Wesley, 2011.
R. S K. W. Algorithms, fourth edition, Addison-Wesley, 2011.
Ey
Ingénierie numérique
G. D, J. M. Mathématiques et informatique, Ellipses, 1988.
N. J. H. Accuracy and stability of numerical algorithms, Siam, 1995.
M. S. Analyse numérique - une approche mathématique, Dunod, 2001.
Pivot de Gauss
t
W. A. Probabilités pour les non probabilistes, H&K, 2013.
gh
S. C, G. J, S. Y. Parallelization of Gauss-Seidel Relaxation for Real Gas Flow,
NAS technical report, juin 2005.
F. C. Valeurs propres de matrices, Masson, 1988.
P. G. C. Introduction à l’analyse matricielle et à l’optimisation, Masson, 1980.
i
pyr
Bases de données
D. M. e eory of Relational Databases, Computer Science Press, 1983. Épuisé, dis-
ponible sur http://web.cecs.pdx.edu/~maier/TheoryBook/TRD.html
Livre_silo 30 août 2013 16:32 Page 385
esl
rol
Ey
t Index
gh
affectation simultanée, 75 bit, 8, 34
agrégation, 285 bitmap, 359
algèbre relationnelle, 266 bloc, 87, 115
algorithme, 84 booléen, 34
i
pyr
Livre_silo 30 août 2013 16:32 Page 386
es
Informatique pour tous
386
l
rol
au pire, 148 EXCEPT,270
dans le meilleur des cas, 149 exponentiation rapide, 104, 134
en espace, 149
en moyenne, 149 factorielle, 104
en temps, 146 Fermat
Ey
optimale, 326 méthode de, 356
compréhension fichier
tableau par, 151, 231 lecture, 375
conditionnement, 191 système de, 16
corps d’une fonction, 115 écriture, 377
correction, 202 file, 300
d’une boucle, 101 firmware, 12
d’une fonction récursive, 135 flocon, 142
t
fonction, 114
débogueur, 30, 103, 118, 137 anonyme, 125, 203, 227
gh
décomposition appel de, 115
LU , 174, 182, 195 arguments d’une, 115
QR, 196 comme argument, 124
de Bruhat, 174, 182, 195 d’agrégation, 286
de Choleski, 196 locale, 124
i
pyr
Livre_silo 30 août 2013 16:32 Page 387
es
Index
387
l
rol
if, 88 lu,
195
elif,90 solve,
186, 188
else, 90 liste, 150, 304
imbriqués, 92
Image, 194, 379 map, 168
Ey
image matplotlib.pyplot, 232
conversion, 360 clf,
234
couleurs, 362 plot, 232
lecture, 379 savefig, 232
traitement, 360 show, 232
écriture, 380 matrice, 160
import, 127 copie, 163
in, 107 création, 162
t
indentation, 88, 97, 115 de Hilbert, 186, 193, 194
inode, 19 de Virginie, 190, 193, 194
gh
input, 72 dimensions, 164
instruction, 9, 85 produit de, 165
boucle conditionnelle, 96 transposition, 164
boucle inconditionnelle, 104 mélange, 314
conditionnelle (test), 88 de Knuth, 168
i
de branchement, 9 mémoire
for, 104 de masse, 12
pyr
if, 88 morte, 12
séquence, 86 virtuelle, 334
while, 96 vive, 7
interpréteur, 24 métadonnée, 19
INTERSECT, 270 méthode, 129
invariant, 194, 202 microcontrôleur, 7
itérable, 106 module, 127
Co
Livre_silo 30 août 2013 16:32 Page 388
es
Informatique pour tous
388
l
rol
linspace, 231 readline, 376
roots, 211 recherche
vectorize, 232 d’un mot dans un texte, 158
n-uplet, 74 dichotomique
dans un tableau, 156
Ey
O(. . .), 146 séquentielle, 155
objet, 129 récurrence
langage orienté, 129 démonstration par, 132, 135
octet, 8, 37 forte, 134, 136
open, 376 récursivité, 130
ordre d’évaluation, 121 registre, 9
regroupement, 287
parenthèses relation, 258
t
langage de, 306 clé, 276
pas relationnel(le)
gh
choix du, 224 algèbre, 266
pas (choix du), 225 calcul, 261
passage modèle, 257
d’arguments, 116 schéma, 258
par valeur, 122 renommage, 266
pendule
i
pyr
Livre_silo 30 août 2013 16:32 Page 389
es
B – Index
389
l
rol
séquence d’instructions, 86 turtle, 380
shell, 15 type, 56
sommet d’une pile, 300 bool, 63
split, 130, 376 float, 60
SQL, 269, 373 int, 57
Ey
str, 79 list, 79
strip, 377 str, 79
structure de données, 299 tuple, 74
swap, 335
système d’exploitation, 13 UNION, 270
système embarqué, 7 unité arithmétique et logique, 9
valeur
table, 258, 363
t
de première classe, 124
tableau, 150, 256
immuable, 163
gh
copie de, 152
itérable, 106
par compréhension, 151
variable
parcours, 153 affectation, 71
terminaison, 202, 207 déclaration, 70
d’une boucle, 98 échanger, 75
d’une fonction récursive, 135 globale, 119
i
terminal, 15 initialisation, 70
pyr
Turing zéro
machine de, 6 comparaison à, 174, 178, 195, 352