INFO 2014 Fichiers et base de données Philippe Fournier-Viger Département d’informatique, U.de Moncton Bureau D216, philippe.fournier-viger@umoncton.ca Hiver 2015
Calendrier TP1 énoncé TP2 énoncé
LA RECHERCHE (CHAPITRE 9)
Introduction Organiser l’information et en permettre la recherche sont des fonctions offertes par de nombreux logiciels. La recherche peut prendre plusieurs formes… (texte, image, base de données, graphes, Web, etc.) Le temps de recherche est généralement important pour les utilisateurs. La recherche peut être exécutée de façon intensive dans certain logiciels et sur de grands volumes de données. Pour ces raisons, il importe de définir et d’utiliser des méthodes de recherche efficientes.
Le problème de la recherche Il existe plusieurs variantes. Par rapport au type de requêtes: recherche exacte, recherche par intervalle, recherche de la plus proche valeur (en employant une mesure de distance), ... Par rapport au type de clés: nombres, vecteurs dans un espace à k dimensions, clés uniques ou non, mots-clés, …
Hypothèses Nous ferons les hypothèses suivantes: Les critères de la recherche sont: biens définis, fixes, Il est possible de déterminer avec exactitude si un élément correspond aux critères ou non, Le domaine de recherche est fini.
Sujet de ce chapitre Au chapitre 9, Au chapitre 10, la recherche d’enregistrements avec une valeur comme clé dans des listes, la recherche de documents par mot-clés (brièvement),… Au chapitre 10, indexations (Arbres 2-3, B+ arbres, etc.), recherche par intervalle….
Modélisation du problème de la recherche Soit une liste L de n enregistrements : L= (k1, I1), (k2, I2) … (kn, In) où Ij est une information associée à une clé kj pour 1 ≤ j ≤ n. Problème de la recherche: Pour une clé K, trouver l’enregistrement (kj, Ij ) dans L tel que kj = K (s’il en existe un). Si trouvé, succès. Sinon, échec.
Les trois grandes approches pour la recherche méthodes séquentielles ou méthode à base de listes (listes, tableaux,…) accès direct par valeur de clé (hachage) (vu dans le cours précédent) méthode d’indexation à base d’arbre (chapitre 10)
Hypothèses (suite) Hypothèses supplémentaires: les valeurs de clé sont uniques, opérateurs de comparaisons: <, >, = les opérateurs de comparaison sont la seule façon de déterminer l’ordre, chaque comparaison a le même coût, le coût d’accès à chaque position d’une liste est le même (tableau). But: Effectuer des recherches avec un nombre minimum le nombre de comparaisons.
Notes Les solutions que nous discuterons sont conçues pour des tableaux. Elles sont pour la plupart applicables à des séquences (des listes où les clés dupliquées sont permises). Il existe des méthodes plus appropriées pour la mémoire vive, pour la mémoire secondaire, ou selon le type de requêtes.
RECHERCHE DANS DES TABLEAUX NON TRIÉS 1, 2, 7, 8, 3, 6, 5, 0, 10, 15, 11, 19
Définition du problème Le problème de la recherche dans une liste non triée L est de trouver la position d’un élément K dans la liste. Notation. La i-ième entrée d’une liste non triée L est notée L[i]. Comment effectuer la recherche? -->
Recherche séquentielle Une approche simple à implémenter: Comparer la valeur K au premier élément. Si égal à K, retourner la position. Sinon, tenter de résoudre le problème pour les n-1 éléments restants. Pseudocode:
Recherche séquentielle (suite) Deux dénouements possibles: K a été trouvé dans une position i ∈ {0, 1… n-1} K n’a pas été trouvé Performance Pour le pire cas: O(n) en temps Pour le cas général? si on veut estimer le coût de l’algorithme pour le cas général, il faudrait connaître la probabilité que K soit dans chacune des positions de la liste (et qu’il n’y soit pas!). suite
Recherche séquentielle (suite) Soit pi la probabilité que K soit en position i de la liste. La probabilité que K ne soit pas trouvé dans la liste est notée 𝑝𝑛 et est définie ainsi: 𝑝𝑛=1 − 𝑖=0 𝑛−1 𝑝𝑖 suite
Recherche séquentielle (suite) Combien de comparaisons sont nécessaires en moyenne pour trouver un élément? Si K est trouvé dans une position i, il faudra i+1 comparaisons pour atteindre la position i. Si K n’est pas présent, il faudra n comparaisons. Le coût moyen en termes de comparaisons pour trouver un élément est donc: 𝑇 𝑛 =𝑛𝑝𝑛+ 𝑖=0 𝑛−1 (𝑖+1)𝑝𝑖 npn : le coût si K n’est pas présent multiplié par la probabilité pn que cela arrive sommation : la sommation des coûts que K se trouve en position 0 à n-1 multiplié par les probabilités respectives que chaque cas arrive. Le cas où K n’est pas présent (n comparaisons) Les autres cas (i+1 comparaisons) suite
Recherche séquentielle (suite) Si on assume que tous les pi sont égaux (sauf pn), on peut remplacer tous les pi par p sauf pn: On a remplacé pi par p On sort p de la sommation On remplace la sommation par le nombre de comparaison moyen. On remplace p par pn. On ramène tout sur le même dénominateur (plusieurs étapes)
Recherche séquentielle (suite) À partir de: 𝑇 𝑛 = 𝑛+1+𝑝𝑛+(𝑛−1) 2 On peut déduire la borne inférieure et supérieure sur le coût moyen : 𝑛+1 2 ≤ 𝑇 𝑛 ≤𝑛 (le coût dépendra de pn) Très lent, pour de grandes listes! Inacceptable si des recherches sont faites régulièrement.
Recherche DANS DES TABLEAUX TRIÉS 1, 2, 4, 5, 7, 12, 30, 45, 60, 89, 200
Définition du problème Le problème de la recherche dans une liste triée L est de trouver la position d’un élément K dans L s’il y est présent. On suppose que la liste L est triée par ordre croissant. Est-ce que la recherche séquentielle est optimale pour ce problème? Non.
Recherche séquentielle modifiée Observation: Pour la recherche séquentielle dans une liste triée, si K > L[n], alors K n’est pas dans L. De façon plus générale, si K > L[j] pour une position j, alors K n’est pas dans les positions 0 à j. Recherche séquentielle modifiée: si K est supérieur à L[n], ne pas parcourir la liste. arrêter si K est supérieur à L[j] pour toute position j. Est-ce que c’est bien? Mieux, mais le coût dans le pire cas reste pratiquement le même.
Recherche par saut Soit une taille d’intervalle j, fixe. Algorithme: Vérifier chaque j ième élément: L[j], L[2j], L[3j], … Si K est supérieur, continuer, Si K est inférieur, effectuer une recherche séquentielle avec les j -1 valeurs de l’intervalle. Si K est égal, retourner la position Si K n’est pas présent, retourner null. Quel est le coût ? j 2j 3j … m j
Coût de la recherche par saut Définissons m tel que mj ≤ n < (m+1)j Dans le pire cas, il y a m+(j-1) comparaisons triples ( <, > =) Donc, dans le pire cas, le nombre de comparaisons est : 𝑇 𝑛 = 𝑛 𝑗 + j − 1 j 2j … m j (m+1) j m+(j-1) = m car il y a m intervalles (j - 1 ) car on fait une recherche séquentielle dans le dernier intervalle m = n / j = le nombre d’intervalles nombre d’intervalles recherche séquentielle dans le dernier intervalle fonction « plancher » (partie entière par défaut)
Coût de la recherche par saut (suite) Quelle est la meilleure valeur de j ? On veut minimiser le coût dans le pire cas: Prendre la dérivée et résoudre pour T’(j) = 0 pour obtenir le minimum. On peut trouver que le minimum est j = 𝑛 et que le coût du pire cas est environ 2 𝒏 Je n’ai pas réussi à recalculer la dérivée.
Recherche par saut (suite) L’idée de la recherche par saut est de balancer le travail entre recherche d’une sous-liste et recherche dans la sous-liste. En général, on souhaite créer des problèmes qui demandent le même effort. Ceci est un exemple de « diviser pour régner ». Et si on étendait cela à trois niveaux (deux niveaux d’intervalles) ou plus?
Recherche binaire la recherche binaire (section 3.5) c’est le meilleur algorithme pour des données triées quand on ne connaît pas la distribution des valeurs. O(log(n)). si on connaît la distribution, on peut utiliser un algorithme de recherche binaire modifié (exemple: on peut assumer que la lettre S se trouve à environ 3/4 du dictionnaire). Détails
Recherche par interpolation Aussi appelée « recherche du dictionnaire ». Initialement, la position p à examiner est déterminée avec la formule: 𝑝 = 𝐾 −𝐿[1] 𝐿 𝑛 −𝐿[1] 𝑛 Le même principe est appliqué ensuite pour les sous- intervalles en remplaçant L[n] par L[p] dans le calcul de p, ou bien en remplaçant 𝐿[1] par L[p] Ainsi, la recherche se dirige dans une direction en fonction de la distance entre K et la borne inférieure ou supérieure de l’intervalle. Nombre d’accès moyen: log(log(n)) preuve
Recherche par interpolation (suite) Recherche binaire quadratique: un cas particulier de recherche par interpolation avec un coût plus facile à analyser.
Recherche binaire quadratique Algorithme : Calculer la position initiale 𝑝 = 𝐾 −𝐿[1] 𝐿 𝑛 −𝐿[1] 𝑛. Si K = L[p], alors retourner p. Si K < L[p], alors chercher vers la gauche par sauts de taille 𝑛 jusqu’à ce qu’une position x telle que L[x] < K soit trouvée. Cela veut dire que K se trouve au maximum à 𝑛 positions de K. Recommencer à partir de l’étape 1. Si K > L[p], alors chercher vers la droite par sauts de taille 𝑛 jusqu’à ce qu’une position x telle que L[x] > K soit trouvée. ….
Recherche binaire quadratique (suite) Quel est le coût ? Nous ne regarderons pas la preuve, mais le coût est de l’ordre de O(log log n), ce qui croît moins vite que O(log n) pour la recherche binaire.
Comparaison n log n log log n Différence 16 4 2 256 8 3 2.7 64k 232 32 6.4 Comparaison de la croissance de log(n) et log(log(n)) n log n-1 2.4 log log n Différence 16 3 4.8 pire 256 7 7.2 même 64k 15 9.6 1.6 232 31 12 2.6 Comparaison de la performance réelle: Cela est causé par les constantes (coût d’interpolation plus élevé): c1 log(n) vs c2 loglog n De plus, un problème de la recherche quadratique est que sa performance dépend fortement de la distribution des données!
Listes TRIÉES PAR FRÉQUENCE
Organisation La plupart des listes sont triées par valeurs. Ce n’est pas la seule option! Une alternative: ordonner les éléments par fréquence!
Listes triée par fréquence Supposons que: Pour chaque clé ki, la probabilité pi que la clé ki soit accédée est connue. La liste est triée par probabilité décroissante. Une recherche séquentielle est utilisée Le coût d’accès à L[0] est 1, le coût d’accès à L[1] est 2, … Pour un grand nombre de recherches, le nombre attendus de comparaisons pour une recherche est: Si les recherches concernent seulement des éléments présents dans le tableau, la somme des probabilité p0 à pn-1 est égale à 1 . 1p0 car 1 comparaison vu que le traitement est séquentielle et p0 est la probabilité de rechercher k0 2p1 car 2 comparaisons vu que la recherche est séquentielle et p1 est la probabilité de rechercher k1 …. La recherche est séquentielle car les données sont triés par fréquence décroissante.
Exemple 1 - requêtes équiprobables Si toutes les requêtes sont équiprobables (cas de la recherche classique) On remplace les pi par 1 / n. Donc, le nombre attendu de comparaisons pour une recherche est : La moitié des enregistrements sont accédés en moyenne. Donc le tri par fréquence n’est pas avantageux dans ce cas.
Exemple 2 – distribution géométrique Si l’on suppose une distribution géométrique: En d’autres mots, 1/2, 1/4 , 1 /16, 1 / 32 … Estimons le coût: +1 Pour la sommation, il y a i+ 1 plutôt que i parce que l’index commence à 0. Si on prend i =0 alors on obtient i+1/ 2^(i+1) = ½ À droite, on a ramené la sommation en faisant commencer l’index à 1. Attention: il y a des erreurs dans le livre. Le résultat 2 fait aussi du sens intuitivement.
Exemple 2 – distribution Zipf Si l’on suppose une distribution zipf: distribution zipf: règle empirique des 80/20: 80 % des requêtes sont faites à 20 % des enregistrements. Autres exemples: les mots utilisés dans une langue, la taille de la population des villes, … … Coût:
Observations Si on connaît la distribution des requêtes, seulement 10 à 15% de la liste a besoin d’être parcourue. Trier une liste peut demander peu de modification au code (contrairement à l’utilisation d’un arbre de recherche…). Or, dans la plupart des applications, il n’est pas possible de connaître la distribution des fréquences à l’avance… De plus, la distribution peut changer avec le temps (ex.: un enregistrement est accédé intensivement pendant une brève période de temps) Les listes auto-organisées visent à résoudre ces deux problèmes
LISTES AUTO-ORGANISÉES
Listes auto-organisées Liste auto-organisée: liste réordonnée en fonction de la distribution actuelle des accès. une bonne approche quand on ne connaît pas la distribution des accès ou elle varie en fonction du temps. utilisation d’heuristiques similaires à celles utilisées pour les bassins de tampon pour réordonner une liste.
Heuristique 1 Calculer la fréquence de chaque enregistrement utiliser un compteur pour chaque enregistrement, quand il y a un accès à un enregistrement, son compteur est incrémenté et il est déplacé vers le début de la liste au besoin. mécanisme d’expiration?
Exemple Un accès a l’élément « B » est effectué indice 0 1 2 3 4 5 6 7 F G B H Y A Z X compteur 7 5 5 4 3 2 2 0 Son compteur est incrémenté indice 0 1 2 3 4 5 6 7 F G B H Y A Z X compteur 7 5 6 4 3 2 2 0 L’élément est ensuite déplacé (si nécessaire) indice 0 1 2 3 4 5 6 7 F B G H Y A Z X compteur 7 6 5 4 3 2 2 0
Heuristique 2 Lors d’un accès, déplacer l’enregistrement en début de liste.
Exemple Un accès a l’élément « B » est effectué 0 1 2 3 4 5 6 7 F G B H Y A Z X Il est déplacé en début de liste 0 1 2 3 4 5 6 7 B F G H Y A Z X Un accès a l’élément « Z » est effectué 0 1 2 3 4 5 6 7 B F G H Y A Z X Il est déplacé en début de liste 0 1 2 3 4 5 6 7 Z B F G H Y A X
Heuristique 2 (suite) pour un tableau, le coût de déplacement d’un élément peut être grand. pour une liste chaînée, le coût de déplacement est faible, ne fonctionne pas bien pour des accès séquentiels répétés.
Heuristique 3 Échanger l’enregistrement accédé avec le précédent dans la liste. Ainsi, les enregistrements fréquemment accédés se déplacent lentement vers le début de la liste.
Exemple Un accès a l’élément « B » est effectué 0 1 2 3 4 5 6 7 F G B H Y A Z X Il est échangé avec l’élément précédent 0 1 2 3 4 5 6 7 F B G H Y A Z X Un accès a l’élément « Z » est effectué 0 1 2 3 4 5 6 7 B F G H Y A Z X Il est échangé avec l’élément précédent 0 1 2 3 4 5 6 7 B F G H Y Z A X
Heuristique 3 (suite) efficient pour un tableau ou une liste chaînée, cas où cela ne fonctionne pas bien: deux enregistrements adjacents X, Y sont successivement accédés. Donc, ils se retrouvent dans le même ordre. En pratique: rare. Variation: déplacer par un nombre de positions supérieur à 1.
Comparaison des performances Exemple: Soit une liste L = A,B,C,D,E,F,G,H Les accès suivants sont effectués: FDFGEGFADFGE Résultat si on utilise l’heuristique 1: FGDEABCH coût des 12 accès: 45 comparaisons Résultat si on utilise l’heuristique 2: EGFDABCH coût des 12 accès: : 54 comparaisons Résultat si on utilise l’heuristique 3: ABFDGECH coût des 12 accès: : 62 comparaisons
Listes auto-organisées (suite) Performe moins bien que la recherche binaire avec une liste triée, qui a une complexité en temps de O(log n). Toutefois, il y a l’avantage de: ne pas avoir à trier la liste. une liste auto-organisée est facile à implémenter. Il y a aussi plusieurs applications intéressantes telle que la compression de données
Application à la compression de texte Entrée: une chaîne de caractères Ex.: “The car on the left hit the car I left.” Sortie: une chaîne de caractères compressée Ex.: “The car on 2 left hit 2 4 I 4.” utilise une liste auto-organisée avec l’heuristique 2 (déplacer un élément accédé en début de liste)
Exemple - encodage The car on the left hit the car I left The The ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: The SORTIE: The
Exemple - encodage The car on the left hit the car I left The Car The ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: The Car The SORTIE: The car
Exemple - encodage The car on the left hit the car I left Car The on ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: Car The on Car The SORTIE: The car on
Exemple - encodage The car on the left hit the car I left on Car The ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: on Car The The on Car SORTIE: The car on 2
Exemple - encodage The car on the left hit the car I left The on Car ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: The on Car left The on Car SORTIE: The car on 2 left
Exemple - encodage The car on the left hit the car I left left The on ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: left The on Car hit left The on Car SORTIE: The car on 2 left hit
Exemple - encodage The car on the left hit the car I left hit left The ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: hit left The on Car The hit left on Car SORTIE: The car on 2 left hit 2
Exemple - encodage The car on the left hit the car I left The hit left ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: The hit left on Car Car The hit left on SORTIE: The car on 2 left hit 2 4
Exemple - encodage The car on the left hit the car I left Car The hit ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: Car The hit left on I Car The hit left on SORTIE: The car on 2 left hit 2 4 I
Exemple - encodage The car on the left hit the car I left I Car The ENTRÉE: The car on the left hit the car I left LISTE AUTO-ORGANISÉE: I Car The hit left on left I Car The hit on SORTIE: The car on 2 left hit 2 4 I 4
Décodage Réalisé en utilisant le même principe (exemple à la diapositive suivante). Point fort de cette approche: il n’est pas nécessaire de transmettre un dictionnaire pour décoder le message. Une généralisation de cette approche de compression est l’algorithme LZW (1984) qui n’utilise pas des mots mais des séquences de caractères de taille variables.
Exemple - décodage The car on 2 left hit 2 4 I 4 The The ENTRÉE: LISTE AUTO-ORGANISÉE: The SORTIE: The
Exemple - décodage The car on 2 left hit 2 4 I 4 The Car The The car ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: The Car The SORTIE: The car
Exemple - décodage The car on 2 left hit 2 4 I 4 Car The on Car The ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: Car The on Car The SORTIE: The car on
Exemple - décodage The car on 2 left hit 2 4 I 4 on Car The The on Car ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: on Car The The on Car SORTIE: The car on the
Exemple - décodage The car on 2 left hit 2 4 I 4 The on Car left The ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: The on Car left The on Car SORTIE: The car on the left
Exemple - décodage The car on 2 left hit 2 4 I 4 left The on Car hit ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: left The on Car hit left The on Car SORTIE: The car on the left hit
Exemple - décodage The car on 2 left hit 2 4 I 4 hit left The on Car ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: hit left The on Car The hit left on Car SORTIE: The car on the left hit the
Exemple - décodage The car on 2 left hit 2 4 I 4 The hit left on Car ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: The hit left on Car Car The hit left on SORTIE: The car on the left hit the car
Exemple - décodage The car on 2 left hit 2 4 I 4 Car The hit left on I ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: Car The hit left on I Car The hit left on SORTIE: The car on the left hit the car I
Exemple - décodage The car on 2 left hit 2 4 I 4 I Car The hit left on ENTRÉE: The car on 2 left hit 2 4 I 4 LISTE AUTO-ORGANISÉE: I Car The hit left on left I Car The hit on SORTIE: The car on the left hit the car I left
LZW Pseudocode de l’encodage w := lire le premier caractère; while( lire le prochain caractère k){ if (wk existe dans le dictionnaire){ w := wk; }else{ ajouter wk au dictionnaire; output le code pour w; w := k; } http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
Exemple Entrée: ^WED^WE^WEE^WEB^WET Sortie: ^WED<256>E<260><261><257>B<260>T
Taux de compression Entrée: ^WED^WE^WEE^WEB^WET 8 bits * 19 = 152 bits Sortie: : ^WED<256>E<260><261><257>B<260>T (8 bits * 7) + (9 bits * 5) = 97 bits Taux de compression: ~37 % Pourquoi 9 bits? tout simplement parce avec 8 bits on ne peut pas dépasser 256. Donc, il faut au moins 9 bits.
LZW Pseudocode du décodage k := lire le premier caractère; output k; w := k; while(lire le prochain caractère ou code k){ entrée := entrée du dictionnaire de k; output entrée; ajouter w + entrée[0] au dictionnaire; w := entrée; } http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
Exemple Entrée : ^WED<256>E<260><261><257>B<260>T Sortie: ^WED^WE^WEE^WEB^WET
Propriétés de LZW La compression débute souvent après un grand nombre de caractères (>100)(ceci dépend de la fréquence de répétition). Sur du texte de grande taille, une compression de 50 % peut souvent être atteinte. Utilisé entre autre pour le format d’images GIF.
Recherche DANS UN ENSEMBLE
Introduction Ensembles au sens mathématique. Problème: Déterminer si une clé est dans un ensemble de clés. Ce problème est un cas particulier du problème de la recherche de clé. Exemple: vérifier si la clé « 2100 » est dans l’ensemble: {100, 200, 6005, 700, 900, 2100, 67} Comment implémenter la vérification de la présence d’un élément dans un ensemble?
Introduction (suite) Comment représenter un ensemble? tableau? liste? arbre: arbre binaire, tas? table de hachage? … Comment implémenter des opérations ensemblistes comme l’union, intersection, négation, etc.?
Hypothèse : toutes les clés d’un ensemble tombent dans un intervalle relativement restreint. Dans ce cas, on peut représenter l’ensemble sous forme d’un vecteur de bits (bitset, bitmap, bit vector…) où chaque position représente un élément. Un bit à « 1 » représente la présence de l’élément dans l’ensemble. Par exemple, les nombres premiers de 1 à 15 peuvent être représentés par le vecteur de bits suivant:
Une représentation compacte Un vecteur de bits est une représentation souvent très compacte. Un bit est 8 fois plus petit que le plus petit type de données en C++ (char). Ex.: deux octets sont suffisants pour indiquer la présence de 16 nombres.
Implémentation des opérations ensemblistes 1) Intersection de deux ensembles:
Implémentation des opérations ensemblistes (suite) 2) Union de deux ensembles:
Implémentation des opérations ensemblistes (suite) 3) Négation d’un ensemble:
Implémentation des opérations ensemblistes (suite) 4) Différence entre deux ensembles:
Implémentations de vecteurs de bits En C++, la classe bitset offre une implémentation de vecteur de bits. boost: dynamic_bitset En Java, la classe BitSet offre une implémentation de vecteur de bits. Méthodes typiquement offertes: mettre un bit à 1 ou à 0 faire l’intersection, union, négation… obtenir la valeur d’un bit à une position obtenir la position du prochain bit à 1. compter le nombre de bits à 1.
Taille d’un vecteur de bits La taille n peut être choisie arbitrairement: Si n > taille des types primitifs on utilise un tableau. Ex.: n = 100 sizeof(int) = 4 octets = 32 bits Un tableau de quatre mots peut être utilisé. 4 x 32 = 128 bits 28 bits sont inutilisés (fragmentation interne) fragmentation interne car 100 x 1 = 100 bits et 4 x 32 = 128 bits. Donc 128 bits non utilisés.
Implémentation Intersection de deux vecteurs de bits Faire l’intersection de chaque mot un par un. intersection(int [] vecteur1, int [] vecteur2){ int [] resultat = new int[vecteur1.length]; for (int i=0; i< i.length; i++){ resultat[i] = vecteur1[i] & vecteur [2]; } L’union, négation, différence peuvent être implémentés de façon similaire.
Implémentation (suite) Comment mettre un bit à 1 ? Pour la plupart des ordinateurs, il n’est pas possible d’accéder à chaque bit individuellement. Solution: Utiliser un masque et le OU logique. Ex.: 01111010 OU 00000100 = 01111110 vecteur masque
Implémentation (suite) Comment mettre un bit à 0 ? Utiliser un masque et le ET logique. Ex.: 01111110 ET 11111011 = 01111010 vecteur masque
Implémentation (suite) Comment inverser un bit? Utiliser un masque et le XOR. Ex.: 01111110 XOR 00000100 = 01111010 vecteur masque
Implémentation (suite) Comment créer un masque avec un bit? En utilisant les opérateurs de décalage bit-à-bit: >> << Une façon simple: masque = 1 << position_bit Ex.: le bit en position 3 d’un masque de 8 bits 1 << 3
Implémentation (suite) Comment vérifier si un bit est à 1? Une façon simple: masque = 1 << position; boolean positif = masque & vecteur; Ex.: vecteur = position = 3 masque = 1 << 3 = masque & valeur = = true;
Performance Effectuer des opérations ensemblistes avec des vecteurs de bits est très efficient. Il est possible d’améliorer davantage la performance en utilisant des stratégies avancés: conserver le résultat d’opérations effectuées en mémoire (intersection, compte, …) réordonner les éléments, conserver les positions du premier et dernier bit à 1. réordonnancement et compression des vecteurs…
Comment utiliser les vecteurs de bits pour la recherche? Exemple: recherche de mots clés dans des documents Chaque mot clé est représenté par un vecteur de bit. Chaque bit représente la présence/absence du mot-clé dans un document. Ex.: Mot-clé: Moncton
Moncton Université Université ET Moncton (ET logique)
Exemple (suite) On pourrait également chercher les documents qui ne contiennent pas un mot clé (négation). qui contiennent un mot ou un autre (ou logique) …
Alternative Une alternative est de stocker un vecteur de bits pour chaque documents. Chaque bit indique la présence ou l’absence d’un mot clé. Par exemple, pour trouver rapidement les mots clés en commun entre deux documents (calculer une similarité). Un tel vecteur est appelé un « fichier de signature ». L’approche pourrait aussi être étendue à d’autres types de fichiers : ex.: musique, vidéo, etc.
Hachage (EN BREF)
Hachage - introduction Structure: tableau de taille M contenant des données Hachage: le calcul de la position d’un enregistrement dans un tableau à partir de sa clé. Fonction de hachage h(K): retourne une position entre 0 ≤ h(K) < M pour une clé K. Le tableau est généralement plus petit que l’étendue des valeurs qui y sont stockées. Collisions. Gestion de collisions. Taille vs risque de collisions.
Hachage - performance accès direct (pratiquement O(1)), le hachage, une méthode de choix pour la recherche d’éléments par clé, La performance (temps, mémoire) dépend de: s’il est permis que de nombreux enregistrements possèdent la même clé. la fonction de hachage, la taille du tableau. Le hachage n’est pas approprié pour la recherche d’éléments par intervalle plutôt que par clé.
Table de hachage vs vecteur de bits Une table de hachage peut être vue comme une implémentation d’un ensemble. La fonction de hachage est utilisée pour rechercher un élément. Il est facile d’implémenter des méthodes comme contains(), union(), intersection(). Très efficient pour contains(). Moins efficient pour union(), intersection()….
Fonction de hachage Hachage parfait: la fonction de hachage identifie un élément de façon unique et est générée généralement après l’ajout de tous les éléments. Utile quand le coût d’accès est élevé (ex.: disque dur, CD…). Hachage imparfait lorsque la fonction de hachage n’identifie pas un élément de façon unique.
Autres facteurs importants taille de la table (risque de collisions) distribution des données (ex.: mots débutant par une lettre) insertion, suppression d’éléments gestion des collisions …
Gestion de collision Hachage ouvert Collisions stockées à l’extérieur du tableau Liste chaînée Efficient en mémoire vive Peu efficient sur disque car les éléments de la liste chaînée peuvent être stockées sur différents secteurs.
Gestion de collision (suite) Hachage fermé Collisions stockées à l’intérieur du tableau Plusieurs stratégies. 1)Stockage par seau Si sur disque, la taille d’un sceau peut être choisie comme celle d’un bloc ou secteur pour une meilleure performance. table de hachage débordement Une bonne fonction de hachage fera que peut d’éléments iront dans la zone de débordement.
Hachage fermé (suite) 2) Stockage sans seau (ajout à la position suivante): ajout de « 1059 »
Hachage fermé (suite) 2) Stockage sans seau (ajout à la position suivante): ajout de « 1059 » Pour éviter de grands amas d’éléments consécutifs, il est possible d’ajouter k positions plus loin, plutôt qu’à la position suivante (k est une constante).
Bibliographie Shaffer Clifford, A practical introduction to data structures and algorithm analysis, edition 3.2. La recherche par interpolation: http://www.cs.technion.ac.il/~itai/publications/Algorithms/p550- perl.pdf J. Ziv and A. Lempel, “Compression of Individual Sequences via Variable-rate Coding,” IEEE Transactions on Information Theory IT24, (1978), pp.530-536. T. Welch, “A Technique for High-Performance Data Compression,” Computer, June 1984. http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html