Manca il modo in cui le due strutture dati gestiscono le collisioni di hash. I filtri bloom non memorizzano i valori effettivi, quindi lo spazio richiesto è la dimensione costante dell'array designato. Invece, se usi un hash tradizionale, cerca di memorizzare tutti i valori che gli dai, quindi cresce con il tempo.
Considera una funzione hash semplificata (solo a scopo di esempio!) f(x) = x % 2. Ora è in ingresso i seguenti numeri interi: 2, 3, 4, 5, 6, 7.
Hash standard: i valori indicati verranno sottoposti a hash e finiremo con molte collisioni dovute a f(2) = f(4) = f(6) = 0e f(3) = f(5) = f(7) = 1. Tuttavia, l'hash memorizza tutti questi valori e sarà in grado di dirti che 8non è memorizzato in esso. Come lo fa? Tiene traccia delle collisioni e memorizza tutti i valori con lo stesso valore hash, quindi quando lo interroghi, confronta ulteriormente la tua query. Quindi interroghiamo la mappa per 8:, f(8) = 0quindi esamineremo un bucket in cui abbiamo già inserito 2, 4, 6e dobbiamo fare 3 confronti per dirti che 8non faceva parte dell'input.
Filtro Bloom: normalmente, ciascun valore di input viene sottoposto a hash rispetto a kdiverse funzioni hash. Ancora una volta, per semplicità, supponiamo che usiamo solo la singola funzione hash f. Abbiamo quindi bisogno di un array di 2 valori e quando incontriamo l'input 2significa che a causa di f(2) = 0impostare il valore dell'array in posizione 0sul valore 1. Lo stesso succede per 4e 6. Allo stesso modo, gli input 3, 5, 7impostano ciascuno la posizione dell'array 1su value 1. Ora chiediamo se 8faceva parte dell'input: f(8) = 0e l'array in posizione lo 0è 1, quindi il filtro bloom affermerà erroneamente che 8era effettivamente parte dell'input.
Per diventare un po 'più realistici, consideriamo che aggiungiamo una seconda funzione hash g(x) = x % 10. Con ciò, il valore di ingresso 2porta a due valori hash f(2) = 0e g(2) = 2e due corrispondenti posizioni di matrice vengono impostati 1. Ovviamente, l'array ora dovrebbe essere almeno di dimensioni 10. Ma quando eseguiamo una query 8, verificheremo l'array nella posizione 8dovuta g(8) = 8e tale posizione rimarrà comunque 0. Ecco perché ulteriori funzioni hash riducono i falsi positivi che otterrai.
Confronto: il filtro bloom utilizza kfunzioni hash, il che significa che ksi accede a posizioni casuali dell'array. Ma quella cifra è esatta. L'hash invece ti garantisce solo un tempo di accesso costante ammortizzato, ma può de-generare in base alla natura della funzione hash e ai dati di input. Quindi è in genere più veloce, ad eccezione dei casi non generati.
Tuttavia, una volta che si verifica una collisione dell'hash, l'hash standard dovrà verificare l'uguaglianza dei valori memorizzati rispetto al valore della query. Questo controllo di uguaglianza può essere arbitrariamente costoso e non si verificherà mai con un filtro di fioritura.
In termini di spazio, il filtro bloom è costante, in quanto non è mai necessario utilizzare più memoria dell'array designato. D'altra parte, l'hash cresce dinamicamente e può diventare molto più grande a causa della necessità di tenere traccia dei valori di collisione.
Trade-off: ora che sai cosa costa poco e cosa no e in quali circostanze, dovresti essere in grado di vedere il trade-off. I filtri Bloom sono fantastici se vuoi rilevare molto rapidamente che un valore è stato visto in precedenza, ma può vivere con falsi positivi. D'altra parte, puoi scegliere la mappa hash se desideri la correttezza garantita al prezzo di non poter giudicare esattamente il tuo tempo di esecuzione, ma puoi accettare casi degenerati occasionalmente che potrebbero essere molto più lenti della media.
Allo stesso modo, se ti trovi in un ambiente di memoria limitato, potresti voler preferire i filtri bloom per la loro garanzia di utilizzo della memoria.