Hash table: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica
Etichette: Modifica da mobile Modifica da web per mobile
 
(14 versioni intermedie di 8 utenti non mostrate)
Riga 6:
 
{{F|programmazione|febbraio 2013}}
In [[informatica]] ununa '''''hash table''''' o '''''hash map''''', in italiano '''tabella hash''' o '''mappa hash''', è una [[struttura dati]] usata per mettere in corrispondenza una data ''chiave'' con un dato ''valore''. Viene usata per l'implementazione di [[Struttura dati#Strutture dati astratte|strutture dati astratte]] associative come Maple mappe o i [[set (informatica)|Setset]].
 
E'È molto utilizzata nei metodi di ricerca nominati ''hashing'' ovvero un'estensione della ricerca indicizzata da chiavi che gestisce problemi di ricerca nei quali le chiavi di ricerca non presentano queste proprietà. Una ricerca basata su hashing è completamente diversa da una basata su confronti: invece di muoversi nella struttura data in funzione dell'esito dei confronti tra chiavi, si cerca di accedere agli elementi nella tabella in modo diretto tramite operazioni aritmetiche che trasformano le chiavi in indirizzi della tabella.
 
Esistono vari tipi di algoritmi di hashing. Per quanto affermato, in una tabella di hashing ben dimensionata il costo medio di ricerca di ogni elemento è indipendente dal numero di elementi. L'hashing è un problema classico dell'informatica; molti algoritmi sono stati proposti, studiati a fondo e impiegati in pratica. Due metodi molto diffusi sono l'hashing statico e l'hashing estendibile e lineare, metodi utilizzati anche dai programmi [[DBMSDatabase management system|programmi di gestione delle basi di dati]].
 
== Descrizione ==
Riga 16:
Il primo passo per realizzare algoritmi di ricerca tramite hashing è quello di determinare la ''funzione di hash'': il dato da indicizzare viene trasformato da un'apposita [[funzione di hash]] in un intero compreso tra <math>0</math> ed <math>m-1</math> che viene utilizzato come indice in un [[array]] di lunghezza m. Supponendo che <math>U</math> sia l'universo delle chiavi e <math>T[0 ... m-1]</math> una tabella hash, una funzione hash h, stabilisce una corrispondenza tra <math>U</math> e le posizioni nella tabella hash, quindi:
 
<math>h:\colon U \rightarrow \{0, 1,... \dots, m-1\}</math>
 
Idealmente, chiavi diverse dovrebbero essere trasformate in indirizzi differenti, ma poiché non esiste la funzione di hash ''perfetta'', ovvero totalmente [[iniettiva]], è possibile che due o più chiavi diverse siano convertite nello stesso indirizzo. Il caso in cui la funzione hash applicata a due chiavi diverse genera un medesimo indirizzo viene chiamato collisione e può essere gestito in vari modi. La scelta di una buona funzione di hash è indispensabile per ridurre al minimo le collisioni e garantire prestazioni sempre ottimali. Il risultato migliore si ha con funzioni pseudo-casuali che distribuiscono i dati in input in modo uniforme.
Il caso in cui la funzione hash applicata a due chiavi diverse genera un medesimo indirizzo viene chiamato '''collisione''' e può essere gestito in vari modi. La scelta di una buona funzione di hash è indispensabile per ridurre al minimo le collisioni e garantire prestazioni sempre ottimali. Il risultato migliore si ha con funzioni pseudo-casuali che distribuiscono i dati in input in modo uniforme.
 
Molto spesso però, una buona funzione di hash può non bastare: infatti le prestazioni di una ''hash table'' sono fortemente legate anche al cosiddetto fattore di carico (''load factor'') calcolato come

:<math>cardinalita\frac\text{cardinalità insieme\ di\ chiavi\ da\ inserire }\over text{dimensione\ massima\ della\ struttura}</math>

e che dice quanta probabilità ha un nuovo elemento di collidere con uno già presente nella tabella. Questa probabilità, in realtà, è più alta di quanto si possa pensare, come dimostra il [[paradosso del compleanno]]. È bene dunque mantenere il load factor il più basso possibile (di solito un valore di {{Formatnum:0.,75}} è quello ottimale) per ridurre al minimo il numero di collisioni. Ciò può essere fatto, ad esempio, ridimensionando l'array ogni volta che si supera il ''load factor'' desiderato.
 
=== Gestione delle collisioni ===
Di seguito vengono riportati i metodi più diffusi per la gestione delle collisioni.
 
* '''Open Hash''' (o indirizzamento aperto)
* '''Hash con concatenazione''' (o con lista di trabocco)''': per ogni cella della tabella di hash si fa corrispondere invece di un elemento, una [[Lista (informatica)|Listalista]] (solitamente una [[LinkedList|lista concatenata]]). In questo modo un elemento che collide viene aggiunto alla lista corrispondente all'indice ottenuto.
 
== Open Hash ==
Nell''''indirizzamento aperto''',''' tutti gli elementi sono memorizzati nella tavola hash stessa; ovvero ogni cella della tavola contiene un elemento dell'insieme dinamico o la costante NULL. Quando cerchiamo un elemento, esaminiamo sistematicamente le celle della tavola finché non troviamo l'elemento desiderato o finché non ci accorgiamo che l'elemento non si trova nella tavola.
 
Diversamente dal concatenamento, non ci sono liste né elementi memorizzati all'esterno della tavola. Quindi nell'indirizzamento aperto, la tavola hash può "riempirsi" al punto tale che non possono essere effettuati altri inserimenti.
 
Il vantaggio dell'indirizzamento aperto sta nel fatto che esclude completamente i puntatori, ''calcoliamo'' la sequenza delle celle da esaminare ('''''ispezione''''').
 
Un concetto importante al riguardo è il cosiddetto hashing uniforme'''hashing uniforme.''' Rappresenta l'hashing ideale, ovvero ogni cella della tabella ha la stessa probabilità di contenere un dato elemento.
 
Esistono diverse tecniche di ispezione, le tre tecniche più comunemente utilizzate sono: ispezione lineare, ispezione quadratica e doppio hashing. Tuttavia, nessuna di queste tecniche soddisfa l'ipotesi di hashing uniforme, in quanto nessuna di esse è in grado di generare più di <math>m^2</math> sequenze di ispezione differenti (anziché <math>m!</math>, come richiede l'hashing uniforme).
Riga 47 ⟶ 50:
Quando si incontra una collisione non si fa altro che utilizzare l'indice successivo a quello che collide, sino a che non si trovi una casella libera.
 
===== Ispezione quadratica =====
<math>h(k,i) = (h^1 (k) + c_1 i + c_2 i^2)\pmod{m}</math>
 
Quando si incontra una collisione non si fa altro che utilizzare l'indice che collide elevato al quadrato con normalizzazione rispetto alla grandezza della tabella dell'indice ottenuto, sino a che non si trovi una casella libera.
 
===== Doppio Hashing =====
<math>h(k,i) = (h_1 (k) + i h_2 (k))\pmod{m}</math> dove, per esempio possiamo porre <math>h_1 (k) = k \pmod{m}</math> e <math>h_2 (k) = 1 + (k \pmod{m^1})</math>.
 
Riga 106 ⟶ 109:
|}
 
dove &<math>\alpha;</math> è il fattore di carico.
 
==Bibliografia==
Riga 115 ⟶ 118:
 
==Altri progetti==
{{interprogetto|preposizione=sull'}}
 
== Collegamenti esterni ==
* {{Collegamenti esterni}}
* {{FOLDOC|hash coding|hash coding}}
* {{Cita web|url=http://didawiki.di.unipi.it/lib/exe/fetch.php/informatica/all-b/hash.pdf|titolo=Note su tabelle hash|cognome=Luccio|nome=Fabrizio|wkautore=Fabrizio Luccio|lingua=italiano|formato=pdf|accesso=20 giugno 2017}}