You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dans ce TD nous allons mettre en pratique les notions vues en cours sur les tables de hachage et les tables associatives.
6
6
7
-
## Exercice 1 (fonction de hachage)
7
+
## Exercice 1 (Fonction de hachage)
8
8
9
9
1. Écrire une fonction de hachage qui prend en paramètre une chaîne de caractères, fait la somme des valeurs ASCII des caractères et renvoie un entier compris entre 0 et un maximum donné nommé `max` (le type de retour du hash doit être `size_t`).
10
10
> Nous utiliserons une simple somme des codes ASCII des caractères suivie d'un modulo pour obtenir un entier compris entre 0 et `max`.
> - $s[i]$ le code ASCII du caractère à l'index $i$ dans la chaîne de caractères
22
22
23
-
Ce que nous venons de faire s'appel la technique dite de **folding** (pliage en français). Cela consiste à découper notre donnée en plusieurs parties, calculer une valeur(hash) pour chacune de ces parties, sommer ces valeurs et enfin appliquer un modulo pour obtenir un entier compris entre 0 et `max`. Ici on traite une chaîne de caractère, on va donc faire la somme des valeurs de hachage de chaque caractère.
23
+
Ce que nous venons de faire s'appelle la technique dite de **folding** (pliage en français). Cela consiste à découper notre donnée en plusieurs parties, calculer une valeur(hash) pour chacune de ces parties, sommer ces valeurs et enfin appliquer un modulo pour obtenir un entier compris entre 0 et `max`. Ici on traite une chaîne de caractère, on va donc faire la somme des valeurs de hachage de chaque caractère.
24
24
25
25
:::info
26
26
On veux se ramener à un entier compris entre 0 et `max` car cette valeur hachée sert généralement d'index dans un tableau (table de hachage). Hors on souhaite un tableau de taille "raisonnable" en mémoire, donc on limite la taille de ce tableau à `max`.
> Nous allons utiliser la technique dite de **polynomial rolling hash**. Cette technique consiste à calculer le hash d'une chaîne de caractères en incorporant la position d'une manière plus complexe pour éviter les collisions. On va donc multiplier la valeur de chaque caractère par une puissance de `p` qui dépend de la position du caractère dans la chaîne de caractères.
50
50
> Pour cela, nous allons utiliser la formule suivante:
51
51
> $$
52
-
> \text{hash}(s) = \sum_{i=0}^{n-1} ((s[i] \times p^i) \mod m)
52
+
> \text{hash}(s) = (\sum_{i=0}^{n-1} s[i] \times p^i) \mod m
53
53
> $$
54
54
>
55
55
> Avec:
56
56
> - $p$ un nombre (généralement un nombre premier)
57
57
> - $m$ un nombre (généralement une puissance de 2)
58
58
59
59
:::warning
60
-
On ne veux pas utiliser la fonction [`std::pow`](https://en.cppreference.com/w/c/numeric/math/pow) de la bibliothèque standard car elle est "lente" est fonctionne avec des **flottants** (ce qui nous ferait faire des conversions inutiles).
60
+
On ne veux pas utiliser la fonction [`std::pow`](https://en.cppreference.com/w/c/numeric/math/pow) de la bibliothèque standard car elle est "lente" et fonctionne avec des **flottants** (ce qui nous ferait faire des conversions inutiles).
61
61
62
62
De plus on ne veux pas recalculer la puissance de `p` à chaque itération car c'est des calculs inutiles. Admettons qu'il faille $n-1$ multiplications pour calculer $p^n$ ($p \times p \times p = p^3$) et que l'on recalculait à chaque fois la puissance. Pour une chaîne de taille $n$ on ferait donc $1 + 2 + \ldots + (n-2) + (n-1)$ multiplications. On peut simplifier cette somme en $n(n-1)/2$ ce qui est de l'ordre de $n^2$ multiplications. On veut éviter ça.
63
63
@@ -66,7 +66,7 @@ On va donc utiliser une variable `power` (initialisée à 1 au début) et multip
66
66
:::
67
67
68
68
:::tip choix de `p` et `m`
69
-
Le choix de `p` et `m` va influencer la qualité (collision) et les performances de notre fonction de hachage. Sans rentrer dans les détails, on choisit généralement `p` un **nombre premier** et `m` un nombre suffisamment grand pour éviter les collisions tout en restant petit pour rester performant et éviter des erreur numériques. Par exemple, on peut choisir `p` = **31** et `m` = **10^9 + 9** (que l'on peut noter `1e9 + 9` en C++ ou tout simplement `1000000009`).
69
+
Le choix de `p` et `m` va influencer la qualité (probabilité de collision) et les performances de notre fonction de hachage. Sans rentrer dans les détails, on choisit généralement `p` un **nombre premier** et `m` un nombre suffisamment grand pour éviter les collisions tout en restant petit pour rester performant et éviter des erreur numériques. Par exemple, on peut choisir `p` = **31** et `m` = **10^9 + 9** (que l'on peut noter `1e9 + 9` en C++ ou tout simplement `1000000009`).
70
70
:::
71
71
72
72
## Exercice 2 (Analyse du nombre d'insectes)
@@ -95,7 +95,7 @@ enum class Insect {
95
95
Je vous fournis également une liste des valeurs de l'énumération `Insect` sous forme de vecteur (pour pouvoir itérer sur les valeurs de l'énumération et éviter de faire des `static_cast` pour obtenir les valeurs de l'énumération à partir d'un entier (index) ou encore avoir le nombre d'éléments de l'énumération). Rappels sur les cast avec enum [ici](/Lessons/S1/Variables/#cast-et-enum).
96
96
```cpp
97
97
#include<vector>
98
-
conststd::vector<Insect> insect_values {
98
+
std::vector<Insect>const insect_values {
99
99
Insect::ClassicBee,
100
100
Insect::Ladybug,
101
101
Insect::Butterfly,
@@ -118,7 +118,7 @@ Il n'existe pas de fonction dans la bibliothèque standard C++ pour obtenir le n
@@ -202,7 +202,7 @@ On utilisera la valeur de l'énumération `Insect` comme **clé** dans la table
202
202
:::
203
203
204
204
:::info
205
-
Le paramètre `seed` de la fonction `get_insect_observations` permet de fixer la graine du générateur de nombres aléatoires. Cela permet de reproduire les mêmes observations à chaque exécution du programme. Si vous ne spécifiez pas de graine, le générateur de nombres aléatoires utilisera une graine aléatoire à chaque exécution du programme sinon vous pouvez le fixer à une valeur de votre choix pour obtenir les mêmes observations à chaque exécution.
205
+
Le paramètre `seed` de la fonction `get_insect_observations` permet de fixer la graine du générateur de nombres aléatoires. Cela permet de reproduire les mêmes observations à chaque exécution du programme. Si vous ne spécifiez pas de graine, le générateur de nombres aléatoires utilisera une graine aléatoire à chaque exécution du programme.
206
206
:::
207
207
208
208
3. Utiliser de nouveau la fonction `probabilities_from_count` pour obtenir les probabilités des insectes observés. Il faudra au préalable convertir le résultat de la table de hachage en un vecteur de comptage pour pouvoir utiliser la fonction `probabilities_from_count`.
@@ -228,7 +228,7 @@ Vous pouvez utiliser l'include `<iomanip>` pour formater l'affichage des nombres
228
228
Ajouter `std::cout << std::fixed << std::setprecision(3);` avant d'afficher les nombres pour afficher les nombres avec 3 chiffres après la virgule.
229
229
:::
230
230
231
-
## Exercice 3 (hash sur une structure)
231
+
## Exercice 3 (Hash sur une structure)
232
232
233
233
Donnons nous les enums et structures suivantes:
234
234
@@ -264,7 +264,7 @@ struct Card {
264
264
265
265
L'idée de cet exercice est de créer une fonction de hachage pour la structure `Card` pour que l'on puisse utiliser cette structure comme clé dans une table de hachage.
266
266
267
-
la bibliothèque standard C++ fournit une fonction de hachage pour les types de base (entiers, flottants, etc.) et les chaînes de caractères. Mais elle ne fournit pas de fonction de hachage pour nos structures.
267
+
La bibliothèque standard C++ fournit une fonction de hachage pour les types de base (entiers, flottants, etc.) et les chaînes de caractères. Mais elle ne fournit pas de fonction de hachage pour nos structures.
268
268
269
269
De la même façon que l'on a surchargé les opérateurs pour nos structures, on va pouvoir surcharger la fonction de hachage de notre structure.
0 commit comments