Hashing universale - Universal hashing

In matematica e informatica , hashing universale (in un algoritmo randomizzato o struttura di dati) si riferisce alla selezione di una funzione hash a caso da una famiglia di funzioni hash con una certa proprietà matematica (vedi definizione di seguito). Ciò garantisce un basso numero di collisioni in attesa , anche se i dati sono scelti da un avversario. Sono note molte famiglie universali (per l'hashing di interi, vettori, stringhe) e la loro valutazione è spesso molto efficiente. L'hashing universale ha numerosi usi in informatica, ad esempio nelle implementazioni di tabelle hash , algoritmi randomizzati e crittografia .

introduzione

Supponiamo di voler mappare le chiavi di un universo in contenitori (etichettati ). L'algoritmo dovrà gestire alcuni set di dati di chiavi, che non è noto in anticipo. Di solito, l'obiettivo dell'hashing è ottenere un basso numero di collisioni (chiavi da quella terra nello stesso contenitore). Una funzione hash deterministica non può offrire alcuna garanzia in un ambiente contraddittorio se la dimensione di è maggiore di , poiché l'avversario può scegliere di essere precisamente la preimmagine di un bin. Ciò significa che tutte le chiavi di dati finiscono nello stesso cestino, rendendo inutile l'hashing. Inoltre, una funzione di hash deterministica non consente il rimaneggiamento : a volte i dati di input risultano essere dannosi per la funzione di hash (ad esempio ci sono troppe collisioni), quindi si vorrebbe cambiare la funzione di hash.

La soluzione a questi problemi è scegliere una funzione a caso da una famiglia di funzioni hash. Una famiglia di funzioni è detta famiglia universale se, .

In altre parole, due chiavi qualsiasi dell'universo si scontrano con probabilità al massimo quando la funzione hash viene estratta casualmente da . Questa è esattamente la probabilità di collisione che ci aspetteremmo se la funzione hash assegnasse codici hash veramente casuali a ogni chiave. A volte, la definizione è rilassata per consentire la probabilità di collisione . Questo concetto è stato introdotto da Carter e Wegman nel 1977, ed ha trovato numerose applicazioni in informatica (vedi, ad esempio ). Se abbiamo un limite superiore di sulla probabilità di collisione, diciamo che abbiamo -quasi universalità.

Molte, ma non tutte, le famiglie universali hanno la seguente proprietà di differenza uniforme più forte :

, quando viene estratto a caso dalla famiglia , la differenza si distribuisce uniformemente in .

Nota che la definizione di universalità riguarda solo se , che conta le collisioni. La proprietà della differenza uniforme è più forte.

(Analogamente, una famiglia universale può essere XOR universale se , il valore è distribuito uniformemente in dove è l'esclusiva bit per bit o l'operazione. Ciò è possibile solo se è una potenza di due.)

Una condizione ancora più forte è l' indipendenza a coppie : abbiamo questa proprietà quando abbiamo la probabilità che venga eseguito l' hashing su qualsiasi coppia di valori hash è come se fossero perfettamente casuali: . L'indipendenza a coppie è talvolta chiamata forte universalità.

Un'altra proprietà è l'uniformità. Diciamo che una famiglia è uniforme se tutti i valori hash sono ugualmente probabili: per qualsiasi valore hash . L'universalità non implica l'uniformità. Tuttavia, una forte universalità implica uniformità.

Data una famiglia con la proprietà della distanza uniforme, si può produrre una famiglia hash indipendente a coppie o fortemente universale aggiungendo alle funzioni hash una costante casuale uniformemente distribuita con valori . (Analogamente, se è una potenza di due, possiamo ottenere l'indipendenza a coppie da una famiglia hash universale XOR eseguendo un'esclusiva o con una costante casuale uniformemente distribuita.) Poiché uno spostamento di una costante è talvolta irrilevante nelle applicazioni (ad esempio tabelle hash) , a volte non viene fatta un'attenta distinzione tra la proprietà della distanza uniforme e l'indipendenza a coppie.

Per alcune applicazioni (come le tabelle hash), è importante che anche i bit meno significativi dei valori hash siano universali. Quando una famiglia è fortemente universale, questo è garantito: se è una famiglia fortemente universale con , allora anche la famiglia fatta delle funzioni per tutti è fortemente universale per . Sfortunatamente, lo stesso non vale per le famiglie (semplicemente) universali. Ad esempio, la famiglia costituita dalla funzione identitaria è chiaramente universale, ma la famiglia costituita dalla funzione non riesce ad essere universale.

UMAC e Poly1305-AES e molti altri codici di autenticazione del messaggio algoritmi sono basati su hashing universale. In tali applicazioni, il software sceglie una nuova funzione hash per ogni messaggio, in base a un nonce univoco per quel messaggio.

Diverse implementazioni di tabelle hash si basano sull'hashing universale. In tali applicazioni, tipicamente il software sceglie una nuova funzione hash solo dopo aver notato che "troppe" chiavi si sono scontrate; fino ad allora, la stessa funzione di hash continua ad essere utilizzata più e più volte. (Alcuni schemi di risoluzione delle collisioni, come l' hashing dinamico perfetto , selezionano una nuova funzione hash ogni volta che si verifica una collisione. Altri schemi di risoluzione delle collisioni, come l' hashing cuculo e l' hashing a scelta 2 , consentono un numero di collisioni prima di selezionare una nuova funzione hash ). Un'indagine sulle funzioni hash universali e fortemente universali più conosciute per numeri interi, vettori e stringhe si trova in.

Garanzie matematiche

Per qualsiasi set di chiavi fisso , l'utilizzo di una famiglia universale garantisce le seguenti proprietà.

  1. Per ogni fisso in , il numero previsto di chiavi nel cestino è . Quando si implementano tabelle hash mediante concatenamento , questo numero è proporzionale al tempo di esecuzione previsto di un'operazione che coinvolge la chiave (ad esempio una query, un inserimento o una cancellazione).
  2. Il numero previsto di coppie di chiavi in ​​con quella collisione ( ) è limitato sopra da , che è dell'ordine . Quando il numero di bin, è scelto lineare in (cioè è determinato da una funzione in ), il numero previsto di collisioni è . Quando si esegue l'hashing nei contenitori, non ci sono affatto collisioni con probabilità almeno della metà.
  3. Il numero previsto di chiavi nei contenitori con almeno chiavi è limitato sopra da . Pertanto, se la capacità di ciascun contenitore è limitata a tre volte la dimensione media ( ), il numero totale di chiavi nei contenitori pieni è al massimo . Questo vale solo con una famiglia hash la cui probabilità di collisione è limitata sopra da . Se viene utilizzata una definizione più debole, delimitandola da , questo risultato non è più vero.

Poiché le garanzie di cui sopra valgono per qualsiasi insieme fisso , valgono se l'insieme di dati viene scelto da un avversario. Tuttavia, l'avversario deve effettuare questa scelta prima (o indipendentemente da) della scelta casuale di una funzione hash da parte dell'algoritmo. Se l'avversario può osservare la scelta casuale dell'algoritmo, la casualità non serve a nulla e la situazione è la stessa dell'hashing deterministico.

La seconda e la terza garanzia sono generalmente utilizzate insieme al rimaneggiamento . Ad esempio, un algoritmo randomizzato può essere preparato per gestire un certo numero di collisioni. Se osserva troppe collisioni, ne sceglie un'altra a caso dalla famiglia e ripete. L'universalità garantisce che il numero di ripetizioni è una variabile casuale geometrica .

costruzioni

Poiché qualsiasi dato informatico può essere rappresentato come una o più parole macchina, sono generalmente necessarie funzioni di hash per tre tipi di domini: parole macchina ("interi"); vettori a lunghezza fissa di parole macchina; e vettori a lunghezza variabile ("stringhe").

Hashing di interi

Questa sezione si riferisce al caso dell'hashing di interi che si adattano alle parole delle macchine; quindi, operazioni come la moltiplicazione, l'addizione, la divisione, ecc. sono istruzioni economiche a livello di macchina. Lascia che l'universo da hash sia .

La proposta originale di Carter e Wegman era quella di scegliere un primo e definire

dove sono scelti a caso gli interi modulo con . (Questa è una singola iterazione di un generatore congruente lineare .)

Per vedere che è una famiglia universale, nota che vale solo quando

per un numero intero compreso tra e . Poiché , se la loro differenza è diversa da zero e ha modulo inverso . Risolvere per i rendimenti

.

Sono possibili scelte per (poiché è escluso) e, variando nell'intervallo consentito, possibili valori diversi da zero per il lato destro. Quindi la probabilità di collisione è

.

Un altro modo per vedere è una famiglia universale è attraverso la nozione di distanza statistica . Scrivi la differenza come

.

Poiché è diverso da zero ed è distribuito uniformemente in , ne consegue che anche modulo è distribuito uniformemente in . La distribuzione di è quindi pressoché uniforme, fino a una differenza di probabilità tra i campioni. Di conseguenza, la distanza statistica da una famiglia uniforme è , che diventa trascurabile quando .

La famiglia delle funzioni hash più semplici

è solo approssimativamente universale: per tutti . Inoltre, questa analisi è quasi serrata; Carter e Wegman lo dimostrano ogni volta che .

Evitare l'aritmetica modulare

Lo stato dell'arte per l'hashing degli interi è lo schema a turni multipli descritto da Dietzfelbinger et al. nel 1997. Evitando l'aritmetica modulare, questo metodo è molto più facile da implementare e funziona anche molto più velocemente nella pratica (di solito di almeno un fattore quattro). Lo schema presuppone che il numero di bin sia una potenza di due, . Sia il numero di bit in una parola macchina. Quindi le funzioni hash vengono parametrizzate su numeri interi positivi dispari (che rientrano in una parola di bit). Per valutare , moltiplicare per modulo e quindi mantenere i bit di ordine superiore come codice hash. In notazione matematica, questo è

e può essere implementato in linguaggi di programmazione C- like da

(size_t) (a*x) >> (w-M)

Questo schema non soddisfa la proprietà della differenza uniforme ed è solo -quasi-universale ; per qualsiasi , .

Per comprendere il comportamento della funzione hash, si noti che, se e hanno gli stessi bit "M" di ordine più alto, allora ha tutti gli 1 o tutti gli 0 come bit M di ordine più alto (a seconda che sia o sia più grande). Supponiamo che il bit impostato meno significativo di appaia in posizione . Poiché è un intero dispari casuale e gli interi dispari hanno inversi nell'anello , ne consegue che sarà distribuito uniformemente tra gli interi a bit con il bit impostato meno significativo sulla posizione . La probabilità che questi bit siano tutti 0 o tutti 1 è quindi al massimo . D'altra parte, se , allora M bit di ordine superiore di contengono sia 0 che 1, quindi è certo che . Infine, se allora il bit di è 1 e se e solo se anche i bit sono 1, il che accade con probabilità .

Questa analisi è stretta, come può essere mostrato con l'esempio e . Per ottenere una funzione hash veramente 'universale', si può usare lo schema multiplo-aggiungi-shift

che può essere implementato in linguaggi di programmazione simili al C da

(size_t) (a*x+b) >> (w-M)

dove è un intero casuale dispari positivo con ed è un intero casuale non negativo con . Con queste scelte di e , per tutti . Questo differisce leggermente ma in modo importante dalla traduzione errata nel documento inglese.

Hashing vettori

Questa sezione riguarda l'hashing di un vettore a lunghezza fissa di parole macchina. Interpreta l'input come un vettore di parole macchina (interi di bit ciascuno). Se è una famiglia universale con la proprietà della differenza uniforme, anche la seguente famiglia (risalente a Carter e Wegman) ha la proprietà della differenza uniforme (e quindi è universale):

, dove ciascuno è scelto in modo indipendente a caso.

Se è una potenza di due, si può sostituire la sommatoria con l'o esclusivo.

In pratica, se è disponibile un'aritmetica a precisione doppia, questa viene istanziata con la famiglia di funzioni hash a spostamento multiplo. Inizializza la funzione hash con un vettore di interi dispari casuali su bit ciascuno. Quindi se il numero di bin è per :

.

È possibile dimezzare il numero di moltiplicazioni, che in pratica si traduce approssimativamente in una doppia accelerazione. Inizializza la funzione hash con un vettore di interi dispari casuali su bit ciascuno. La seguente famiglia di hash è universale:

.

Se non sono disponibili operazioni a doppia precisione, si può interpretare l'input come un vettore di mezze parole ( -bit interi). L'algoritmo utilizzerà quindi le moltiplicazioni, dove era il numero di mezze parole nel vettore. Pertanto, l'algoritmo viene eseguito a una "velocità" di una moltiplicazione per parola di input.

Lo stesso schema può essere utilizzato anche per l'hashing di interi, interpretando i loro bit come vettori di byte. In questa variante, la tecnica vettoriale è nota come tabulation hashing e fornisce una pratica alternativa agli schemi di hashing universali basati sulla moltiplicazione.

È anche possibile una forte universalità ad alta velocità. Inizializza la funzione hash con un vettore di interi casuali sui bit. Calcolare

.

Il risultato è fortemente universale sui bit. Sperimentalmente, è stato scoperto che funziona a 0,2 cicli di CPU per byte sui recenti processori Intel per .

Hashing stringhe

Questo si riferisce all'hashing di un vettore di parole macchina di dimensioni variabili . Se la lunghezza della stringa può essere delimitata da un numero piccolo, è meglio usare la soluzione vettoriale dall'alto (concettualmente riempiendo il vettore con zeri fino al limite superiore). Lo spazio richiesto è la lunghezza massima della stringa, ma il tempo per valutare è solo la lunghezza di . Finché gli zeri sono vietati nella stringa, lo zero-padding può essere ignorato durante la valutazione della funzione hash senza influire sull'universalità. Nota che se gli zeri sono consentiti nella stringa, allora potrebbe essere meglio aggiungere un carattere fittizio diverso da zero (es. 1) a tutte le stringhe prima del riempimento: questo assicurerà che l'universalità non sia influenzata.

Ora supponiamo di voler eseguire l'hash , dove un buon limite non è noto a priori. Una famiglia universale proposta da tratta la stringa come i coefficienti di un polinomio modulo un primo grande. Se , sia un numero primo e definiamo:

, dove è uniformemente casuale e viene scelto casualmente da un dominio intero di mappatura familiare universale .

Usando le proprietà dell'aritmetica modulare, sopra può essere calcolato senza produrre grandi numeri per stringhe di grandi dimensioni come segue:

uint hash(String x, int a, int p)
	uint h = INITIAL_VALUE
	for (uint i=0 ; i < x.length ; ++i)
		h = ((h*a) + x[i]) mod p
	return h

Questo rolling hash di Rabin-Karp si basa su un generatore congruenziale lineare . L'algoritmo di cui sopra è anche noto come funzione hash moltiplicativa . In pratica, l' operatore mod e il parametro p possono essere evitati del tutto semplicemente permettendo a integer di traboccare perché è equivalente a mod ( Max-Int-Value + 1) in molti linguaggi di programmazione. La tabella seguente mostra i valori scelti per inizializzare h e a per alcune delle implementazioni più diffuse.

Implementazione VALORE INIZIALE un
La funzione hash di Bernstein djb2 5381 33
STLPort 4.6.2 0 5
La funzione hash di Kernighan e Ritchie 0 31
java.lang.String.hashCode() 0 31

Consideriamo due stringhe e sia la lunghezza di quella più lunga; per l'analisi, la stringa più corta è concettualmente riempita di zeri fino alla lunghezza . Una collisione prima dell'applicazione implica che sia una radice del polinomio con coefficienti . Questo polinomio ha al massimo radici modulo , quindi la probabilità di collisione è al massimo . La probabilità di collisione attraverso il random porta la probabilità di collisione totale a . Quindi, se il numero primo è sufficientemente grande rispetto alla lunghezza delle stringhe con hash, la famiglia è molto vicina all'universale (in distanza statistica ).

Altre famiglie universali di funzioni hash utilizzate per eseguire l'hashing di stringhe di lunghezza sconosciuta in valori hash di lunghezza fissa includono l' impronta digitale Rabin e il Buzhash .

Evitare l'aritmetica modulare

Per mitigare la penalità computazionale dell'aritmetica modulare, in pratica vengono utilizzati tre trucchi:

  1. Si sceglie il numero primo vicino a una potenza di due, come un numero primo di Mersenne . Ciò consente di implementare il modulo aritmetico senza divisione (utilizzando operazioni più veloci come addizioni e spostamenti). Ad esempio, su architetture moderne si può lavorare con , mentre 's sono valori a 32 bit.
  2. Si può applicare l'hashing vettoriale ai blocchi. Ad esempio, si applica l'hashing vettoriale a ciascun blocco di 16 parole della stringa e si applica l'hashing di stringa ai risultati. Poiché l'hashing di stringa più lento viene applicato su un vettore sostanzialmente più piccolo, questo sarà essenzialmente veloce come l'hashing di vettore.
  3. Si sceglie una potenza di due come divisore, consentendo l' implementazione del modulo aritmetico senza divisione (usando operazioni più veloci di mascheramento dei bit ). La famiglia di funzioni hash di NH adotta questo approccio.

Guarda anche

Riferimenti

  1. ^ a b c d e Carter, Larry; Wegman, Mark N. (1979). "Classi universali di funzioni hash" . Giornale di scienze informatiche e di sistema . 18 (2): 143-154. doi : 10.1016/0022-0000(79)90044-8 . Versione conferenza in STOC'77.
  2. ^ Miltersen, Peter Bro. "Hashing universale" (PDF) . Archiviato dall'originale (PDF) il 24 maggio 2011 . Estratto il 24 giugno 2009 .
  3. ^ Motwani, Rajeev; Raghavan, Prabhakar (1995). Algoritmi randomizzati . Cambridge University Press. pag. 221. ISBN  0-521-47465-5.
  4. ^ David Wagner, ed. "Advances in Cryptology - CRYPTO 2008" . pag. 145.
  5. ^ Jean-Philippe Aumasson, Willi Meier, Raphael Phan, Luca Henzen. "La funzione hash BLAKE" . 2014. pag. 10.
  6. ^ Thorup, Mikkel (2015). "Hashing ad alta velocità per numeri interi e stringhe". arXiv : 1504.06804 [ cs.DS ].
  7. ^ a b Baran, Ilya; Demaine, Erik D.; Pătraşcu, Mihai (2008). "Algoritmi subquadratici per 3SUM" (PDF) . Algoritmica . 50 (4): 584-596. doi : 10.1007/s00453-007-9036-3 .
  8. ^ Dietzfelbinger, Martin; Hagerup, Torben; Katajainen, Jyrki; Penttonen, Martti (1997). "Un affidabile algoritmo randomizzato per il problema della coppia più stretta" (Postscript) . Giornale di algoritmi . 25 (1): 19-51. doi : 10.1006/jagm.1997.0873 . Estratto il 10 febbraio 2011 .
  9. ^ Thorup, Mikkel . "Algoritmi da libro di testo a SODA" .
  10. ^ Woelfel, Philipp (2003). Über die Komplexität der Multiplikation in eingeschränkten Branchingprogrammmodell (PDF) (Ph.D.). Universität Dortmund . Estratto il 18 settembre 2012 .
  11. ^ Woelfel, Philipp (1999). Efficiente Hashing fortemente universale e ottimamente universale . Fondamenti matematici dell'informatica 1999. LNCS. 1672 . pp. 262–272. doi : 10.1007/3-540-48340-3_24 .
  12. ^ a b c d Thorup, Mikkel (2009). Hashing delle stringhe per il sondaggio lineare . Proc. 20° Simposio ACM-SIAM sugli algoritmi discreti (SODA) . pp. 655-664. CiteSeerX 10.1.1.215.4253 . doi : 10.1137/1.9781611973068.72 .  , sezione 5.3
  13. ^ a b Dietzfelbinger, Martin; Gil, Giuseppe; Matias, Yossi; Pippenger, Nicholas (1992). Le funzioni hash polinomiali sono affidabili (riassunto esteso) . Proc. 19° Colloquio Internazionale su Automi, Linguaggi e Programmazione (ICALP) . pp. 235-246.
  14. ^ Nero, J.; Halevi, S.; Krawczyk, H.; Krovetz, T. (1999). UMAC: autenticazione rapida e sicura dei messaggi (PDF) . Progressi nella crittografia (CRYPTO '99) . , Equazione 1
  15. ^ Pătraşcu, Mihai ; Thorup, Mikkel (2011). Il potere dell'hashing della tabulazione semplice . Atti del 43° Simposio annuale di ACM on Theory of Computing (STOC '11) . pagine 1–10. arXiv : 1011.5200 . doi : 10.1145/1993636.1993638 .
  16. ^ a b Kaser, Owen; Lemire, Daniel (2013). "L'hashing delle stringhe fortemente universale è veloce". Giornale informatico . La stampa dell'università di Oxford. 57 (11): 1624–1638. arXiv : 1202.4961 . doi : 10.1093/comjnl/bxt070 .
  17. ^ "Diapositive del corso dell'università ebraica" (PDF) .
  18. ^ Robert Uzgalis. "Funzioni hash di libreria" . 1996.
  19. ^ Kankowsk, Peter. "Funzioni hash: un confronto empirico" .
  20. ^ Yigit, Ozan. "Funzioni di hash delle stringhe" .
  21. ^ Kernighan; Richi (1988). "6". Il linguaggio di programmazione C (2a ed.). pp.  118 . ISBN 0-13-110362-8.CS1 maint: più nomi: elenco autori ( link )
  22. ^ "Stringa (piattaforma Java SE 6)" . docs.oracle.com . Estratto il 10/06/2015 .

Ulteriori letture

link esterno