diff --git a/slides/cours_14.md b/slides/cours_14.md new file mode 100644 index 0000000000000000000000000000000000000000..635af0eb15f19647de117a2b4f4bede1156f8c5d --- /dev/null +++ b/slides/cours_14.md @@ -0,0 +1,366 @@ +--- +title: "Tables de hachage" +date: "2022-01-19" +patat: + eval: + tai: + command: fish + fragment: false + replace: true + ccc: + command: fish + fragment: false + replace: true + images: + backend: auto +--- + +# Rappel sur les tables de hachage (1/N) + +## Définition? Qui se souvient? + +. . . + +Structure de données abstraite où chaque *valeur* (ou élément) est associée à une *clé* (ou +argument). + +On parle de paires *clé-valeur* (*key-value pairs*). + +## Donnez des exemples de telles paires + +. . . + +* Annuaire (nom-téléphone), +* Catalogue (objet-prix), +* Table de valeur fonctions (nombre-nombre), +* Index (nombre-page) +* ... + +# Rappel sur les tables de hachage (1/N) + +## Opérations principales sur les tables + +* Insertion d'élément (`insert(clé, valeur)`{.C}), insère la paire `clé-valeur` +* Consultation (`get(clé)`{.C}), retourne la `valeur` correspondant à `clé` +* Suppression (`remove(clé)`{.C}), supprime la paire `clé-valeur` + +## Transformation de clé (hashing) + +* Format: 106.3123.8492.13 + + ``` + Numéro AVS | Nom + 0000000000000 | ------- + ... | ... + 1063123849213 | Paul + ... | ... + 3066713878328 | Orestis + ... | ... + 9999999999999 | ------- + ``` +* Nombre de numéros >> nombre d'entrées. + +# Fonctions de transformation de clé (hash functions) + +* La table est représentée avec un tableau. +* La taille du tableau est beaucoup plus petit que le nombre de clés. +* On produit un indice du tableau à partir d'une clé: +$$ +h(key) = n,\quad n\in\mathbb{N}. +$$ +En français: on transforme `key` en nombre entier qui sera l'indice dans le +tableau correspondant à `key`. + +## La fonction de hash + +* La taille du domaine des clés est beaucoup plus grand que le domaine des + indices. +* Plusieurs indices peuvent correspondre à la **même clé**: + * Il faut traiter les **collisions**. +* L'ensemble des indices doit être plus petit ou égal à la taille de la table. + +## Une bonne fonction de hash + +* Distribue uniformément les clés sur l'ensemble des indices. + +# Fonctions de transformation de clés: exemple + +## Méthode par division modulo + +Taille de l'index: `N` chiffres. + +``` +h(key) = key % N. +``` + +## Quelle doit être la taille de la table? + +. . . + +Oui comme vous le pensiez au moins `N`. + +# Traitement des collisions + +## La collision + +``` +key1 != key2, h(key1) == h(key2) +``` + +## Traitement (une idée?) + +. . . + +* La première clé occupe la place prévue dans le tableau. +* La deuxième (troisième, etc.) est placée ailleurs de façon **déterministe**. + +Dans ce qui suit la taille de la table est `table_size`. + +# La méthode séquentielle + +\footnotesize + +* Quand l'index est déjà occupé on regarde sur la position suivante, jusqu'à en + trouver une libre. + +```C +index = h(key); +while (table[index].state == OCCUPIED && table[index].key != key) { + index = (index + 1) % table_size; // attention à pas dépasser +} +table[index].key = key; +table[index].state = OCCUPIED; +``` + +# Méthode de chaînage + +## Comment ça marche? + +* Chaque index de la table contient un pointeur vers une liste chaînée + contenant les paires clés-valeurs. + +## Un petit dessin + +``` + + + + + + + + + + + +``` + +# Méthode de chaînage + +## Exemple + +On hash avec la fonction `h(key) = key % 11` (`key` est le numéro de la lettre +de l'alphabet) + +``` + U | N | E | X | E | M | P | L | E | D | E | T | A | B | L | E + 10 | 3 | 5 | 2 | 5 | 2 | 5 | 1 | 5 | 4 | 5 | 9 | 1 | 2 | 1 | 5 +``` + +## Comment on représente ça? (à vous) + +. . . + +{width=80%} + +# Exercice 1 + +* Construire une table à partir de la liste de clés suivante: + ``` + R, E, C, O, U, P, A, N, T + ``` + +* On suppose que la table est initialement vide, de taille $n = 13$. +* Utiliser la fonction $h1(k)= k \mod 13$ où k est la $k$-ème lettre de l'alphabet et un traitement séquentiel des collisions. + +# Exercice 2 + +* Reprendre l'exercice 1 et utiliser la technique de double hachage pour traiter + les collisions avec + +\begin{align*} +h_1(k)&=k\mod 13,\\ +h_2(k)&=1+(k\mod 11). +\end{align*} +* La fonction de hachage est donc $h(k)=(h(k)+h_2(k)) \% 13$ en cas de + collision. + + +# Exercice 3 + +* Stocker les numéros de téléphones internes d'une entreprise suivants dans un +tableau de 10 positions. +* Les numéros sont compris entre 100 et 299. +* Soit $N$ le numéro de téléphone, la fonction de hachage est +$$ +h(N)=N\mod 10. +$$ +* La fonction de gestion des collisions est +$$ +C_1(N,i)=(h(N)+3\cdot i)\mod 10. +$$ +* Placer 145, 167, 110, 175, 210, 215 (mettre son état à occupé). +* Supprimer 175 (rechercher 175, et mettre son état à supprimé). +* Rechercher 35. +* Les cases ni supprimées, ni occupées sont vides. +* Expliquer se qui se passe si on utilise? +$$ +C_1(N,i)=(h(N)+5\cdot i)\mod 10. +$$ + +# Préambule + +\small + +* On considère pas le cas du chaînage en cas de collisions. +* L'insertion est construite avec une forme du type + + ```C + index = h(key); + while (table[index].state == OCCUPIED + && table[index].key != key) { + index = (index + k) % table_size; // attention à pas dépasser + } + table[index].key = key; + table[index].state = OCCUPIED; + ``` +\normalsize + +* Gestion de l'état d'une case *explicite* + + ```C + typedef enum {EMPTY, OCCUPIED, DELETED} state; + ``` + +# L'insertion + +## Pseudocode? + +. . . + +```C +insert(table, key, value) { + index = hash de la clé; + index = + si "index" est déjà "occupé" + et la clé correspondante n'est pas "key" + alors gérer la collision; + + changer l'état de la case "index" à "occupé"; + changer la valeur de la case "index" à "value"; +} +``` + +# La suppression + +## Pseudocode? + +. . . + +```C +value_t remove(table, key) { + index = hash de la clé; + tant que l'état de la case n'est pas "vide" + si "index" est "occupé" et la clé est "key" + changer l'état de la case à "supprimé" + sinon + index = rehash +} +``` + +# La recherche + +## Pseudocode? + +. . . + +```C +bool search(table, key, value) { + index = hash de la clé; + tant que l'état de la case n'est pas "vide" + si "index" est "occupé" et la clé est "key" + retourner vrai + sinon + index = rehash +} +``` + +# Écrivons le code! + +* Mais avant: + * Quelles sont les structures de données dont nous avons besoin? + * Y a-t-il des fonctions auxiliaires à écrire? + * Écrire les signatures des fonctions. + +. . . + +## Structures de données + +\footnotesize + +. . . + +```C +typedef enum {empty, deleted, occupied}; +typedef ... key_t; +typedef ... value_t; +typedef struct _cell_t { + key_t key; + value_t value; + state_t state; +} cell_t; +typedef struct _hm { + cell_t *table; + int capacity; + int size; +} hm; +``` + +# Écrivons le code! + +## Fonctions auxiliaires + +. . . + +```C +static int hash(key_t key); +static int rehash(int index, key_t key); +static int find_index(hm h, key_t key); +``` + +## Signature de l'API + +. . . + +```C +void hm_init(hm *h, int capacity); +void hm_destroy(hm *h); +bool hm_set(hm *h, key_t key, value_t *value); +bool hm_get(hm h, key_t key, value_t *value); +bool hm_remove(hm *h, key_t key, value_t *value); +bool hm_search(hm h, key_t key); +void hm_print(hm h); +``` + +# Live code session! + +0. Offered to you by ProtonVPN[^1]! + +. . . + +1. Like the video. +2. Subscribe to the channel. +3. Use our one time voucher for ProtonVPN: `PAULISAWESOME`. +4. Consider donating on our patreon. + +[^1]: The fastest way to connect to BBB!