Je suis assez confus au sujet des concepts de base d'une table de hachage. Si je devais coder un hachage, comment pourrais-je même commencer? Quelle est la différence entre une table de hachage et juste un tableau normal?
Fondamentalement, si quelqu'un a répondu à cette question, je pense que toutes mes questions recevraient une réponse: si j'avais 100 numéros générés de manière aléatoire (sous forme de clés), comment pourrais-je implémenter une table de hachage et pourquoi serait-ce avantageux sur un tableau?
Psuedo-code ou Java serait apprécié comme outil d'apprentissage ...
Jusqu'à présent, les réponses ont aidé à définir des tables de hachage et à expliquer certaines théories, mais je pense qu'un exemple peut vous aider à mieux les comprendre.
Quelle est la différence entre une table de hachage et juste un tableau normal?
Une table de hachage et un tableau sont deux structures qui vous permettent de stocker et de récupérer des données. Les deux vous permettent de spécifier un index et de récupérer une valeur qui lui est associée. La différence, comme l'a noté Daniel Spiewak, est que les indices d'un tableau sont séquentiels, tandis que ceux d'une table de hachage sont basés sur la valeur des données associé à eux.
Pourquoi devrais-je utiliser une table de hachage?
Une table de hachage peut fournir un moyen très efficace de rechercher des éléments dans de grandes quantités de données, en particulier des données qui ne sont pas facilement consultables autrement. ("Large" signifie ici ginormous , dans le sens où il faudrait beaucoup de temps pour effectuer une recherche séquentielle).
Si je devais coder un hachage, comment pourrais-je même commencer?
Aucun problème. Le moyen le plus simple consiste à inventer une opération mathématique arbitraire que vous pouvez effectuer sur les données, qui renvoie un nombre N
(généralement un entier). Utilisez ensuite ce nombre comme index dans un tableau de "compartiments" et stockez vos données dans le compartiment #N
. L'astuce consiste à sélectionner une opération qui a tendance à placer des valeurs dans différents compartiments d'une manière qui vous permet de les retrouver plus facilement plus tard.
Exemple: Un grand centre commercial conserve une base de données des voitures et des emplacements de stationnement de ses clients, pour aider les acheteurs à se souvenir de l'endroit où ils se sont garés. La base de données stocke make
, color
, license plate
, et parking location
. En quittant le magasin, un acheteur trouve sa voiture en entrant sa marque et sa couleur. La base de données renvoie une liste (relativement courte) de plaques d'immatriculation et de places de stationnement. Une analyse rapide localise la voiture du client.
Vous pouvez implémenter cela avec une requête SQL:
SELECT license, location FROM cars WHERE make="$(make)" AND color="$(color)"
Si les données ont été stockées dans un tableau, qui n'est essentiellement qu'une liste, vous pouvez imaginer implémenter la requête en analysant un tableau pour toutes les entrées correspondantes.
D'un autre côté, imaginez une règle de hachage:
Ajoutez les codes de caractères ASCII de toutes les lettres de la marque et de la couleur, divisez par 100 et utilisez le reste comme valeur de hachage.
Cette règle convertira chaque élément en un nombre compris entre 0 et 99, essentiellement tri les données en 100 compartiments. Chaque fois qu'un client a besoin de localiser une voiture, vous pouvez hacher la marque et la couleur pour trouver le seau un sur 100 qui contient les informations. Vous avez immédiatement réduit la recherche d'un facteur 100!
Modifiez maintenant l'exemple à d'énormes quantités de données, par exemple une base de données avec des millions d'entrées qui est recherchée en fonction de dizaines de critères. Une "bonne" fonction de hachage distribuera les données dans des compartiments d'une manière qui minimise toute recherche supplémentaire, économisant ainsi beaucoup de temps.
Tout d'abord, vous devez comprendre ce qu'est une fonction de hachage. Une fonction de hachage est une fonction qui prend une clé (par exemple, une chaîne de longueur arbitraire) et renvoie un nombre aussi unique que possible . La même clé doit toujours renvoyer le même hachage. Une fonction de hachage de chaîne vraiment simple dans Java pourrait ressembler à
public int stringHash(String s) {
int h = s.length();
for(char c : s.toCharArray()) {
h ^= c;
}
return h;
}
Vous pouvez étudier une bonne fonction de hachage sur http://www.azillionmonkeys.com/qed/hash.html
Maintenant, la carte de hachage utilise cette valeur de hachage pour placer la valeur dans un tableau. Simpliste Java:
public void put(String key, Object val) {
int hash = stringHash(s) % array.length;
if(array[hash] == null) {
array[hash] = new LinkedList<Entry<String, Object> >();
}
for(Entry e : array[hash]) {
if(e.key.equals(key)){
e.value = val;
return;
}
}
array[hash].add(new Entry<String, Object>(key, val));
}
(Cette carte applique des clés uniques. Toutes les cartes ne le font pas.)
Il est possible que deux clés différentes hachent à la même valeur, ou deux hachages différents à mapper au même index de tableau. Il existe de nombreuses techniques pour y faire face. Le plus simple consiste à utiliser une liste chaînée (ou arbre binaire) pour chaque index de tableau. Si la fonction de hachage est suffisamment bonne, vous n'aurez jamais besoin d'une recherche linéaire.
Maintenant, pour rechercher une clé:
public Object get(String key) {
int hash = stringHash(key) % array.length;
if(array[hash] != null) {
for(Entry e : array[hash]) {
if(e.key.equals(key))
return e.value;
}
}
return null;
}
Les tables de hachage sont associatives . C'est une énorme différence par rapport aux tableaux, qui ne sont que des structures de données linéaires. Avec un tableau, vous pourriez faire quelque chose comme ceci:
int[] arr = ...
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i] + 1);
}
Remarquez comment vous extrayez un élément du tableau en spécifiant un décalage mémoire exact (i
). Cela contraste avec les tables de hachage, qui vous permettent de stocker des paires clé/valeur, puis de récupérer la valeur en fonction de la clé:
Hashtable<String, Integer> table = new Hashtable<String, Integer>();
table.put("Daniel", 20);
table.put("Chris", 18);
table.put("Joseph", 16);
Avec le tableau ci-dessus, nous pouvons effectuer l'appel suivant:
int n = table.get("Chris");
... et soyez assuré que n
sera évalué à 18
.
Je pense que cela répondra probablement à la plupart de vos questions. L'implémentation d'une table de hachage est un sujet assez intéressant, un que Wikipédia traite passablement bien .
"Je suis plus intéressé par la façon dont les tables de hachage recherchent la clé et comment la clé est générée."
Le hachage transforme un objet clé en nombre. Cela s'appelle "hachage" - il fait un hachage de l'objet. Voir Fonction de hachage . La sommation des octets d'une chaîne, par exemple, est une technique de hachage standard. Vous calculez la somme modulo 232 pour garder le hachage à une taille gérable. Hash donne toujours la même réponse. C'est [~ # ~] o [~ # ~] (1).
Le nombre vous donne un "slot" dans le HashTable. Étant donné un objet clé arbitraire, la valeur de hachage calcule une valeur de hachage. La valeur de hachage vous donne alors l'emplacement dans le tableau. Généralement mod( hash, table size )
. C'est [~ # ~] o [~ # ~] (1), également.
Voilà la solution générale. Deux calculs numériques et vous êtes passé d'un objet arbitraire comme clé à un objet arbitraire comme valeur. Peu de choses peuvent être aussi rapides.
La transformation de l'objet en valeur de hachage se produit de l'une de ces manières courantes.
S'il s'agit d'un objet "primitif" de 4 octets, la valeur native de l'objet est un nombre.
L'adresse de l'objet est de 4 octets, puis l'adresse de l'objet peut être utilisée comme valeur de hachage.
Une simple fonction de hachage (MD5, SHA1, peu importe) accumule les octets de l'objet pour créer un nombre de 4 octets. Les hachages avancés ne sont pas de simples sommes d'octets, une simple somme ne reflète pas suffisamment tous les bits d'entrée d'origine.
L'emplacement dans la table de hachage est mod (nombre, taille de la table).
Si cet emplacement a la valeur souhaitée, vous avez terminé. Si ce n'est pas la valeur souhaitée, vous devez chercher ailleurs. Il existe plusieurs algorithmes de sondage populaires pour rechercher une place libre dans le tableau. Linéaire est une simple recherche de la prochaine place libre. Quadratic est un saut non linéaire à la recherche d'une fente libre. Un générateur de nombres aléatoires (avec une graine fixe) peut être utilisé pour générer une série de sondes qui répartiront les données de manière uniforme mais arbitraire.
Les algorithmes de sondage ne sont pas [~ # ~] o [~ # ~] (1). Si la table est suffisamment grande, les chances de collision sont faibles et les sondes n'ont pas d'importance. Si la table est trop petite, les collisions se produisent et le sondage se produit. À ce stade, il s'agit de "régler et de peaufiner" pour équilibrer le sondage et la taille de la table afin d'optimiser les performances. Habituellement, nous agrandissons simplement la table.
Voir Tableau de hachage .
Quelque chose que je n'ai pas encore vu spécifiquement noté:
Le point d'utilisation d'une table de hachage sur un tableau est la performance.
L'itération à travers un tableau prend généralement entre O(1) à O(x) où x est le nombre d'éléments dans le tableau. Cependant, le temps trouver votre article sera extrêmement variable, surtout si nous parlons de centaines de milliers d'articles dans le tableau.
Une table de hachage correctement pondérée a généralement un temps d'accès presque constant d'un peu plus de O (1), quel que soit le nombre d'éléments dans la table de hachage.
Vous ne voudriez pas utiliser une table de hachage pour 100 nombres générés aléatoirement.
Une bonne façon de penser aux tables de hachage est de penser aux paires de valeurs. Utilisons les étudiants et disons que tout le monde a un numéro d'identification d'étudiant. Dans votre programme, vous stockez des informations sur les étudiants (noms, numéros de téléphone, factures, etc.). Vous souhaitez rechercher toutes les informations sur un étudiant en utilisant uniquement des informations de base (nom ou ID étudiant, par exemple).
Disons que vous avez 10 000 étudiants. Si vous les stockez tous dans un tableau, vous devez parcourir l'ensemble du tableau en comparant l'ID d'étudiant de chaque entrée avec celui que vous recherchez.
Si, à la place, vous "hachez" (voir ci-dessous) leur numéro d'identification d'étudiant à une position dans le tableau, alors il vous suffit de rechercher les numéros d'étudiant qui ont le même hachage. Beaucoup moins de travail pour trouver ce que vous vouliez.
Dans cet exemple, supposons que les identifiants des étudiants ne sont que des nombres à 6 chiffres. Notre fonction de hachage pourrait n'utiliser que les 3 derniers chiffres du numéro comme "clé de hachage". Ainsi, 232145 est haché à l'emplacement de tableau 145. Vous n'avez donc besoin que d'un tableau de 999 éléments (chaque élément étant une liste d'étudiants).
Cela devrait être un bon début pour vous. Vous devriez, bien sûr, lire un manuel ou wikipedia pour ce genre d'informations. Mais je suppose que vous l'avez déjà fait et que vous en avez assez de lire.
Voici, en bref, comment fonctionne une table de hachage.
Imaginez que vous ayez une bibliothèque pleine de livres. Si vous deviez stocker les livres dans un tableau, vous mettriez chaque livre sur une étagère, puis lorsque quelqu'un vous demanderait de trouver un livre, vous parcourriez toutes les étagères - assez lentement. Si quelqu'un a dit "livre # 12345", vous pouvez le trouver assez facilement.
Disons plutôt que vous dites, si le titre du livre commence par "A", il va dans la ligne 1. Si la deuxième lettre est "B", il va dans la ligne 1, rack 2. Si la troisième lettre est "C", il va dans la rangée 1, le rack 2, l'étagère 3 ... et ainsi de suite jusqu'à ce que vous identifiiez la position du livre. Ensuite, en fonction du titre du livre, vous pourriez savoir exactement où il devrait être.
Maintenant, il y a quelques problèmes dans l'algorithme de "hachage" simpliste que j'ai décrit - certaines étagères vont être surchargées tandis que d'autres resteront vides, certains livres seront assignés au même emplacement .. donc les vraies fonctions de hachage sont soigneusement construites pour essayez d'éviter de tels problèmes.
Mais c'est l'idée de base.
Je vais répondre à cette partie sur la différence entre une table de hachage et un tableau ... mais comme je n'ai jamais implémenté d'algorithme de hachage d'aucune importation auparavant, je laisserai cela à quelqu'un de plus compétent :)
Un tableau n'est qu'une liste ordonnée d'objets. L'objet lui-même n'a pas vraiment d'importance ... ce qui est important, c'est que si vous voulez lister les objets par ordre d'insertion, c'est toujours le même (ce qui signifie que le premier élément toujours a un index de 0).
Quant à une table de hachage, qui est indexée par des clés, pas par ordre ... Je pense qu'une recherche de base sur les algorithmes de hachage vous donnera beaucoup plus d'informations que je peux ... Wikipedia en a une très décente ... qui détermine "bucket" "que les clés entrent pour une récupération rapide des objets arbitraires utilisés comme clés.
Quant aux avantages: Si l'ordre d'insertion est important, un tableau ou une sorte de liste ordonnée est nécessaire. Si une recherche rapide par clé arbitraire (saisie par diverses fonctions de hachage) est importante, alors une table de hachage est logique.
[Ceci est la réponse à un commentaire de me.yahoo.com/a ci-dessus]
Cela dépend de votre fonction de hachage. Supposons que votre fonction de hachage hache un mot selon la longueur de votre mot, la clé pour chris sera 5. De même, la clé pour yahoo sera également 5. Maintenant, les deux valeurs (chris et yahoo) passeront sous 5 (c'est-à-dire dans un 'seau' claveté par 5). De cette façon, vous n'avez pas à créer un tableau égal à la taille de vos données.
La table de hachage est une structure de données créée pour une recherche rapide.
Les tables de hachage ne sont pas efficaces lorsque le nombre d'entrées est très faible.
Quelques exemples:
import Java.util.Collection;
import Java.util.Enumeration;
import Java.util.Hashtable;
import Java.util.Set;
public class HashtableDemo {
public static void main(String args[]) {
// Creating Hashtable for example
Hashtable companies = new Hashtable();
// Java Hashtable example to put object into Hashtable
// put(key, value) is used to insert object into map
companies.put("Google", "United States");
companies.put("Nokia", "Finland");
companies.put("Sony", "Japan");
// Java Hashtable example to get Object from Hashtable
// get(key) method is used to retrieve Objects from Hashtable
companies.get("Google");
// Hashtable containsKey Example
// Use containsKey(Object) method to check if an Object exits as key in
// hashtable
System.out.println("Does hashtable contains Google as key: "+companies.containsKey("Google"));
// Hashtable containsValue Example
// just like containsKey(), containsValue returns true if hashtable
// contains specified object as value
System.out.println("Does hashtable contains Japan as value: "+companies.containsValue("Japan"));
// Hashtable enumeration Example
// hashtabl.elements() return enumeration of all hashtable values
Enumeration enumeration = companies.elements();
while (enumeration.hasMoreElements()) {
System.out.println("hashtable values: "+enumeration.nextElement());
}
// How to check if Hashtable is empty in Java
// use isEmpty method of hashtable to check emptiness of hashtable in
// Java
System.out.println("Is companies hashtable empty: "+companies.isEmpty());
// How to find size of Hashtable in Java
// use hashtable.size() method to find size of hashtable in Java
System.out.println("Size of hashtable in Java: " + companies.size());
// How to get all values form hashtable in Java
// you can use keySet() method to get a Set of all the keys of hashtable
// in Java
Set hashtableKeys = companies.keySet();
// you can also get enumeration of all keys by using method keys()
Enumeration hashtableKeysEnum = companies.keys();
// How to get all keys from hashtable in Java
// There are two ways to get all values form hashtalbe first by using
// Enumeration and second getting values ad Collection
Enumeration hashtableValuesEnum = companies.elements();
Collection hashtableValues = companies.values();
// Hashtable clear example
// by using clear() we can reuse an existing hashtable, it clears all
// mappings.
companies.clear();
}
}
Production:
Does hashtable contains Google as key: true
Does hashtable contains Japan as value: true
hashtable values: Finland
hashtable values: United States
hashtable values: Japan
Is companies hashtable empty: false
Size of hashtable in Java: 3
La question, je crois, reçoit une réponse assez claire et de différentes manières à ce jour.
Je voudrais juste ajouter une autre perspective (ce qui peut également dérouter un nouveau lecteur)
À un niveau de moindre abstraction, les tableaux ne sont que des blocs de mémoire contigus. Étant donné l'adresse de départ (startAddress
), la taille (sizeOfElement
) et le index
d'un seul élément, l'adresse de l'élément est calculée comme suit:
elementAddress = startAddress + sizeOfElement * index
La chose intéressante à noter ici est que les tableaux peuvent être extraits/vus comme des tables de hachage avec index
comme clé et la fonction ci-dessus comme une fonction de hachage qui calcule l'emplacement d'une valeur dans O (1)