Per quale tipo di dati sono le operazioni della tabella hash O (1)?


18

Dalle risposte a (Quando) è la ricerca della tabella hash O (1)? , Mi risulta che le tabelle hash abbiano un comportamento nel caso peggiore di O(1) , almeno ammortizzato, quando i dati soddisfano determinate condizioni statistiche e esistono tecniche per contribuire a rendere tali condizioni ampie.

Tuttavia, dal punto di vista di un programmatore, non so in anticipo quali saranno i miei dati: spesso provengono da una fonte esterna. E raramente ho tutti i dati in una volta: spesso inserimenti ed eliminazioni avvengono a una velocità che non è molto inferiore alla frequenza delle ricerche, quindi la preelaborazione dei dati per ottimizzare la funzione hash è fuori.

Quindi, facendo un passo avanti: date alcune conoscenze sull'origine dei dati, come posso determinare se una tabella hash ha la possibilità di avere operazioni e forse quali tecniche usare sulla mia funzione hash?O(1)


Oh, e le tabelle Hash rispetto agli alberi binari sono correlate, ma qui mi sto concentrando sulle tabelle hash e quando sono (o non sono) al meglio.
Gilles 'SO- smetti di essere malvagio' il

Il caso migliore per qualsiasi funzione hash è quando i dati sono distribuiti uniformemente.
0x0

@Sunil: non è vero. Puoi avere funzioni hash su misura.
Raffaello

Penso che questa domanda sia troppo ampia. In particolare, puoi concretizzare quale sarebbe la conoscenza delle fonti di dati?
Raffaello

@Raphael Ad esempio, se le chiavi sono stringhe: nomi di persone, nomi di file in una directory, tag XML, hash di file, ...
SO di Gilles

Risposte:


4

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



5

O(1)

O(1)O(n2W)

O(logn/loglogn)O(1)


5

hun',B(X)=un'X+Bmodp

In passato, secondo un articolo di Usenix di Crosby e Wallach , i comuni linguaggi di programmazione non hanno fatto nulla del genere, lasciando molte app Web (e altri server) aperte a un attacco DoS basato su collisioni di produzione. (L'articolo è del 2003, ma suggerisce che Dan Bernstein aveva scoperto la stessa idea un po 'prima.)

Una rapida ricerca su google afferma che lo stato dell'arte in termini di implementazioni è migliorato e non migliorato .

Un altro aspetto a parte è che in un mondo ad alta larghezza di banda, gli attacchi ai tempi rendono difficile trovare collisioni online (piuttosto che offline come suggerisce il collegamento Crosby-Wallach). Mi sembra di ricordare che Daniel Golovin ha avuto risultati alcuni anni fa su strutture di dati che non sono vulnerabili agli attacchi di temporizzazione, ma non so se siano ampiamente utilizzati.


0

L'analisi del caso medio per le tabelle hash viene effettuata in base alla solita assunzione di uniformità degli input, che una volta rende dovuta al rasoio occam.

Se hai ulteriori conoscenze sul dominio e sulla distribuzione delle chiavi puoi prendere la stessa analisi del caso medio e sostituire la distribuzione uniforme con la tua distribuzione e ricalcolare le aspettative, almeno in teoria.

Ovviamente la difficoltà deriva dal fatto che un'analisi non uniforme dei casi di avaerage "è difficile da fare. E la tua "conoscenza" potrebbe non essere convenientemente esprimibile come una distribuzione che può essere facilmente utilizzata in tale analisi.

Ovviamente la cosa più semplice da fare sono le simulazioni. Implementa le tabelle hash e osserva come si comportano per il tuo tipico set di input.


8
Non sono d'accordo con la prima frase. L'ipotesi standard è che la funzione hash sia casuale, non i dati di input. Supponendo che i dati distribuiti uniformemente spingano l'analisi nel regno della fantasia, i dati del mondo reale non sono mai uniformi! Ma ci sono tecniche da manuale per rendere le funzioni di hash sufficientemente uniformi. Vedi hashing universale e in particolare hash della tabulazione .
JeffE,

@JeffE Guarda l'analisi del caso medio nella risposta di Raffaello che afferma questa ipotesi di uniformità. Non è possibile eseguire un'analisi del caso medio senza una distribuzione. Devi sceglierne uno e se non ti viene dato, il rasoio di Occam suggerisce quello uniforme.
uli

6
Naturalmente hai una distribuzione; è la distribuzione che usi per scegliere la funzione hash. Scegliere una distribuzione per i dati di input è come cercare le chiavi perse sotto il lampione; certo, la luce è migliore, ma probabilmente non è lì che li hai lasciati cadere.
JeffE

@JeffE Ecco come viene eseguita un'analisi di caso medio, scegli una distribuzione e inizia a calcolare. Come sempre la scelta della distribuzione è discutibile. Siete i benvenuti a fare un'analisi del caso medio non uniforme.
uli

4
Sì, so come è fatto. (Controlla il mio profilo.) Se vuoi che la tua analisi sia predittiva (che è l'intero punto di analisi), devi randomizzare la funzione hash. Quindi conosci la distribuzione precisa, perché l'hai scelta.
JeffE

-1

Permutazioni (di lunghezza fissa), come un caso specifico di insiemi noti finiti: è relativamente facile assegnare numeri univoci alle permutazioni, come in questo documento . L'ho usato (in un'implementazione un po 'meno orribile) per mappare le permutazioni di lunghezzan in una matrice di dimensioni n!. Ma potevo farlo perché alla fine avrei avuto bisogno di ogni permutazione; se si utilizza solo un sottoinsieme, è necessaria una funzione personalizzata per quel sottoinsieme o un array sparse efficiente.

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.