Sto cercando di capire le tabelle hash - qualcuno può spiegarmelo - chiaramente?


25

Voglio capire l'uso corretto e l'implementazione delle tabelle hash in php (scusate).

Ho letto da qualche parte che un programmatore inesperto ha creato una tabella hash e poi ha iterato attraverso di essa. Ora capisco perché è sbagliato, ma non ho abbastanza conoscenza per sapere se la mia comprensione è corretta (se capisci cosa intendo).

Qualcuno potrebbe spiegarmi come implementare una tabella hash in php (presumibilmente un array associativo) e forse, cosa ancora più importante, come accedere ai valori "con un hash" e cosa significa in realtà?

Risposte:


37

Panoramica della tabella hash semplice

Come aggiornamento, una tabella hash è un modo per memorizzare un valore in una chiave specifica in una struttura di dati. Ad esempio, potrei memorizzare il valore "a"sotto la chiave 1e poi recuperarlo cercando la chiave 1nella tabella hash.

L'esempio più semplice di una tabella hash che mi viene in mente dalla parte superiore della mia testa è una tabella hash che può solo memorizzare numeri interi, dove la chiave per la voce della tabella hash è anche il valore che viene archiviato. Supponiamo che la tua tabella abbia dimensioni 8 ed è sostanzialmente un array in memoria:

---------------------------------
|   |   |   |   |   |   |   |   |
---------------------------------
  0   1   2   3   4   5   6   7  

Funzione hash

Le funzioni hash ti danno un indice su dove archiviare il tuo valore. Una funzione hash piuttosto semplice per questa tabella sarebbe quella di aggiungere 1 al valore che si desidera memorizzare e quindi modificarlo di 8 (la dimensione della tabella). In altre parole, la tua funzione hash è (n+1)%8, dov'è nil numero intero che vuoi memorizzare.

inserti

Se vuoi inserire un valore in questa tabella hash, chiami la tua funzione hash (in questo caso (n+1)%8) sul valore che vuoi inserire per darti un indice. Ad esempio, se vogliamo inserire 14, chiamiamo (14 + 1) % 8e otteniamo l'indice 7, quindi inseriamo il valore nell'indice 7.

---------------------------------
|   |   |   |   |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Allo stesso modo, possiamo inserire 33, 82 e 191 in questo modo:

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

collisioni

Ma cosa succede se proviamo a inserire qualcosa che si scontrerebbe con una voce? 2 dovrebbe andare in indice 3, ma è preso da 82. Ci sono molti modi per risolvere questo problema, il più semplice è chiamare ripetutamente la nostra funzione di hash finché non troviamo uno spazio vuoto.

Quindi la logica è la seguente:

  1. (2 + 1)% 8 = 3
  2. L'indice 3 è pieno
  3. Ricollega 3 alla nostra funzione hash. ( 3 + 1)% 8 = 4 , che è vuoto.
  4. Posiziona il nostro valore nell'indice 4 .

Ora la tabella hash è simile a questa, con il valore 2 memorizzato nell'indice 4.

---------------------------------
|191|   |33 |82 |2  |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

L'aspetto negativo di questa soluzione è che molto presto, il nostro tavolo si riempirà! Se sai che le dimensioni dei tuoi dati sono limitate, questo non dovrebbe costituire un problema se la tua tabella è abbastanza grande da contenere tutti i possibili valori. Se vuoi essere in grado di trattenerne di più, puoi gestire le collisioni in modo diverso. Torniamo al punto in cui eravamo prima di inserire 2.

---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Se ricordi, (2+1)%8ci dà un indice 3, che è preso. Se non si desidera riempire la tabella hash, è possibile utilizzare ciascun indice di tabella come elenco collegato e aggiungerlo all'elenco in tale indice. Quindi, invece di chiamare nuovamente la funzione hash, aggiungeremo semplicemente all'elenco in indice 3:

            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Questo elenco può quindi crescere fino a quando la memoria lo permetterà. Posso inserire 18, e sarà solo aggiunto a 2:

            -----
            |18 |
            -----
            | 2 |
---------------------------------
|191|   |33 |82 |   |   |   |14 |
---------------------------------
  0   1   2   3   4   5   6   7  

Ricerche

La ricerca dei valori nella tabella hash è rapida, dato che la tabella hash ha dimensioni piuttosto grandi. Chiama semplicemente la tua funzione hash e ottieni l'indice. Diciamo che vuoi vedere se 82 è nella tua tabella. La funzione di ricerca chiamerebbe (82+1)%8= 3, e guarderebbe l'elemento in indice 3e lo restituirebbe per te. Se hai cercato 16, la funzione di ricerca cercherebbe nell'indice 1e vedrà che non esiste.

Anche le ricerche devono gestire le collisioni!

Se si tenta di cercare il valore 2, la tabella hash dovrebbe utilizzare la stessa logica di collisione utilizzata per l'archiviazione dei dati e per il recupero dei dati. A seconda del modo in cui funziona la tua tabella di hash, dovrai sempre ripetere l'hashing della chiave fino a trovare la voce che stai cercando (o trovare uno spazio vuoto), oppure scorrere l'elenco collegato fino a quando non trovi l'elemento (o arrivato alla fine della lista)

Sommario

Pertanto, le tabelle hash sono un buon modo per archiviare e accedere rapidamente a coppie chiave-valore. In questo esempio abbiamo usato la stessa chiave del valore, ma nelle tabelle hash del mondo reale le chiavi non sono così limitate. Le funzioni di hash funzioneranno sulle chiavi per generare un indice, quindi la chiave / valore può essere memorizzata in quell'indice. Le tabelle hash non sono pensate per essere ripetute, anche se è possibile farlo. Come puoi vedere, le tabelle di hash possono avere molti spazi vuoti e iterarli attraverso di loro sarebbe una perdita di tempo. Anche se la tabella hash ha una logica per saltare le ricerche di spazi vuoti nel suo iteratore, sarebbe più adatto usare una struttura di dati progettata per gli iteratori, come gli elenchi collegati.


2
ASCII art FTW!
Anto

2
Bella risposta. Vale la pena ricordare che il metodo in cui ciascun indice è un elenco collegato è chiamato concatenamento.
alexn

+1 Risposta eccellente, mi è venuta in mente quasi ogni dubbio. Devi fare un'altra domanda. Ogni implementazione utilizza l'hashing per memorizzare numeri interi? o questo è usato per casi specifici? se sì, quali sono questi casi?
decimale0

@PHIfounder Non sono sicuro di aver compreso completamente la tua domanda, ma la funzione hash che viene eseguita sulla chiave è progettata per essere generica, non solo per applicarsi a un tipo di dati specifico come numeri interi. Se stiamo parlando di codice C, la tabella hash potrebbe essere progettata per accettare (void *) per la chiave e il valore ed eseguire un calcolo hash sul valore del puntatore della chiave.
Jeff

@Jeff in realtà potrei essere uno sciocco a chiedere questo, ma sto parlando della struttura interna di un computer; se ogni computer utilizza una struttura di dati come la tabella hash per memorizzare i riferimenti ai numeri interi o no internamente?
decimale0

7

Immagina una biblioteca con migliaia di libri. Devi organizzare i libri in modo da poterli trovare per titolo il più rapidamente possibile.

Un modo (comune) per farlo è quello di ordinare i libri in ordine alfabetico. Se il titolo inizia con "G", trovi l'area "G", quindi cerca la seconda lettera, pronuncia "ö", quindi "d", "e", "l", restringendo la ricerca e così via , fino a trovare il libro. Questo, tuttavia, potrebbe richiedere molto tempo e inoltre, quando arrivano nuovi libri, a volte è necessario riorganizzare il layout per fare spazio ai nuovi arrivi.

Questa è ricerca binaria. Va bene.

Vi è, tuttavia, un modo più rapido per farlo. Supponiamo che tu enumeri tutte le librerie e gli scaffali e quindi per ogni libro calcoli un numero speciale, si spera univoco, che corrisponda a una libreria / scaffale in cui il libro dovrebbe essere trovato. Il modo in cui calcoli la "chiave" non ha molta importanza purché fornisca un numero dall'aspetto casuale. Ad esempio, potresti aggiungere codici di caratteri di tutte le lettere nel titolo e poi dividerlo per un numero primo (forse non il metodo migliore, ma funziona comunque).

Questo è hash. È molto più veloce, perché non è necessario scorrere intere librerie e scaffali cercando la lettera successiva nel titolo. L'hashing è di solito un'operazione one-shot, a meno che non si abbia una "collisione" quando due o più libri si risolvono nella stessa chiave. Ma va bene, sai che si trovano uno accanto all'altro e, a seconda della qualità della funzione hash, non dovrebbero essercene troppi sotto lo stesso tasto.

Le tabelle hash presentano alcune limitazioni e capricci (rimodellamento / ridimensionamento), che mantiene la ricerca binaria come un concorrente praticabile. Non è tutto in bianco e nero per quanto riguarda quale metodo è migliore. Ma questa è una storia diversa.

PS Ci scusiamo per non aver risposto direttamente alla tua domanda (scrivi una tabella hash in PHP), ma sono dettagli e si chiama "programmazione";)


2
Mi piacciono le spiegazioni non relative al computer ai problemi relativi al computer. +1
gablin

1

La tabella hash in PHP, per quanto ne so, viene semplicemente implementata tramite un:

$my_hash = array(
    1 => "Bob",
    2 => "Alice",
    3 => "Jack"
);

È quindi possibile accedere ai dati tramite chiamate come:

echo $my_hash[2]; // Will echo "Alice"

Utilizzare la funzione foreach () per scorrere i contenuti dell'array.

Il modo migliore per capire le tabelle hash è leggere qualcosa come http://en.wikipedia.org/wiki/Hash_table , ma in sostanza si riduce a questo: il lato sinistro di ogni riga all'interno di quella chiamata array () sono le chiavi . Queste chiavi verranno inserite in un calcolo hash e il risultato è un hash. Probabilmente hai già visto hash MD5 o SHA prima, sembra abbastanza simile a questo. Una parte specifica di questo hash, in genere i primi caratteri X ma a volte l'hash completo, verrà utilizzata per identificare i cosiddetti "bucket", che sono le aree di archiviazione per i valori (lato destro).

Quindi ogni volta che accedi alla tua hashtable, usi la chiave per arrivare al valore. La chiave viene nuovamente calcolata in un hash e l'hash viene utilizzato per cercare rapidamente il valore associato. Quindi le tabelle hash consentono una ricerca più rapida della semplice ricerca di linearità se tutto è stato semplicemente memorizzato. L'unico aspetto negativo è che alcune implementazioni di hash soffrono di collisioni, che è lo stesso hash calcolato per due chiavi diverse. In generale, non è qualcosa di cui devi preoccuparti molto.

Spero che questo fornisca alcuni retroscena, ma per favore prova a leggere di più sull'argomento se ti interessa. La mia spiegazione è molto rudimentale e sono sicuro che ci siano abbastanza buchi, ma dovrebbe bastare per una rapida spiegazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.