Vous êtes sur la page 1sur 39

Algorithmes de recherche : hachage et tries

Eric Gaussier

Université Grenoble 1 - Lab. Informatique Grenbole

Eric Gaussier Algorithmes de recherche : hachage et tries


Objectif

On cherche à construire des algorithmes qui permettent de


localiser une clé (nombre, chaı̂ne de caractères) dans un ensemble
pré-déterminé, indicé par des entiers (valeurs de tableaux par
exemple).

Exemple On cherche à savoir si un terme d’une requête est un


terme d’indexation d’une collection de documents donnée. Si oui,
on désire connaı̂tre son indice pour retrouver l’information qui lui
est associée (liste des documents dans lesquels il apparaı̂t par
exemple).

Deux types d’approche :


◮ Hachage
◮ Arbres de recherche digitaux et tries

Eric Gaussier Algorithmes de recherche : hachage et tries


Hachage - Plan

1. Fonctions de hachage
2. Chainage séparé
3. Test linéaire
4. Hachage double
5. Hachage dynamique

Eric Gaussier Algorithmes de recherche : hachage et tries


Principe du hachage

Le hachage se déroule en deux étapes :


1. Utilisation d’une fonction de hachage qui transforme la clé
en un indice de tableau
2. Processus de résolution des collisions qui permet de gérer
les cas où plusieurs clés correspondent au même indice.

Remarque Le hachage est un bon exemple de compromis


temps/espace. Solution dans le cas où la mémoire est infinie ?
Solution dans le cas où l’on n’a pas de contrainte temporelle ?

Eric Gaussier Algorithmes de recherche : hachage et tries


Fonctions de hachage (1)

1. Papamètre M : taille du tableau considéré ; la fonction de


hachage doit donc retourner une valeur entre 0 et M − 1.
2. La fonction de hachage dépend du type de la clé (une
fonction est nécessaire pour chaque type de clé utilisable)

Fonction de hachage simple sur flottant


1. Flottant x compris entre 0 et 1 : h(x) = int(x ∗ M)
2. Flottant compris s et t : h(x) = int( x−s
t−s ∗ M)
3. Entiers bornés : on peut se ramener au cas précédent.
Toutefois l’opération est coûteuse dans la mesure où plusieurs
opérations sont mises en jeu (soustractions, division, voire
conversion (cast))

Eric Gaussier Algorithmes de recherche : hachage et tries


Fonctions de hachage (2)

Fonction de hachage simple sur entier


1. Choisir M (taille du tableau) nombre premier
2. h(k) = kmodM ouEnt(k ∗ α)modM
α est une valeur choisie arbitrairement ; on choisit souvent en
pratique le nombre d’or : α = 0.618033

⇒ Fonction de hachage modulaire (répartit uniformément les


clés sur les valeurs inférieures à M)

Eric Gaussier Algorithmes de recherche : hachage et tries


Fonctions de hachage (3)

Exemple

16838 57 38 6
5758 35 58 58
25566 55 66 0
26966 0 66 65
12767 60 67 90
11367 18 67 25

Fonctions utilisées
◮ k%97 (gauche)
◮ k%100 (centre)
◮ ((int)(α ∗ k))%100 (droite)

Eric Gaussier Algorithmes de recherche : hachage et tries


Fonctions de hachage (4)

Fonction de hachage sur chaı̂ne de caractères


Comment calculer une valeur de hachage pour le mot averylongkey
par exemple ?

En ASCII 7-bits, ce mot correspond à l’entier :

97∗12811 +118∗12810 +101∗1289 +114∗1288 +121∗1287 +108∗1286

+111 ∗ 1285 + 110 ∗ 1284 + 103 ∗ 1283 + 107 ∗ 1282 + 101 ∗ 128 + 121

Notation et algorithme de Horner

((((((((((97∗128+118)∗128+101)∗128+114)∗128+121)∗128+108)

∗128+111)∗128+110)∗128+103)∗128+107)∗128+101)∗128+121

Eric Gaussier Algorithmes de recherche : hachage et tries


Fonctions de hachage (5)
Remarques :
1. ∀(a, b, x, M) ∈ N, ((ax mod M) + b)modM = (ax + b)modM
2. Utilisation de 127, nombre premier, au lieu de 128 (permet
d’éviter certains problèmes dus aux valeurs de M considérées)

Algorithme associé :
static int hash(String s, int M) {
int h = 0, a = 127 ;
for (int i = 0 ; i < s.length() ; i++)
h = (a*h + s.charAt(i)) % M ;
return h ;
}

Question :
Que se passe-t-il lorsque la taille de la table est un multiple de
127 ?

Eric Gaussier Algorithmes de recherche : hachage et tries


Fonctions de hachage (6)

Algorithme de hachage universel

Le principe consiste à randomiser le facteur multiplicatif, en le


faisant varier suivant une suite pseudo-aléatoire

static int hashU(String s, int M) {


int h = 0, a = 31415, b=27183 ;
for (int i = 0 ; i < s.length() ; i++) {
h = (a*h + s.charAt(i)) % M ;
a = (a*b) % (M-1) ;
}
return h ;
}

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (1)
Chaı̂nage séparé

On construit, pour chaque adresse, une liste chaı̂née (en général


non ordonnée) dont les clés produisent cette adresse après hachage.

Propriétés
1. Le chaı̂nage séparé réduit le nombre de comparaisons pour la
recherche séquentielle d’un facteur M en moyenne (M listes
chaı̂nées au lieu d’une), en requérant un espace mémoire
supplémentaire pour M liens.
2. Dans une table de hachage avec chaı̂nage séparé sur M listes
pour N clés (et pour peu que la fonction de hachage soit bien
N
définie), la longueur moyenne des listes est M (la probabilité
N
pour que le nombre de clés dans chaque liste soit M est à peu
près 1).

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (2)

Test linéaire
Cas où l’on connaı̂t à l’avance le nombre d’éléments à placer dans
la table et où l’on dispose d’un emplacement mémoire contigu
permettant de stocker toutes les clés en laissant un peu d’espace
libre : placer N éléments dans une table de taille M > N (hachage
ouvert)
Principe
◮ Hacher la clé
◮ Si collision, on teste la place suivante de la table
◮ Si elle libre, on y range la clé
◮ On continue sinon

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (3)

Test linéaire

i = h(x) ; while (st[i] !=null) i = (i+1)%M ; st[i] = h(x) ;


// test d’égalité lors de la recherche // while (st[i] !=null) ...

Remarque : problème de clustering et de facteur de charge


(facteur α ci-dessous)

Propriété Par test linéaire, le nombre moyen de tests nécessaires


pour une recherche dans une table de taille M contenant N = αM
éléments est (recherche fructueuse, infructueuse) :

1 1 1 1
(1 + ) ; (1 + )
2 1−α 2 (1 − α)2

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (4)

Hachage double

Problème de clustering dans le test linéaire (des clés de valeurs de


hachage différentes sont comparées à la clé courante, ce qui
ralentit la recherche lorsque la table est presque pleine)

Solution : au lieu de considérer les positions suivantes, on utilise


une seconde fonction de hachage pour obtenir un incrément donné
pour la suite de tests.

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (5)

Hachage double

i = h(x) ; k = h2(x) ; aléatoire (par ex.)


while (st[i) !=null) i = (i+k)%M ;
st[i] = h(x) ;
// test d’égalité lors de la recherche // while (st[i] != null) ...

Propriété Par hachage double, le nombre moyen de tests


nécessaires pour une recherche dans une table de taille M
contenant N = αM éléments est (recherche fructueuse,
infructueuse) :
1 1 1
log ;
α 1−α 1−α

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (6)

Hachage dynamique

Cas où la taille de la table de hachage augmente au cours du temps

Principe
◮ Dès que plus de la moitié de la table de hachage est remplie,
on double sa taille
◮ On utilise ensuite un chaı̂nage séparé, un test linéaire ou un
chaı̂nage double

Eric Gaussier Algorithmes de recherche : hachage et tries


Résolution des conflits (7)

Exemple
A S E R C H I N G X M P L
7 3 9 9 8 4 11 7 10 12 0 8 6
1 3 1 5 5 5 3 3 2 3 5 4 2
◮ Comment se déroule un test linéaire (1ière et 2ième lignes)
sur ces données ?
◮ Et un chaı̂nage double (1ière, 2ième et 3ième lignes) ?

Eric Gaussier Algorithmes de recherche : hachage et tries


Arbres de recherche - Plan

1. Arbres de recherche digitaux


2. Tries
3. Tries patricia
4. Tries multi-voies

Eric Gaussier Algorithmes de recherche : hachage et tries


Principe

Les algorithmes que nous allons voir examinent les clés par
morceaux au lieu de comparer leur totalité. On parle alors de
méthodes de recherche radix.

Ces méthodes sont utiles lorsqu’il est facile d’accéder à des parties
de clé.

Eric Gaussier Algorithmes de recherche : hachage et tries


Arbres de recherche digitaux (1)
La méthode la plus simple de recherche radix sur un arabre utilise
les arbres de recherche digitaux (ARD) (digital search trees). Les
ARDs sont des arbres binaires dans lesquels le brachement dans
l’arbre se fait par comparaion de la partie (bit par exemple)
courante de la clé.
On supposera les classes génériques suivantes :
class KEY {
... // définition du type et accès aux parties
}
class ITEM {
private KEY key ;
... // méthodes de comparaison entre items
}
class Node {
private ITEM item ; private Node l, r ;
Node(ITEM x) { item = x ; l = r = null ; }
}
Eric Gaussier Algorithmes de recherche : hachage et tries
Arbres de recherche digitaux (2)

Méthode search
private ITEM searchR(Node h, KEY v, int i) {
if (h == null) return null ;
if (equals(v,h.item.key)) return h.item ;
if (bit(v,i) == 0)
return searchR(h.l, v, i+1) ;
else return searchR(h.r, v, i+1) ;
}
ITEM search(KEY key) {
return searchR(head, key, 0) ;
}
Remarques Longueur des clés (préfixe) ; clés dupliquées

Eric Gaussier Algorithmes de recherche : hachage et tries


Arbres de recherche digitaux (3)

L’insertion d’une nouvelle clé dans l’arbre se déroule de la même


façon.
Méthode insert
private Node insertR(Node h, ITEM x, int i) {
if (h == null) return new Node(x) ;
if (bit(x.key,i) == 0)
h.l = insertR(h.l,x,i+1) ;
else h.r = insertR(h.r,x,i+1) ;
return h ;
}
void insert(ITEM x) {
return insertR(head, x, 0) ;
}

Eric Gaussier Algorithmes de recherche : hachage et tries


Arbres de recherche digitaux (4)

Exemple On utilise la représentation sur 5 bits des lettres de


l’alphabet
A 00001, S 10011, E00101, R 10010, C 00011, H 01000, I 01001
Propriété Une recherche ou une insertion dans un ARD construit
à partir de N clés aléatoires requiert environ log(N) comparaisons
en moyenne, et 2 log(N) dans le pire des cas. Le nombre de
comparaisons n’est jamais supérieur au nombre de bits des clés.

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries (1)

Définition Un trie est un arbre binaire associant une clé à chacune


de ses feuilles.
1. Le trie associé à un ensemble vide de clés est le lien null
2. Le trie associé à une clé unique est la feuille contenant cette
clé
3. Le trie associé à un ensemble de clés est un n ?ud interne dont
le fils gauche (resp. droit) contient un trie associé aux clés
dont le premier bit (première partie) est à 0 (resp. 1).

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries (2)
Méthode search
private ITEM searchR(Node h, KEY v, int i) {
if (h == null) return null ;
if (h.l == null && h.r == null) {
if (equals(v,h.item.key)) return h.item ;
else return null ;
}
if (bit(v,i) == 0)
return searchR(h.l, v, i+1) ;
else return searchR(h.r, v, i+1) ;
}
ITEM search(KEY key) {
return searchR(head, key, 0) ;
}
Remarques Longueur des clés (préfixe) ; clés dupliquées

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries (3)
Insertion : 2 cas de recherche infructueuse (dans un n ?ud interne
ou dans une feuille)
Méthode insert
private Node insertR(Node h, ITEM x, int i) {
if (h == null) return new Node(x) ;
if (h.l == null && h.r == null)
return split(new Node(x),h,i) ;
if (bit(x.key,i) == 0)
h.l = insertR(h.l,x,i+1) ;
else h.r = insertR(h.r,x,i+1) ;
return h ;
}
void insert(ITEM x) {
return insertR(head, x, 0) ;
}
Remarques Méthode split dans la classe Node
Eric Gaussier Algorithmes de recherche : hachage et tries
Tries (4)

Exemple Illustration sur l’exemple précédent


A 00001, S 10011, E 00101, R 10010, C 00011, H 01000, I 01001
Propriété 1 La structure d’un trie est indépendante de l’ordre
d’insertion des clés : un trie unique résulte du processus d’insertion

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries (5)

Propriété 2 L’insertion ou la recherche d’une clé aléatoire dans un


trie construit à partir de N clés distinctes de type chaı̂ne de bits
requiert environ log(N) comparaisons. Dans le pire des cas, le
nombre de comparaisons est limité par le nombre de bits de la clé.

Propriété 3 Un trie construit à partir de N clés aléatoires de bits


posède environ log(N)
2 ≈ 1, 44N n ?uds en moyenne.

Problèmes
1. Les branchements unaires conduisent à la création de n ?uds
supplémentaires
2. Le fait d’avoir 2 types de n ?uds entraı̂ne des complications

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (1)

1968, Morrison propose une solution à ces deux problèmes :


Practical Algorithm To Retrieve Information Coded In
Alphanumeric

Comme les ARDs, les tries patricia permettent la recherche de N


clés dans un arbre de N n ?uds. Comme les tries, ils ne nécéssitent
que log(N) comparaisons de bits.

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (2)

Les n ?uds d’un trie patricia contiennent un champ indiquant le


numéro du bit qui distinue les clés situées à droite du n ?ud
courant de celles situées à gauche. On “saute” ainsi directement
au bit déterminant le choix de la direction, sans faire les
comparaisons de bits intermédiaires.

On stocke les données dans les n ?uds internes et on remplace les


liens vers les n ?uds externes par des liens “remontant” vers le
noued interne correspondant.

Illustration au tableau

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (3)

Méthode search
private ITEM searchR(Node h, KEY v, int i) {
if (h.bit ≤ i) return h.item ;
if (bit(v,h.bit) == 0)
return searchR(h.l, v, h.bit) ;
else return searchR(h.r, v, h.bit) ;
}
ITEM search(KEY key) {
ITEM t = searchR(head, key, 0) ;
if (t == null) return null ;
if (equals(t.key,key)) return t ;
return null ;
}
Remarques Longueur des clés (préfixe) ; clés dupliquées

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (4)

Méthode insert
Pour insérer une clé, on commence par une recherche. La méthode
précédente renvoie la seule clé de l’arbre qui doit être distinguée de
la clé à insérer. On détermine alors la position du premier bit sur
lequel les clés diffèrent, puis on compare ce bit à celui des n ?uds
du chemin de recherche.
Si on arrive sur un n ?ud spécifiant une position supérieure, on sait
qu’on a sauté un bit dans la recherche qui aurait conduit à un lien
null dans un trie classique. On ajoute alors un nouveau n ?ud
testant le bit. Si l’on ne rencontre pas un tel n ?ud, on se trouve
dans un cas correspondant à une recherche se terminant sur une
feuille dans un trie classique. On crée alors un nouveau n ?ud qui
distingue la clé de recherche de la clé ayant terminé la recherche.

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (5)

Méthode insert
Par convention, le lien le plus à gauche (qui correspond à la clé
dont tous les bits sont à 0) ne référence aucun n ?ud interne (seule
la clé nulle a tous ses bits à 0).
void insert(ITEM x) {
int i = 0 ;
KEY v = x.key() ;
ITEM t = searchR(head.l, v, -1) ;
KEY w = (t == null) ? null : t.key() ;
if (v == w) return ;
while (bit(v,i) == bit(w,i)) i++ ;
head.l = insertR(head.l, x, i, head) ;
}

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (6)

Méthode insert
private Node insertR(Node h, ITEM x, int i, Node p) {
KEY v = x.key() ;
if ((h.bit ≥ i) || (h.bit ≤ p.bit)) {
Node t = new Node(x,i) ;
t.l = bit(v,t.bit) == 0 ? t : h ;
t.r = bit(v,t.bit) == 0 ? h : t ;
return t ;
}
if (bit(v,h.bit) == 0)
h.l = insertR(h.l,x,i,h) ;
else h.r = insertR(h.r,x,i,h) ;
return h ;
}

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries patricia (7)
Propriété Une recherche ou une insertion dans un ARD construit
à partir de N clés aléatoires requiert environ log(N) comparaisons
en moyenne, et environ 2 log(N) dans le pire des cas. Le nombre de
comparaisons n’est jamais supérieur au nombre de bits de la clé la
plus grande.
Comparaison expériementale (tps de recherche en sec.)

N A T P
1250 3 4 3
2500 7 6 5
5000 15 12 11
25000 115 87 80
200000 1579 1012 945

Remarque Comparaison au hachage

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries multi-voies (1)

Il est possible d’accélérer la recherche radix en s’occupant de


plusieurs bits à la fois. Pour ce faire, on considère des tries R-aires.

Exemple Chaı̂nes de caractères (trie d’existence R=26, trie


abstrait, trie d’existence R=3)

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries multi-voies (2)

De façon à réaliser un bon compromis entre rapidité et espace


mémoire, une valeur de R privilégiée : 3

⇒ TTR, trie ternaire de recherche (TST - Ternary Search Trie)

Propriété Une recherche ou une insertion dans un TTR prend un


temps proportionnel à la longueur de la clé. Le nombre de liens est
au plus le triple du nombre de caractères dans les clés.

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries multi-voies (TTR) (3)

Méthode search
private boolean searchR(Node h, char[] s, int i) {
if (h == null) return false ;
if (i == s.length) return h.c = END ;
if (s[i] < h.c) return searchR(h.l, s, i) ;
if (s[i] > h.c) return searchR(h.r, s, i) ;
return searchR(h.m, s, i+1) ;
}
boolean search(String s) {
return searchR(head, s.toCharArray(), 0) ;
}
Remarques Longueur des clés (préfixe) ; clés dupliquées

Eric Gaussier Algorithmes de recherche : hachage et tries


Tries multi-voies (TTR) (4)

Méthode insert
private Node insertR(Node h, char[] s, int i) {
char ch = (i < s.length) ? s[i] : END ;
if (h == null) { h = new Node() ; h.c = ch ; }
if (ch == END && h.c == END) return h ;
if (s[i] < h.c) h.l = insertR(h.l, s, i) ;
if (s[i] == h.c) h.m = insertR(h.m, s, i) ;
if (s[i] > h.c) h.r = insertR(h.r, s, i) ;
return h ;
}
boolean insert(String s) {
head = insertR(head, s.toCharArray(), 0) ;
}

Eric Gaussier Algorithmes de recherche : hachage et tries

Vous aimerez peut-être aussi