Esistono diverse tecniche che garantiscono che le ricerche richiedano sempre operazioni O (1), anche nel caso peggiore.
Come posso determinare se una tabella hash ha la possibilità di avere operazioni O (1) e possibilmente quali tecniche usare sulla mia funzione hash?
Il caso peggiore si verifica quando un malintenzionato malintenzionato (Mallory) fornisce deliberatamente dati che Mallory ha appositamente selezionato per rallentare il sistema.
Una volta che hai scelto una particolare funzione di hash, probabilmente è troppo ottimista presumere che Mallory non scoprirà mai quale funzione di hash hai scelto. Una volta che Mallory scopre quale funzione hash hai scelto, se permetti a Mallory di darti molti dati da inserire nella tua tabella hash usando quella funzione hash, allora sei condannato: Mallory può generare internamente rapidamente miliardi di elementi di dati, li hash con il tuo funzione hash per trovare quali elementi di dati sono suscettibili di scontrarsi e quindi fornirti milioni di elementi di dati su mille che potrebbero scontrarsi, portando a ricerche che funzionano molto più lentamente di O (1).
Tutte le tecniche che garantiscono "O (1) ricerche anche nel peggiore dei casi" evitano questo problema facendo un po 'di lavoro extra su ogni inserimento per garantire che, in futuro, ogni possibile ricerca possa avere successo in O (1) tempo . In particolare, supponiamo (nel peggiore dei casi) che Mallory prima o poi scoprirà quale funzione hash stiamo usando; ma ha solo la possibilità di inserire alcuni elementi di dati prima di scegliere una diversa funzione di hash - hash della tabulazione o qualche altro hash universale - uno che selezioniamo appositamente in modo tale che tutti i dati che abbiamo finora possano essere cercati in 2 o 3 sonde - ovvero O (1). Poiché selezioniamo casualmente questa funzione, possiamo essere abbastanza sicuri che Mallory non saprà quale funzione abbiamo scelto per un po '. Anche se Malloryci fornisce immediatamente dati che, anche con questa nuova funzione di hash, si scontrano con i dati precedenti, possiamo quindi scegliere un'altra nuova funzione di hash in modo tale che, dopo aver ripassato, tutti i dati precedenti che lui e tutti gli altri ci hanno fornito possano ora essere visualizzati in 2 o 3 sonde nel peggiore dei casi - ovvero, O (1) ricerche nel peggiore dei casi.
È abbastanza facile selezionare casualmente una nuova funzione hash e ripassare l'intera tabella abbastanza spesso da garantire che ogni ricerca sia sempre O (1). Sebbene ciò garantisca che ogni ricerca sia sempre O (1), queste tecniche, quando si inserisce l'ennesimo elemento in una tabella hash che contiene già elementi N-1, può occasionalmente richiedere tempo O (N) per quell'inserto. Tuttavia, è possibile progettare il sistema in modo tale che, anche quando Mallory ti fornisce deliberatamente nuovi dati che, utilizzando la nuova funzione hash, si scontrano con dati precedenti, il sistema può accettare molti elementi da Mallory e altri prima di dover fare un ricostruzione O (N) completa. Le tecniche di hash table che selezionano una nuova funzione e rehash al fine di garantire le ricerche O (1), anche nel caso peggiore, includono:
- l'hash del cuculo garantisce che ogni ricerca chiave abbia successo con al massimo 2 calcoli hash e 2 ricerche tabella.
- L'hash di hopscotch garantisce che ogni ricerca di chiavi abbia esito positivo dopo l'ispezione di un numero ridotto di H (forse H = 32) nella tabella.
- hash dinamico perfetto - l'articolo del 1994 di Dietzfelbinger è il primo che ho letto che ha sottolineato che, anche se si ripassa "frequentemente" per garantire che ogni ricerca chiave abbia sempre successo con 2 calcoli di hash e 2 ricerche, è possibile per eseguire un rehash completo così raramente che anche se ogni rehash completo utilizza il tempo O (n), il costo medio atteso degli inserimenti e della cancellazione viene ammortizzato O (1).
Strutture dati / tabelle hash