Tabelle hash
Con 47 memorizzato in una tabella di 7 bucket, il computer va dritto al bucket 47 mod 7 = 5 invece di cercare in tutta la tabella.
Trasformare una chiave in una posizione
Finora trovare un valore voleva dire cercare: fino a n passi in un array non ordinato o in una lista concatenata. Gli array, però, raggiungono qualsiasi posizione in un passo. E se fosse la chiave stessa a indicare la posizione? Se le chiavi fossero piccoli numeri interi, per esempio numeri di matricola da 0 a 99, potresti tenere un array di 100 celle e mettere lo studente k nella cella k. Gli appunti del MIT lo chiamano direct access array (array ad accesso diretto). Non funziona quando le chiavi possibili sono enormi, come numeri di telefono o parole, perché non puoi permetterti una cella per ogni chiave possibile.
Una funzione hash risolve il problema comprimendo qualsiasi chiave in un piccolo intervallo di posizioni, da 0 a m − 1. Una semplice per i numeri interi è h(k) = k mod m, il resto della divisione per m. L'array di m celle è la tabella hash, e le sue celle sono spesso chiamate bucket. Per memorizzare 47 con m = 7, calcola 47 mod 7 = 5 e mettilo nel bucket 5. Per ritrovare 47 più tardi, fai lo stesso calcolo e guarda solo nel bucket 5.
Collisioni e concatenamento
C'è un problema. Con 7 bucket e più di 7 chiavi possibili, alcune chiavi devono condividere un bucket: 12, 33 e 47 danno tutte resto 5. Si chiama collisione, e gli appunti del MIT fanno notare che non si può evitare, per il principio dei cassetti. Il rimedio solito è il concatenamento (chaining): ogni bucket contiene una piccola lista, una catena, di tutte le chiavi finite lì. Trovare 47 costa allora 1 passo per l'hash più un confronto per ogni chiave controllata nella catena 5. Se 12 e 33 sono davanti, fa 1 + 3 = 4 passi.
Quanto sono lunghe le catene? Se le chiavi si distribuiscono in modo uniforme, ogni catena contiene circa n/m chiavi, un numero chiamato fattore di carico. Tieni m grande più o meno quanto n e le catene restano corte, così trovare, inserire ed eliminare richiedono in media un numero costante di passi. Il pericolo è una cattiva distribuzione. Se tutte le chiavi finiscono nello stesso bucket, una sola catena contiene tutte le n chiavi e la tabella non è migliore di una lista concatenata. Nel laboratorio imposta 7 bucket e inserisci 14, 21 e 28: finiscono tutte nel bucket 0.
Trascina per ruotare. Scorri o avvicina le dita per lo zoom.
Le tabelle hash in JavaScript
Raramente scrivi da solo una tabella hash, perché JavaScript ha Map e Set. La specifica ECMAScript dice che devono essere «implementati usando tabelle hash o altri meccanismi che, in media, forniscano tempi di accesso sublineari rispetto al numero di elementi». In altre parole, map.get(key) e set.has(x) devono essere più veloci che cercare in tutta la collezione. MDN aggiunge che una Map conserva le chiavi nell'ordine di inserimento e, a differenza di un oggetto semplice, non contiene chiavi predefinite che potrebbero scontrarsi con le tue.
const price = new Map();
price.set("apple", 1.2);
price.set("bread", 2.5);
console.log(price.get("bread")); // 2.5, nessuna ricerca nella lista
console.log(price.has("milk")); // false
const seen = new Set([4, 8, 15]);
console.log(seen.has(8)); // true| Funzione hash | Trasforma una chiave in un numero di bucket da 0 a m − 1, per esempio k mod m. |
| Collisione | Due chiavi diverse ottengono lo stesso bucket. Inevitabile quando le chiavi sono più dei bucket. |
| Concatenamento | Ogni bucket tiene una lista di tutte le chiavi finite lì. |
| Fattore di carico | n/m, la lunghezza media delle catene. |
| Map e Set | Le collezioni con chiave integrate in JavaScript, che in media devono essere più veloci di una ricerca lineare. |
- Con h(k) = k mod 7, in quale bucket va 47?
- La tabella contiene 12, 25, 33, 47, 70 (inseriti in quest'ordine) con m = 7. Quanti passi richiede trovare 47 (1 per l'hash più le chiavi confrontate)?
- Stessa tabella: quanti passi per trovare 12?
- Stessa tabella: 19 non è memorizzato. Quanti passi richiede una ricerca di 19?
- Stessa tabella: qual è il fattore di carico n/m? (due cifre decimali)
- Che cos'è una collisione?
- Una chiave troppo grande
- Due chiavi diverse che finiscono nello stesso bucket
- Eliminare due volte una chiave
- Una tabella senza chiavi
- Perché nessuna funzione hash può evitare le collisioni quando le chiavi possibili sono più dei bucket?
- I computer arrotondano i numeri
- Il principio dei cassetti
- Le chiavi sono sempre pari
- Le catene sono troppo corte
- Se tutte le chiavi finiscono nello stesso bucket, come si comporta la ricerca di una chiave?
- Sempre 1 passo
- Come cercare in una lista concatenata di n chiavi
- Diventa impossibile
- Come la ricerca binaria
- Che cosa richiede la specifica ECMAScript per i tempi di accesso di Map?
- Esattamente 1 passo
- Sublineari rispetto al numero di elementi, in media
- Θ(n log n)
- Niente
- L'hash di Luhn del 1953 sommava le coppie di cifre vicine e teneva l'ultima cifra di ogni somma. Che cosa diventa 31415926?
- Una funzione hash trasforma una chiave in un numero di bucket; k mod m è una funzione semplice.
- Le collisioni sono inevitabili; il concatenamento tiene una lista in ogni bucket.
- Con un fattore di carico n/m piccolo, trovare, inserire ed eliminare richiedono in media un numero costante di passi.
- Map e Set di JavaScript devono essere in media più veloci di una ricerca lineare.
Fonti di questo corso (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
- 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
- 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
- MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
- Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
- ECMAScript 2025 Language Specification (tc39.es/ecma262)
- 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
- Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
- D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
- "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).
Consultato il: 2026-10-18