(LIFAPSD) CM4 - Fichier, Tri Et Complexité
(LIFAPSD) CM4 - Fichier, Tri Et Complexité
(LIFAPSD) CM4 - Fichier, Tri Et Complexité
Programmation et
Structures de données
Nicolas Pronost
Chapitre 4
Fichier, tri et complexité
2
Tri et complexité
3
Analyse des performances
• De quoi dépend le temps d’exécution d’un programme?
• du nombre d’opérations à réaliser, donc du nombre d’objets à
traiter et de la quantité de manipulations à effectuer
• du langage
• du compilateur
• de la machine (processeur, temps d’accès à la mémoire...)
• Une analyse empirique peut mesurer le temps d’exécution
en testant des données en entrée de différentes tailles et en
les comparant
• Une analyse formelle peut
• comparer la performance de deux algorithmes au niveau du
principe, en ignorant les détails bas niveau comme le langage ou le
processeur
• prédire comment l’algorithme se comportera si on augmente la
taille des données en entrée (sans avoir à l’exécuter, évite les
problèmes d’interprétation des résultats)
4
Analyse formelle de la complexité
• Nous allons calculer la complexité d’un algorithme de
recherche du maximum dans un tableau de réels dans le
pire des cas
• c’est le cas intéressant, celui qui assure que la complexité ne sera
jamais pire que celle obtenue (jamais de mauvaise surprise lors de
l’exécution)
Variables
tab : tableau [1...n] de réels
i : entier
max : réel
Début
max ← tab[1]
pour i allant de 2 à n par pas de 1 faire
si (tab[i] > max) alors
max ← tab[i]
fin si
fin pour
afficher(max)
Fin
5
Analyse formelle de la complexité
• Etape 0: réécrire les pour en tant-que de l’algorithme afin de
faire apparaître les opérations élémentaires
max ← tab[1]
i ← 2
tant que i <= n faire
si (tab[i] > max) alors
max ← tab[i]
fin si
i ← i + 1
fin tant que
6
Analyse formelle de la complexité
• Etape 1: recenser les opérations élémentaires
7
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1
4 si (tab[i] > max) alors
2 5 max ← tab[i]
3 6 fin si
7 i ← i + 1
4 8 fin tant que
5
6
7
8
8
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 5 max ← tab[i]
3 6 fin si
7 i ← i + 1
4 8 fin tant que
5
6
7
8
9
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 6 fin si
7 i ← i + 1
4 8 fin tant que
5
6
7
8
10
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 6 fin si
7 i ← i + 1
4 8 fin tant que
5
6
Combien de fois teste-t-on la
7
condition du tant que?
8 jusqu’à ce qu’elle devienne fausse,
c.-à-d. quand i=n+1 inclus, soit de 2
à n+1 inclus = n comparaisons.
11
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 8 fin tant que
5
6
Combien de fois teste-t-on la
7
condition du tant que?
8 jusqu’à ce qu’elle devienne fausse,
c.-à-d. quand i=n+1 inclus, soit de 2
à n+1 inclus = n comparaisons.
12
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 8 fin tant que
5
6 Si l’on fait x tests de la condition
7 du tant-que, alors on exécute x-1 fois
8 l’intérieur du tant-que
13
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 n-1 n-1 8 fin tant que
5
6 Si l’on fait x tests de la condition
7 du tant-que, alors on exécute x-1 fois
8 l’intérieur du tant-que
14
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 n-1 n-1 8 fin tant que
5
6
Dans le pire des cas, la condition
7
du si est toujours vraie (tableau
8
trié), même nombre d’exécutions
du corps du si que le test de sa
condition
15
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 n-1 n-1 8 fin tant que
5 n-1 n-1
6
Dans le pire des cas, la condition
7
du si est toujours vraie (tableau
8
trié), même nombre d’exécutions
du corps du si que le test de sa
condition
16
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 n-1 n-1 8 fin tant que
5 n-1 n-1
6
On ne fait aucune d’opération
7
élémentaire sur les sorties de
8
boucle/fonction
17
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 n-1 n-1 8 fin tant que
5 n-1 n-1
6
7 n-1 n-1
8
18
Analyse formelle de la complexité
• Etape 2: compter le nombre de fois où chaque opération est
effectuée, pour chaque ligne
Accès Aff. Aff. Comp. Comp. Add. 1 max ← tab[1]
Ligne
tableau entier réel entier réel entier 2 i ← 2
3 tant que i <= n faire
1 1 1
4 si (tab[i] > max) alors
2 1 5 max ← tab[i]
3 n 6 fin si
7 i ← i + 1
4 n-1 n-1 8 fin tant que
5 n-1 n-1
6
On ne fait aucune d’opération
7 n-1 n-1
élémentaire sur les sorties de
8
boucle/fonction
19
Analyse formelle de la complexité
• Etape 3: faire les totaux par opération élémentaire
Ligne Accès tableau Aff. entier Aff. réel Comp. entier Comp. réel Add. entier
1 1 1
2 1
3 n
4 n-1 n-1
5 n-1 n-1
7 n-1 n-1
20
Analyse formelle de la complexité
• Etape 4: appliquer comme constantes multiplicatives les
durées (ou autre mesure comme l’empreinte mémoire) des
opérations élémentaires
• Notons 𝜏𝑎𝑐𝑐 le temps en microsecondes pour effectuer un accès
dans un tableau, 𝜏𝑎𝑓𝑓𝑒 le temps pour effectuer une affectation
d’entier, etc.
Ligne Accès tableau Aff. entier Aff. réel Comp. entier Camp. réel Add. entier
Cstes 𝜏𝑎𝑐𝑐 𝜏𝑎𝑓𝑓𝑒 𝜏𝑎𝑓𝑓𝑟 𝜏𝑐𝑚𝑝𝑒 𝜏𝑐𝑚𝑝𝑟 𝜏𝑎𝑑𝑑𝑒
Total # 2n-1 n n n n-1 n-1
Temps 𝜏𝑎𝑐𝑐 2𝑛 − 1 + 𝜏𝑎𝑓𝑓𝑒 𝑛 + 𝜏𝑎𝑓𝑓𝑟 𝑛 + 𝜏𝑐𝑚𝑝𝑒 𝑛 + 𝜏𝑐𝑚𝑝𝑟 𝑛 − 1 + 𝜏𝑎𝑑𝑑𝑒 𝑛 − 1
21
Analyse formelle de la complexité
• Etape 5: réorganiser les termes en fonction du degré de n
𝑇 𝑛 = 𝜏𝑎𝑐𝑐 2𝑛 − 1 + 𝜏𝑎𝑓𝑓𝑒 𝑛 + 𝜏𝑎𝑓𝑓𝑟 𝑛 + 𝜏𝑐𝑚𝑝𝑒 𝑛 + 𝜏𝑐𝑚𝑝𝑟 𝑛 − 1 + 𝜏𝑎𝑑𝑑𝑒 𝑛 − 1
𝑇 𝑛 = 𝑛 2𝜏𝑎𝑐𝑐 + 𝜏𝑎𝑓𝑓𝑒 + 𝜏𝑎𝑓𝑓𝑟 + 𝜏𝑐𝑚𝑝𝑒 + 𝜏𝑐𝑚𝑝𝑟 + 𝜏𝑎𝑑𝑑𝑒 + −𝜏𝑎𝑐𝑐 − 𝜏𝑐𝑚𝑝𝑟 − 𝜏𝑎𝑑𝑑𝑒
22
Analyse formelle de la complexité
• L’analyse formelle regarde ce qui se passe lorsque 𝑛 devient
très grand: comportement asymptotique
• Notation 𝑂 (grand-o, aussi dit de Landau)
• Etant donné deux fonctions 𝑓(𝑛) et 𝑔(𝑛), on dit que 𝑓(𝑛) est
𝑂(𝑔 𝑛 ) s’il existe un constante positive 𝑐 et un entier constant
𝑛0 ≥ 1 tels que
𝑓(𝑛) ≤ 𝑐 × 𝑔(𝑛) pour tout 𝑛 ≥ 𝑛0
• Cela signifie que le taux de croissance de 𝑓 n’est pas aussi grand
que celui de 𝑔
• Le taux de croissance n’est pas affecté par
• les facteurs constants et les termes d’ordres inférieurs
• donc supprimer les de 𝑔(𝑛)
• Plus en LIFAP6
23
Les algorithmes de tri
• En algorithmique il est souvent utile de pouvoir trier un
ensemble d’objets pour faire, en autres, des recherches
rapides d’éléments
• nécessite de maintenir à jour l’ensemble trié à chaque ajout ou de
trier les objets régulièrement
• nécessite de donner une relation d’ordre entre les objets
• implémentée en général par l’opérateur < (strict. inférieur)
• les objets sont souvent organisés dans un tableau, le tri consiste
alors à déplacer les (pointeurs sur) objets d’un élément à un autre
• Les algorithmes de tri ont des propriétés différentes (ex.
complexité)
• Choisir le bon tri pour la bonne utilisation
• Tri par sélection, tri par insertion, tri rapide, tri fusion, tri bulle, etc.
24
Tri par sélection
• Principe: pour chaque élément i de 1 à n-1,
échanger tab[i] avec l’élément minimum de
tab[i...n]
• rechercher les minimums d’un sous-tableau de plus
en plus petit
• les éléments à gauche de i sont à leur place définitive
(et ne seront plus modifiés), donc le tableau est
complètement trié lorsque i arrive sur l’avant-dernier
élément (le dernier élément étant forcément le plus
grand)
25
Tri par sélection
Procédure tri_sélection (tab: tableau [1...n] de Element)
Précondition: les éléments de tab sont initialisés
Post-condition: les éléments de tab sont triés (plus petits à gauche)
Paramètre en mode donnée-résultat: tab
Variables locales:
i,j,indmin : entiers
min : Element
Début
pour i allant de 1 à n-1 par pas de 1 faire
indmin ← i
pour j allant de i+1 à n par pas de 1 faire
si tab[j] < tab[indmin] alors
indmin ← j
finsi
finpour
min ← tab[indmin]
tab[indmin] ← tab[i]
tab[i] ← min
finpour
Fin tri_sélection
26
Tri par sélection
Procédure tri_sélection (tab: tableau [1...n] de Element)
Précondition: les éléments de tab sont initialisés
Post-condition: les éléments de tab sont triés (plus petits à gauche)
Paramètre en mode donnée-résultat: tab
Variables locales:
i,j,indmin : entiers
min : Element
Début
pour i allant de 1 à n-1 par pas de 1 faire
indmin ← i
pour j allant de i+1 à n par pas de 1 faire
si tab[j] < tab[indmin] alors
indmin ← j
finsi
finpour
min ← tab[indmin]
tab[indmin] ← tab[i]
tab[i] ← min
finpour
Fin tri_sélection
Element désigne le type d’objet à trier, ex. réel, entier, Date, ...
27
Tri par sélection
Procédure tri_sélection (tab: tableau [1...n] de Element)
Précondition: les éléments de tab sont initialisés
Post-condition: les éléments de tab sont triés (plus petits à gauche)
Paramètre en mode donnée-résultat: tab
Variables locales:
i,j,indmin : entiers
min : Element
Début
pour i allant de 1 à n-1 par pas de 1 faire
indmin ← i
pour j allant de i+1 à n par pas de 1 faire
si tab[j] < tab[indmin] alors
indmin ← j
finsi
finpour
min ← tab[indmin]
tab[indmin] ← tab[i]
tab[i] ← min
finpour
Fin tri_sélection
28
Tri par sélection
Procédure tri_sélection (tab: tableau [1...n] de Element)
Précondition: les éléments de tab sont initialisés
Post-condition: les éléments de tab sont triés (plus petits à gauche)
Paramètre en mode donnée-résultat: tab
Variables locales:
i,j,indmin : entiers
min : Element
Début
pour i allant de 1 à n-1 par pas de 1 faire
indmin ← i
pour j allant de i+1 à n par pas de 1 faire
si tab[j] < tab[indmin] alors
indmin ← j
finsi
finpour
min ← tab[indmin]
tab[indmin] ← tab[i]
tab[i] ← min
finpour
Fin tri_sélection
29
Tri par sélection
Procédure tri_sélection (tab: tableau [1...n] de Element)
Précondition: les éléments de tab sont initialisés
Post-condition: les éléments de tab sont triés (plus petits à gauche)
Paramètre en mode donnée-résultat: tab
Variables locales:
i,j,indmin : entiers
min : Element
Début
pour i allant de 1 à n-1 par pas de 1 faire
indmin ← i
pour j allant de i+1 à n par pas de 1 faire
si tab[j] < tab[indmin] alors
indmin ← j
finsi
finpour
min ← tab[indmin]
tab[indmin] ← tab[i]
tab[i] ← min
finpour
Fin tri_sélection
30
Validité d’un algorithme
• L’invariant de boucle est la propriété des données vérifiant
les conditions suivantes
• Initialisation: la propriété est vraie avant la 1ère itération
• Conservation: si la propriété est vraie à l’itération i, alors elle reste
vraie à l’itération i+1
• Terminaison: une fois la boucle terminée, la propriété est utile pour
montrer la validité de l’algorithme
31
Tri par sélection: invariant de boucle
• L’invariant de boucle est
Si i >= 2, tab est trié entre les indices 1 et i-1, et tous les
éléments restants sont supérieurs ou égaux à tab[i-1]
• Initialisation
• Pour i=2 (on vient de faire l’affectation i←2 mais on n’a pas exécuté
le corps de la boucle pour i=2), a-t-on la propriété suivante: « tab
est trié entre les indices 1 et 1, et tous les éléments restants sont
supérieurs ou égaux à tab[1] »?
• Entre les indices 1 et 1, il n’y a qu’un seul élément donc ce sous-
tableau est forcément trié. Par ailleurs, l’élément qui se trouve en
position 1 est le min de tout le tableau, donc les éléments restants
lui sont donc bien supérieurs ou égaux
32
Tri par sélection: invariant de boucle
• L’invariant de boucle est
Si i >= 2, tab est trié entre les indices 1 et i-1, et tous les
éléments restants sont supérieurs ou égaux à tab[i-1]
• Conservation
• Supposons que la propriété soit vraie à l’itération i, soit « tab est
trié entre les indices 1 et i-1, et tous les éléments restants sont
supérieurs ou égaux à tab[i-1] »
• On doit montrer que la propriété reste vraie pour i+1, soit « tab est
trié entre les indices 1 et i, et tous les éléments restants sont
supérieurs ou égaux à tab[i] »
• Or, lors de l’itération i, on prend un élément dans la partie non triée
pour le mettre à la place i, et on ne touche pas aux éléments
tab[1...i-1]. Cet élément est supérieur ou égal à tab[i-1] donc le
début du tableau restera bien trié, jusqu’à i inclus maintenant
• Comme par ailleurs, cet élément est le minimum de la partie non
triée, les éléments restants sont bien supérieurs ou égaux à tab[i]
33
Tri par sélection: invariant de boucle
• L’invariant de boucle est
Si i >= 2, tab est trié entre les indices 1 et i-1, et tous les
éléments restants sont supérieurs ou égaux à tab[i-1]
• Terminaison
• Pour i=n (dernière valeur prise par i, qui va causer la sortie de la
boucle), l’invariant de boucle s’écrit « tab est trié entre les indices 1
et n-1, et tous les éléments restants sont supérieurs ou égaux à
tab[n-1] »
• Donc on a un tableau trié jusqu’à n-1, suivi d’une valeur supérieure
ou égale à toutes les autres. Le tableau est donc bien trié de 1 à n,
ce qui prouve que l’algorithme de tri est correct.
34
Quelques complexités de tris
Nom Cas optimal Cas moyen Pire des cas Complexité spatiale
35
Quelques complexités de tris
Nom Cas optimal Cas moyen Pire des cas Complexité spatiale
En effet, même si le tableau est déjà trié, on doit faire une double
boucle imbriquée pour placer tous les i.
36
Quelques complexités de tris
Nom Cas optimal Cas moyen Pire des cas Complexité spatiale
37
Importance de la complexité
• Exemple: tri d’un tableau de 𝑛 = 106 nombres par ordre
croissant
Ordinateur rapide: 1 Ordinateur 100 fois
milliard d’instructions plus lent: 10 millions
par seconde d’instructions par
seconde
Très bon programmeur, langage bas Programmeur débutant, langage haut niveau,
niveau, etc. : 𝜏𝑖𝑛𝑠 = 2 𝑚𝑠 compilateur non optimisé : 𝜏𝑖𝑛𝑠 = 50 𝑚𝑠
38
Tri par insertion
• Principe: les éléments sont insérés les uns après les autres
dans un tableau que l’on maintient trié
• Insérer le premier élément du sous-tableau restant à trier dans la
partie déjà triée du tableau
• Itérer la procédure jusqu’au tri complet du tableau
39
Tri externe
• Parfois, le nombre d’objets à trier est tel qu’on ne peut pas
les stocker tous en mémoire
• Exemples
• articles d’un gros catalogue de vente par correspondance
• données d’un recensement national
• Il faut des algorithmes de tri externe, c’est-à-dire capables
de travailler en ne stockant en mémoire que des petites
parties du tableau
40
Tri externe: caractéristiques
• Pendant le tri, seule une partie des données se trouve en
mémoire, le reste est stocké sur disque, bande magnétique,
en ligne, etc.
• Accéder aux données est donc beaucoup plus coûteux que
de les comparer ou de faire des opérations arithmétiques
dessus
• Il peut y avoir des restrictions fortes sur l’accès
• ex. si accès physique, accès uniquement séquentiel
41
Tri externe
• Critères pour les algorithmes
• Minimiser le nombre de fois où l’on accède à un élément
• Accéder séquentiellement aux éléments
• Les deux algorithmes de tri externe les plus courants sont
• le tri fusion (merge sort)
• le tri par paquets (bucket sort)
42
Notion de monotonie
• Une monotonie est une séquence d’éléments triés
• Toute séquence de taille 1 est trivialement une monotonie
• Exemple
56 12 16 23 28 6 2 1 57 59 45
11 monotonies de longueur 1
43
Notion de monotonie
• Une monotonie est une séquence d’éléments triés
• Toute séquence de taille 1 est trivialement une monotonie
• Exemple
56 12 16 23 28 6 2 1 57 59 45
5 monotonies de longueur 2
44
Notion de monotonie
• Une monotonie est une séquence d’éléments triés
• Toute séquence de taille 1 est trivialement une monotonie
• Exemple
56 12 16 23 28 6 2 1 57 59 45
3 monotonies de longueur 3
45
Notion de monotonie
• Une monotonie est une séquence d’éléments triés
• Toute séquence de taille 1 est trivialement une monotonie
• Exemple
56 12 16 23 28 6 2 1 57 59 45
1 monotonie de longueur 4
46
Fusion de deux monotonies
• Fusionner deux monotonies consiste à construire une
nouvelle monotonie comprenant tous les éléments des
deux monotonies initiales
• on prend le plus petit des 2 éléments accessibles (ceux en tête de
chaque monotonie) et on le recopie dans la nouvelle monotonie
• on passe à l’élément suivant sur la monotonie dont on a enlevé
l’élément de tête
• on recommence ce processus jusqu’à épuisement d’une monotonie
• on recopie le reste de la monotonie non encore copié
• A chaque instant, on n’a besoin en mémoire que des deux
éléments à comparer
• les monotonies sont stockées dans un fichier
• On accède bien séquentiellement aux éléments d’une
monotonie donnée
47
Principe du tri fusion
• Approche top-down (utilisée en tri interne : tout le tableau
en mémoire)
• L’algorithme sera vu en TD
• Approche bottom-up (utilisée en tri externe : partie du
tableau en mémoire)
1. Créer n monotonies de longueur 1
2. Fusionner les paires d’éléments successifs pour obtenir une
séquence de n/2 monotonies de longueur 2
3. Fusionner les paires de monotonies successives pour obtenir une
séquence de n/4 monotonies de longueur 4
4. Continuer jusqu’à obtenir une seule monotonie de longueur n
48
Tri fusion externe sur fichier
• En utilisant 3 fichiers distincts : A, B et X
• Phase d’éclatement: on répartit les monotonies contenues
dans le fichier X dans deux fichiers A et B
• 1 sur 2 va dans le fichier A, 1 sur 2 va dans le fichier B
• Phase de fusion:
• on fusionne la 1ère monotonie du fichier A avec la 1ère monotonie du
fichier B, en écrivant la monotonie résultante dans le fichier X (on
écrase l’ancien contenu du fichier X)
• de même avec les 2ème monotonies des fichiers A et B
• et ainsi de suite jusqu’à la fin des fichiers A et B
• On recommence les phases d’éclatement et de fusion
jusqu’à ce que l’on obtienne une seule monotonie
49
Tri fusion externe : exemple
éclatement (𝑙 = 1) 56 12 16 23 28 6 2 1 57 59 45
fusion
éclatement (𝑙 = 2) 12 56 16 23 6 28 1 2 57 59 45
fusion
éclatement (𝑙 = 4) 12 16 23 56 1 2 6 28 45 57 59
fusion
éclatement (𝑙 = 8) 1 2 6 12 16 23 28 56 45 57 59
fusion
1 2 6 12 16 23 28 45 56 57 59
50
Fichiers
51
Différents types de fichiers
Etc. Etc.
52
Fichier « texte »
• Les octets du fichier ne contiennent que des codes de
caractères
• Les données numériques doivent être converties en
caractères
• Exemple: un nombre au format IEEE 754 simple précision
0 1 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
signe exposant décalé pseudo-mantisse
53
Fichier « texte »
• Les octets du fichier ne contiennent que des codes de
caractères
• Les données numériques doivent être converties en
caractères
• Exemple: un nombre au format IEEE 754 simple précision
0 1 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
signe exposant décalé pseudo-mantisse
54
Fichier « texte »
• Avantages
• portabilité
• fichier lisible (et facilement modifiable) avec un simple éditeur de
texte
• Inconvénients
• coût en temps induit par les conversions nécessaires
• penser à écrire suffisamment de décimales pour ne pas perdre en
précision numérique
55
Fichier « binaire »
• On recopie l’information comme elle figure en mémoire
• Exemple
0 1 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
signe exposant décalé pseudo-mantisse
• Ecriture dans le fichier (regroupement en octet juste pour lisibilité)
01000010 10101011 00000000 00000000
• Avantages
• fichier plus compact (n’utilise pas de codage intermédiaire)
• lecture/écriture sans conversion = manipulation plus rapide
• pas de formatage de présentation = pas de perte de précision
• Inconvénients
• fichier illisible (et difficilement éditable) par un éditeur de texte
• il faut gérer les problèmes de portabilité
• il faut savoir qu’est ce qui est écrit pour pouvoir le relire
56
Utilisation depuis un programme
• Doit-on se soucier du support physique des fichiers?
• Exemple en C++ sous Linux:
Bibliothèque iostream (C++):
open, close, <<, >>, ...
Buffer
57
Notion de buffer
• Le buffer est une structure de donnée de type FIFO
• First in, first out
• File (cf. cours sur les files plus tard)
• Espace de stockage temporaire pour limiter le nombre
d’appels système qui sont lents (l’accès physique au disque
dur est l’une des opérations les plus lente sur un ordinateur)
• Un curseur indique où on en est dans la lecture/écriture
58
Les fichiers en C++
• C++ fournit trois classes pour manipuler des fichiers
• ofstream est la classe pour écrire dans un fichier (o pour out)
• ifstream est la classe pour lire depuis un fichier (i pour in)
• fstream est la classe pour lire et écrire
• Ces classes fonctionnent exactement comme les classes
ostream et istream déjà utilisées pour afficher à l’écran et
saisir au clavier
• On utilise les mêmes opérateurs >> et <<
• Ces opérateurs sont surchargés dans les classes fstream
• On a juste besoin de spécifier le fichier physique dans lequel
lire/écrire
59
Ouvrir un fichier
• Pour associer un fichier physique à un flux dans lequel on lit
ou écrit, il faut l’ouvrir avec la procédure membre open
void open (const char *nom_fichier, openmode mode);
60
Ouvrir un fichier
• Les modes d’ouverture peuvent être combinés en utilisant
l’opérateur OU (|)
• Chaque procédure membre open des classes fstream,
ofstream et ifstream a un mode d’ouverture par défaut qu’il
est inutile de préciser si seul le nom du fichier est donné en
paramètre
ofstream ios::out
ifstream ios::in
61
Ouvrir un fichier
• Exemples
• Pour ouvrir un fichier en lecture
ifstream monFichier;
monFichier.open("fichier.txt");
62
Ouvrir un fichier
• Comme on souhaite très souvent créer un flux et ouvrir un
ficher en même temps, les trois classes ont un constructeur
qui prend en paramètre les mêmes paramètres que la
procédure membre open
• Les exemples précédents peuvent donc aussi s’écrire :
ifstream monFichier ("fichier.txt");
63
Ouvrir un fichier
• L’opération d’ouverture n’est pas forcément réussie
• Ex. un fichier en lecture qui n’existe pas
• Ex. un fichier en lecture ou écriture dont vous n’avez pas les droits
• Ex. un fichier en écriture alors qu’il n’y a plus de place sur le disque
• La fonction membre is_open retourne une valeur
booléenne indiquant si l’ouverture a été un succès
• Il faut absolument, toujours, vérifier si l’ouverture a été un
succès
• manipuler un flux non ouvert produit un crash à l’exécution
ofstream monFichier ("fichier.txt");
if (monFichier.is_open()) { /* OK, on peut travailler sur le flux */ }
else { /* Attention, erreur dans l’ouverture */ }
64
Fermeture d’un fichier
• Quand les opérations d’écriture et de lecture sont finies, il ne faut
pas oublier de fermer le fichier
monFichier.close();
65
Ecriture dans un fichier
• Les écritures dans le flux fichier se font de la même manière
que l’affichage sur l’écran: avec l’opérateur <<
ofstream monFichier ("fichier.txt");
Date maDate(1,2,2000);
if (monFichier.is_open()) {
monFichier << "La somme de " << 10 << " et " << 5 << " vaut " << 10+5;
monFichier << endl << " en date du " << maDate;
monFichier.close();
}
else {
cout << "Erreur d’ouverture du fichier: " << "fichier.txt" ;
}
66
Lecture depuis un fichier
• Les lectures dans le flux fichier peuvent se faire de la même
manière que la saisie au clavier: avec l’opérateur >>
• Mais il extrait les données une à une en prenant en compte
les caractères de séparation (espace, tabulation, fin de ligne
etc.)
• il permet de faire une extraction mot à mot
• De plus il ne fait pas la distinction entre caractères
provenant d’un mot et nombres convertis en caractères lors
de l’écriture
• Tout est caractère pour cet opérateur
67
Lecture depuis un fichier
• Donc ici il faudrait le code suivant
ifstream monFichier ("fichier.txt");
string mot;
Date maDate;
int x,y,z;
if (monFichier.is_open()) {
monFichier >> mot >> mot >> mot ; // La somme de (3 mots)
monFichier >> x >> mot >> y >> mot >> z; // x et y vaut z (2 mots)
monFichier >> mot >> mot >> mot >> maDate; // en date du maDate (3 mots)
monFichier.close();
}
else {
cout << "Erreur d’ouverture du fichier: " << "fichier.txt" ;
}
68
Lecture depuis un fichier
• Pour lire une ligne entière dans le fichier, on peut utiliser la
fonction getline
• depuis le curseur courant jusqu’au prochain caractère de fin de
ligne
• La chaîne de caractères résultant peut ensuite être traitée
pour récupérer des données, rechercher des sous chaînes…
• toutes les fonctionnalités de la classe string sont alors disponibles
ifstream monFichier ("fichier.txt");
string ligne;
if (monFichier.is_open()) {
getline(monFichier,ligne);
// traitement de la première ligne (la somme de x et y vaut z)
getline(monFichier,ligne);
// traitement de la deuxième ligne ( en date du maDate)
monFichier.close();
}
else {
cout << "Erreur d’ouverture du fichier: " << "fichier.txt" ;
}
69
Contrôle de l’état du flux
• Si vous ne connaissez pas à l’avance le nombre de
caractères/lignes du fichier, vous pouvez tester si le curseur
est en fin de fichier avec la fonction membre eof
ifstream monFichier ("fichier.txt");
if (monFichier.is_open()) {
while (!monFichier.eof()) {
// lecture contenu:
// ex. monFichier >> mot; ou bien getline(monFichier,ligne);
}
// traitement de fin de fichier
monFichier.close();
}
else {
cout << "Erreur d’ouverture du fichier: " << "fichier.txt" ;
}
70
Contrôle dans le flux
• En interne, le fichier est stocké dans un buffer (pour
accélérer les accès)
• On a la possibilité (rarement utile) de manipuler le curseur
indiquant où on en est dans la lecture/écriture du buffer
• On peut donc « sauter/relire/réécrire » des données déjà
lues/écrites
• Les fonctions membres tellg et tellp permettent de
récupérer les positions des curseurs de lecture et d’écriture
• Les fonctions membres seekg et seekp permettent de
modifier la position de ces curseurs
71
Contrôle dans le flux
• Exemple pour calculer la taille d’un fichier
streampos debut,fin;
//streampos est le type des positions des curseurs
debut = monFichier.tellg();
// position du curseur après ouverture (début)
monFichier.seekg(0, ios::end);
// se déplace à la fin (décalage de 0 par la rapport à la fin)
fin = monFichier.tellg();
// position du curseur (fin)
monFichier.close();
// fermeture du fichier
cout << "la taille du fichier est: " << (fin-debut) << " octets";
// on peut effectuer des opérations sur des streampos (equiv. int)
72
Mode binaire
• En mode binaire, on n’a pas besoin de formater les données
du fichier (pas de caractères, pas de retour à la ligne, ...)
• On ne doit pas utiliser les opérateurs >> et << mais les
fonctions membres d’un flux write et read
• pour écrire les n octets à partir de l’adresse s dans le flux
ofstream& write (const char* s, streamsize n)
73
Mode binaire
• Exemple pour recopier un fichier binaire
74
Mode binaire
• Exemple pour écrire puis lire les nombres de 0 à 100
ofstream monFichier ("fichier.bin", ios::binary);
for (int i=0;i<=100;i++)
monFichier.write((const char*)(&i), sizeof(i));
}
monFichier.close();
75