Remplacement de la recherche dichotomique par une table de hachage

This commit is contained in:
rabot
2026-10-07 11:51:31 +02:00
parent d3f19871d7
commit 57aaec48d0
5 changed files with 281 additions and 27 deletions
+62 -1
View File
@@ -218,4 +218,65 @@ Empreinte du rapport : 1d53de903efd2b7c
real 0m1.251s
user 0m1.214s
sys 0m0.004s
```
```
## Remplacement de la dichotomie par une table de hachage
Un index de 40 001 cases associe chaque mot à son entrée dans le tableau.
`hacher_mot` calcule un entier à partir des caractères du mot ; le modulo
donne la première case à examiner. Chaque case contient l'indice de
l'entrée + 1, ou zéro si elle est libre.
En cas de collision, `chercher_mot` vérifie le mot avec `egales()` puis
avance à la case suivante, en revenant au début si nécessaire. Deux mots
ayant le même hachage restent donc bien distincts. Les nouveaux mots sont
ajoutés à la fin du tableau, sans décaler les entrées existantes.
La recherche et l'insertion sont en O(1) en moyenne pour ces mots de
longueur bornée, avec un pire cas en O(n) en cas de nombreuses collisions.
La limite de 20 000 mots distincts est vérifiée avant chaque nouvelle
insertion ; avec 40 001 cases, l'index reste rempli à moins de 50 %.
Ce choix ajoute `40 001 * sizeof(int)` octets, soit environ 156 Kio sur
cette machine. L'index est libéré après le comptage, avant le tri final
qui déplace les entrées. L'empreinte du rapport est conservée, mais cette
version utilise davantage de mémoire pendant le comptage.
Profil enregistré dans [out/profil_apres_hachage.txt](out/profil_apres_hachage.txt).
La comparaison porte sur la version précédente avec dichotomie et moyenne
calculée une seule fois, conservée dans `out/profil_apres_moyenne.txt`.
| Mesure | Dichotomie | Hachage |
| --- | ---: | ---: |
| Appels de `chercher_mot` | 150 182 | 150 182 |
| Comparaisons de chaînes dans `chercher_mot` | 1 718 447 (`comparer`) | 153 101 (`egales`) |
| Temps réel médian sans profiling (`gcc -O0`, 5 exécutions par version) | 0,888 s | 0,831 s |
Les recherches font environ 11,2 fois moins de comparaisons. Le temps
total baisse d'environ 6,4 % : le gain reste limité car la normalisation
et le tri occupent désormais l'essentiel du temps. L'ordre d'arrivée des
mots remplace l'ordre alphabétique pendant le comptage ; le tri final
effectue donc un nombre différent de comparaisons, tout en produisant
le même classement.
Les cinq mesures, réalisées en alternant les deux versions sur la même
machine, sont respectivement : 0,883 / 0,892 / 0,888 / 0,883 / 0,888 s
avant, et 0,831 / 0,830 / 0,831 / 0,832 / 0,829 s après.
Validation : compilation sans avertissement avec `-Wall -Wextra -Wpedantic`,
rapport et sortie console identiques octet par octet sur les dix
exécutions et avec `-pg`. L'empreinte reste **`1d53de903efd2b7c`**.
Des tests temporaires sous AddressSanitizer et UndefinedBehaviorSanitizer
ont vérifié les collisions avec retour à la première case, les doublons,
les mots de 0/1/31 caractères, 10 000 comptages comparés à une référence
linéaire, les 20 000 entrées possibles et le refus d'un dépassement.
Le programme complet a également été exécuté avec ces deux outils,
sans erreur et avec une sortie identique.
Commandes pour reproduire ce profil :
```bash
mkdir -p out/apres_hachage
gcc -O0 -pg analyseur.c -o out/apres_hachage/analyseur
(cd out/apres_hachage && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_hachage.txt)
```