従来の工場インタビューの難しい質問:URLが20億のURLのセットに含まれているかどうかをすばやく判断する方法は?...

問題

問題の説明:ウェブサイトのブラックリストに20億のURLがあります。このブラックリストを保存するにはどうすればよいですか?この時点でURLを入力した場合、そのURLがこのブラックリストに含まれているかどうかをすばやく判断するにはどうすればよいですか?そして、それは与えられたメモリ空間(例えば:500M)内で迅速に判断される必要があります。

分析

おそらく多くの人が最初に考えるのはHashSetを使用することです。HashSetはHashMapに基づいており、理論上の時間の複雑さはO(1)だからです。簡単な目標を達成しましたが、スペースの複雑さはどうですか?URL文字列はハッシュを介して整数値を取得します。整数は4バイトを占有します。20億のURLには理論的に次のものが必要です。

20億* 4/1024/1024/1024 = 7.45G

メモリはスペースの複雑さの要件を満たしていません。

こちらが本記事で紹介する「ブルームフィルター」です。

ブルームフィルターとは

ブルームフィルター(ブルームフィルター)は1970年にブルームによって提案されました。これは実際には非常に長いバイナリベクトルであり、一連のランダムマッピング関数です。ブルームフィルターを使用して、要素がコレクション内にあるかどうかを取得できます。その利点は、スペース効率とクエリ時間が一般的なアルゴリズムよりもはるかに優れていることです。不利な点は、誤認識率が高く、削除が難しいことです。

説明はより抽象的ですか?次に、原理を直接理解してください!

上記の例を例にとります:

ハッシュアルゴリズムによって取得される整数の最大ハッシュ値は次のとおりです。

Integer.MAX_VALUE=2147483647

つまり、URLのハッシュは0〜2147483647になります。

次に、コレクションのすべての可能な値を格納するために使用される長さ2147483647のバイト配列を定義できます。このバイト配列を格納するために、システムは次のことだけを必要とします。

2147483647/8/1024/1024=256M

たとえば、次のURL(X)ハッシュが2であり、次いで、第2の位置にバイト配列に落ち1であり、バイト配列は次のようになります000….00000010

以下では、20億の数値すべてをバイト配列にハッシュします。

バイト配列の2番目のビットが1の場合、このURL(X)が存在する可能性があります。なぜそれが可能ですか?他のURLがハッシュ衝突によってハッシュされる可能性があるため、これは誤判断です。

ただし、このバイト配列の2番目のビットが0の場合、このURL(X)はコレクションに存在してはなりません。

複数のハッシュ

1233356-e8ba00cf2c559ca3.png

ハッシュの衝突によって引き起こされる誤判断の確率を減らすために、このバイト配列に該当するN個のハッシュ値を取得するために、さまざまなハッシュアルゴリズムを使用してこのURL(X)をN回ハッシュすることができます。位置がすべて1でない場合、このURL(X)はコレクションに存在していてはなりません。

グアバのBloomFilter

Guavaフレームワークは、Bloomフィルターの特定の実装であるBloomFilterを提供するため、開発者は独自のアルゴリズム実装を作成する必要がなくなります。

BloomFilterを作成する

BloomFilterは、インスタンスを作成するためのオーバーロードされた静的createメソッドをいくつか提供します。

public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions, double fpp);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions, double fpp);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions);

メソッドを呼び出す:

static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions, double fpp, Strategy strategy);

パラメータの意味:

  • funnelは、IntegerFunnel、LongFunnel、StringCharsetFunnelなど、ブルームフィルターに格納されるデータのタイプを指定します。
  • expectedInsertions格納されると予想されるデータの量
  • fppの誤判定率。デフォルトは0.03です。

BloomFilterのバイト配列のサイズは、expectedInsertionsおよびfppパラメータによって決定されます。メソッドを参照してください。

static long optimalNumOfBits(long n, double p) {
    if (p == 0) {
        p = Double.MIN_VALUE;
    }
    return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
}

実際のバイト配列は、BitArrayクラスで維持されます。最後に、putメソッドとmightContainメソッドを使用して、要素を追加し、要素が存在するかどうかを確認します。

アルゴリズムの特徴

1.ハッシュ判定を使用しているため、時間効率は非常に高いです。スペース効率も大きな利点です。
2.特定のシナリオで使用する必要がある誤判断の可能性があります。
3.ハッシュ衝突を区別することは不可能であるため、削除操作はあまり良くありません。

シーンを使用

ブルームフィルターの優れた使い方は、要素がコレクション内にあるかどうかをすばやく判断することです。一般的な使用シナリオは次のとおりです。

1.ブラックリスト:スパム対策、メールボックスが数十億のスパムリスト(同様にスパムメッセージ)からのスパムであるかどうかを判断します
2. URL重複排除:WebクローラーによるURL重複排除により、同じものをクロールしないようにしますURLアドレス
3、単語のスペルチェック
4、Key-Valueキャッシュシステムのキー検証(キャッシュペネトレーション):キャッシュペネトレーション、ハッカーが存在しないキャッシュにアクセスするときに、すべての可能なデータキャッシュをブルームフィルターに入れます。すぐに戻ってキャッシュとDBのハングを回避します。
5. ID検証。たとえば、注文システムは注文IDが存在するかどうかを照会し、存在しない場合は直接返します。

参考文献

https://www.jianshu.com/p/4d31af4c08fb


Kotlin開発者コミュニティ

1233356-4cc10b922a41aa80

中国で最初のKotlin開発者コミュニティの公開アカウント。主にKotlinプログラミング言語、Spring Boot、Android、React.js / Node.js、関数型プログラミング、プログラミングのアイデアなどの関連トピックを共有および交換しています。

世界が騒々しいほど、より平和な思考が必要です。

1665件のオリジナル記事が公開されました 1067件の賞賛 750,000回

おすすめ

転載: blog.csdn.net/universsky2015/article/details/105531347