Remplacement du tri à bulles par un tri par tas pour améliorer les performances de tri

This commit is contained in:
rabot
2026-10-07 12:20:50 +02:00
parent 1c4405c75e
commit 57c9ce933b
6 changed files with 237 additions and 9 deletions
+1
View File
@@ -0,0 +1 @@
// Piste : réduire le temps d'exécution de `doit_echanger`
+25 -8
View File
@@ -319,16 +319,33 @@ int doit_echanger(const Entree *a, const Entree *b)
return comparer(a->mot, b->mot) > 0;
}
/* La racine du tas est l'entrée qui doit apparaître le plus tard. */
void retablir_tas(Entree *table, int n, int racine)
{
while (racine < n / 2) {
int enfant = 2 * racine + 1;
if (enfant + 1 < n && doit_echanger(&table[enfant + 1], &table[enfant]))
enfant++;
if (!doit_echanger(&table[enfant], &table[racine]))
break;
Entree tmp = table[racine];
table[racine] = table[enfant];
table[enfant] = tmp;
racine = enfant;
}
}
void trier_entrees(Entree *table, int n)
{
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (doit_echanger(&table[j], &table[j + 1])) {
Entree tmp = table[j];
table[j] = table[j + 1];
table[j + 1] = tmp;
}
}
/* Construire le tas, puis placer chaque racine à sa position finale. */
for (int i = n / 2 - 1; i >= 0; i--)
retablir_tas(table, n, i);
for (int fin = n - 1; fin > 0; fin--) {
Entree tmp = table[0];
table[0] = table[fin];
table[fin] = tmp;
retablir_tas(table, fin, 0);
}
}
+51 -1
View File
@@ -365,4 +365,54 @@ Empreinte du rapport : 1d53de903efd2b7c
real 0m0.751s
user 0m0.703s
sys 0m0.004s
```
```
## Remplacement du tri à bulles par un tri par tas
Le tri à bulles effectuait systématiquement n(n - 1)/2 comparaisons,
soit 17 202 045 appels à `doit_echanger` pour 5 866 mots distincts.
`trier_entrees` utilise maintenant un tri par tas. La racine du tas contient
l'entrée qui doit apparaître le plus tard dans le classement. Elle est
échangée avec la dernière entrée de la partie non triée, puis `retablir_tas`
restaure cette propriété sur les entrées restantes. La construction du tas
coûte O(n), puis les extractions coûtent O(log n) chacune : le tri complet
passe de O(n²) à O(n log n), y compris dans le pire cas.
Le tri travaille directement dans le tableau, avec O(1) de mémoire
supplémentaire. `doit_echanger` reste inchangée : fréquence décroissante,
puis ordre alphabétique croissant. Chaque échange déplace l'entrée entière,
y compris la longueur mémorisée.
Profil enregistré dans [out/profil_apres_tri.txt](out/profil_apres_tri.txt).
La comparaison porte sur la version précédente, décrite dans
`out/profil_apres_longueurs.txt`.
| Mesure | Tri à bulles | Tri par tas |
| --- | ---: | ---: |
| Appels à `doit_echanger` pendant le tri | 17 202 045 | 129 602 |
| Appels à `comparer` pour départager les fréquences égales | 12 172 087 | 32 813 |
| Temps réel médian du programme sans profiling (`gcc -O0`, 5 exécutions par version) | 0,367 s | 0,113 s |
Le tri effectue environ 133 fois moins de comparaisons d'entrées.
Le temps total baisse de 69,3 %, soit une accélération d'environ 3,25.
Les cinq mesures, réalisées en alternant les deux versions sur la même
machine, sont respectivement : 0,365 / 0,363 / 0,372 / 0,383 / 0,367 s
avant, et 0,112 / 0,113 / 0,115 / 0,113 / 0,112 s après.
Validation : compilation sans avertissement avec `-Wall -Wextra -Wpedantic`,
rapport et sortie console identiques octet par octet sur les dix exécutions,
avec `-pg` et sous AddressSanitizer et UndefinedBehaviorSanitizer.
L'empreinte reste **`1d53de903efd2b7c`**.
Des tests temporaires ont comparé le résultat à `qsort` sur des tableaux
de 0 à 20 000 entrées, aléatoires, déjà triés ou inversés. Ils vérifient
aussi les fréquences égales, les doublons, les préfixes, les valeurs
extrêmes de fréquence et la conservation des longueurs des mots.
Commandes pour reproduire ce profil :
```bash
mkdir -p out/apres_tri
gcc -O0 -pg analyseur.c -o out/apres_tri/analyseur
(cd out/apres_tri && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_tri.txt)
```
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+160
View File
@@ -0,0 +1,160 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
45.45 0.05 0.05 12198772 0.00 0.00 egales
18.18 0.07 0.02 1 20.00 27.09 analyser_bigrammes
9.09 0.08 0.01 5297256 0.00 0.00 est_lettre
9.09 0.09 0.01 1266663 0.00 0.00 en_minuscule
9.09 0.10 0.01 250000 0.00 0.00 est_mot_vide
9.09 0.11 0.01 1 10.00 10.00 decouper_lignes
0.00 0.11 0.00 928117 0.00 0.00 alea
0.00 0.11 0.00 150182 0.00 0.00 chercher_mot
0.00 0.11 0.00 150182 0.00 0.00 compter_mot
0.00 0.11 0.00 150182 0.00 0.00 hacher_mot
0.00 0.11 0.00 129602 0.00 0.00 doit_echanger
0.00 0.11 0.00 32813 0.00 0.00 comparer
0.00 0.11 0.00 26801 0.00 0.00 ajouter
0.00 0.11 0.00 21746 0.00 0.00 compter_mots_ligne
0.00 0.11 0.00 21746 0.00 0.00 normaliser_ligne
0.00 0.11 0.00 8798 0.00 0.00 retablir_tas
0.00 0.11 0.00 5866 0.00 0.00 copier
0.00 0.11 0.00 1 0.00 0.00 construire_rapport
0.00 0.11 0.00 1 0.00 0.00 ecart_type_longueur
0.00 0.11 0.00 1 0.00 0.00 empreinte
0.00 0.11 0.00 1 0.00 0.00 generer_texte
0.00 0.11 0.00 1 0.00 0.00 generer_vocabulaire
0.00 0.11 0.00 1 0.00 0.00 longueur_moyenne
0.00 0.11 0.00 1 0.00 0.00 racine
0.00 0.11 0.00 1 0.00 0.00 trier_entrees
Call graph
granularity: each sample hit covers 4 byte(s) for 9.09% of 0.11 seconds
index % time self children called name
<spontaneous>
[1] 100.0 0.00 0.11 main [1]
0.00 0.06 21746/21746 compter_mots_ligne [2]
0.02 0.01 1/1 analyser_bigrammes [5]
0.00 0.01 21746/21746 normaliser_ligne [6]
0.01 0.00 1/1 decouper_lignes [9]
0.00 0.00 1/1 generer_vocabulaire [23]
0.00 0.00 1/1 generer_texte [22]
0.00 0.00 1/1 trier_entrees [26]
0.00 0.00 1/1 longueur_moyenne [24]
0.00 0.00 1/1 ecart_type_longueur [20]
0.00 0.00 1/1 racine [25]
0.00 0.00 1/1 construire_rapport [19]
0.00 0.00 1/1 empreinte [21]
-----------------------------------------------
0.00 0.06 21746/21746 main [1]
[2] 54.5 0.00 0.06 21746 compter_mots_ligne [2]
0.01 0.05 250000/250000 est_mot_vide [3]
0.00 0.00 150182/150182 compter_mot [11]
-----------------------------------------------
0.01 0.05 250000/250000 compter_mots_ligne [2]
[3] 54.0 0.01 0.05 250000 est_mot_vide [3]
0.05 0.00 12045671/12198772 egales [4]
-----------------------------------------------
0.00 0.00 153101/12198772 chercher_mot [10]
0.05 0.00 12045671/12198772 est_mot_vide [3]
[4] 45.5 0.05 0.00 12198772 egales [4]
-----------------------------------------------
0.02 0.01 1/1 main [1]
[5] 24.6 0.02 0.01 1 analyser_bigrammes [5]
0.01 0.00 3757672/5297256 est_lettre [7]
-----------------------------------------------
0.00 0.01 21746/21746 main [1]
[6] 11.7 0.00 0.01 21746 normaliser_ligne [6]
0.01 0.00 1266663/1266663 en_minuscule [8]
0.00 0.00 1539584/5297256 est_lettre [7]
-----------------------------------------------
0.00 0.00 1539584/5297256 normaliser_ligne [6]
0.01 0.00 3757672/5297256 analyser_bigrammes [5]
[7] 9.1 0.01 0.00 5297256 est_lettre [7]
-----------------------------------------------
0.01 0.00 1266663/1266663 normaliser_ligne [6]
[8] 9.1 0.01 0.00 1266663 en_minuscule [8]
-----------------------------------------------
0.01 0.00 1/1 main [1]
[9] 9.1 0.01 0.00 1 decouper_lignes [9]
-----------------------------------------------
0.00 0.00 150182/150182 compter_mot [11]
[10] 0.6 0.00 0.00 150182 chercher_mot [10]
0.00 0.00 153101/12198772 egales [4]
0.00 0.00 150182/150182 hacher_mot [13]
-----------------------------------------------
0.00 0.00 150182/150182 compter_mots_ligne [2]
[11] 0.6 0.00 0.00 150182 compter_mot [11]
0.00 0.00 150182/150182 chercher_mot [10]
0.00 0.00 5866/5866 copier [18]
-----------------------------------------------
0.00 0.00 27934/928117 generer_vocabulaire [23]
0.00 0.00 900183/928117 generer_texte [22]
[12] 0.0 0.00 0.00 928117 alea [12]
-----------------------------------------------
0.00 0.00 150182/150182 chercher_mot [10]
[13] 0.0 0.00 0.00 150182 hacher_mot [13]
-----------------------------------------------
0.00 0.00 129602/129602 retablir_tas [17]
[14] 0.0 0.00 0.00 129602 doit_echanger [14]
0.00 0.00 32813/32813 comparer [15]
-----------------------------------------------
0.00 0.00 32813/32813 doit_echanger [14]
[15] 0.0 0.00 0.00 32813 comparer [15]
-----------------------------------------------
0.00 0.00 5867/26801 construire_rapport [19]
0.00 0.00 20934/26801 generer_vocabulaire [23]
[16] 0.0 0.00 0.00 26801 ajouter [16]
-----------------------------------------------
0.00 0.00 8798/8798 trier_entrees [26]
[17] 0.0 0.00 0.00 8798 retablir_tas [17]
0.00 0.00 129602/129602 doit_echanger [14]
-----------------------------------------------
0.00 0.00 5866/5866 compter_mot [11]
[18] 0.0 0.00 0.00 5866 copier [18]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[19] 0.0 0.00 0.00 1 construire_rapport [19]
0.00 0.00 5867/26801 ajouter [16]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[20] 0.0 0.00 0.00 1 ecart_type_longueur [20]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[21] 0.0 0.00 0.00 1 empreinte [21]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[22] 0.0 0.00 0.00 1 generer_texte [22]
0.00 0.00 900183/928117 alea [12]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[23] 0.0 0.00 0.00 1 generer_vocabulaire [23]
0.00 0.00 27934/928117 alea [12]
0.00 0.00 20934/26801 ajouter [16]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[24] 0.0 0.00 0.00 1 longueur_moyenne [24]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[25] 0.0 0.00 0.00 1 racine [25]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[26] 0.0 0.00 0.00 1 trier_entrees [26]
0.00 0.00 8798/8798 retablir_tas [17]
-----------------------------------------------
Index by function name
[16] ajouter [9] decouper_lignes [23] generer_vocabulaire
[12] alea [14] doit_echanger [13] hacher_mot
[5] analyser_bigrammes [20] ecart_type_longueur [24] longueur_moyenne
[10] chercher_mot [4] egales [6] normaliser_ligne
[15] comparer [21] empreinte [25] racine
[11] compter_mot [8] en_minuscule [17] retablir_tas
[2] compter_mots_ligne [7] est_lettre [26] trier_entrees
[19] construire_rapport [3] est_mot_vide
[18] copier [22] generer_texte