2つのデータ構造がハッシュの衝突をどのように扱うかが欠けています。ブルームフィルターは実際の値を保存しないため、必要なスペースは指定された配列の一定サイズです。代わりに、従来のハッシュを使用する場合、指定したすべての値を保存しようとするため、時間とともに成長します。
単純化されたハッシュ関数を考えてみましょう(例のためだけに!)f(x) = x % 2
。ここで、次の整数を入力します2, 3, 4, 5, 6, 7
。
標準ハッシュ:与えられた値がハッシュ化され、我々は起因する衝突の多くで終わるf(2) = f(4) = f(6) = 0
とf(3) = f(5) = f(7) = 1
。それにもかかわらず、ハッシュはこれらの値をすべて8
保存し、保存されていないことを伝えることができます。それはどうやって?衝突を追跡し、すべての値を同じハッシュ値で保存し、クエリを実行すると、クエリをさらに比較します。それでは、クエリのマップましょう8
:f(8) = 0
、それは我々がすでに挿入されているバケットに見ていきますので2, 4, 6
、そのあなたを伝えるために3つの比較を行う必要がある8
入力の一部ではありませんでした。
ブルームフィルター:通常、各入力値はk
異なるハッシュ関数に対してハッシュされます。繰り返しますが、簡単にするために、単一のハッシュ関数のみを使用すると仮定しますf
。その場合、2つの値の配列が必要です。入力2
に遭遇するとf(2) = 0
、位置の配列値をvalueに設定することを意味し0
ます1
。とについて4
も同じことが起こり6
ます。同様に、入力は3, 5, 7
それぞれ配列の位置1
をvalueに設定します1
。次に8
、入力の一部であるかどうかを照会します。f(8) = 0
位置の配列0
はです1
。そのため、ブルームフィルターは8
、入力の一部であると誤って主張します。
もう少し現実的にするために、2番目のハッシュ関数を追加することを考えてみましょうg(x) = x % 10
。それと共に、入力値の2
2つのハッシュ値にリード線f(2) = 0
及びg(2) = 2
二つ対応する配列位置に設定されます1
。もちろん、配列は少なくともsizeでなければなりません10
。しかし、我々はを照会するとき8
、我々は位置の配列をチェックする8
原因にg(8) = 8
、その位置はまだなります0
。これが、追加のハッシュ関数が取得する誤検知を減らす理由です。
比較:ブルームフィルターは、k
ハッシュ関数を使用しk
ます。これは、アクセスされるランダムな配列位置までを意味します。しかし、その数字は正確です。ハッシュは代わりに償却された一定のアクセス時間を保証するだけですが、ハッシュ関数と入力データの性質によっては縮退する場合があります。そのため、通常は、縮退した場合を除いて高速です。
ただし、ハッシュの衝突が発生すると、標準ハッシュは保存された値とクエリ値の等価性をチェックする必要があります。この等価性チェックは、arbitrarily意的に高価になる場合があり、ブルームフィルターでは発生しません。
指定された配列よりも多くのメモリを使用する必要がないため、スペースに関してはブルームフィルターは一定です。一方、ハッシュは動的に増加し、衝突した値を追跡する必要があるため、はるかに大きくなる可能性があります。
トレードオフ:安価なものとそうでないもの、そしてどのような状況であるかがわかったので、トレードオフを確認できるはずです。ブルームフィルターは、値が以前に見られたことを非常に迅速に検出したいが、誤検出を伴う可能性がある場合に最適です。一方、ランタイムを正確に判断できないという代償を払って正確性を保証したい場合はハッシュマップを選択できますが、平均よりもはるかに低速な場合によっては縮退したケースを受け入れることができます。
同様に、限られたメモリ環境にいる場合、メモリ使用量の保証のためにブルームフィルターを好むかもしれません。