ALGORITHM VISUALIZER

ブルームフィルタ(Bloom Filter)

「確実にない」か「たぶんある」かを超高速に判定する確率的データ構造。偽陽性は許容するが偽陰性は絶対に起きない。

設定
ビット配列サイズ32
ハッシュ関数数3
挿入
検索
統計
32
ビット配列サイズ
3
ハッシュ関数数
0
挿入済みキー数
0
1のビット数
充填率0.0%
偽陽性確率0.00%
挿入済みキー
まだキーが挿入されていません
凡例
ビット = 0(空)
ビット = 1(セット済み)
検索ヒット(存在する)
検索ミス(存在しない)
偽陽性(誤検出)
操作ログ
操作なし
BIT ARRAYREADY
🔍
キーを挿入してブルームフィルタを体験しましょう
INSERT A KEY TO BEGIN
解説

📌
ブルームフィルタとは

ブルームフィルタは、1970年にBurton Howard Bloomが提案した確率的データ構造です。「ある要素が集合に含まれているか?」という問いに対し、「確実にない」「たぶんある」かを超高速・超省メモリで判定します。

最大の特徴は、偽陽性(False Positive)は起こりうるが、偽陰性(False Negative)は絶対に起こらないということです。つまり「ない」と答えたら本当にない。「ある」と答えたときだけ、まれに間違える可能性があります。

メモリ効率が極めて高く、1要素あたりわずか数ビットで管理できます。HashSetなら1要素数十〜数百バイト必要なのに対し、ブルームフィルタは数ビットで済みます。この圧倒的な省メモリ性が、大規模システムで採用される理由です。

どこで使われているか
🌐 Google Chrome
悪意あるURL(フィッシングサイト等)のローカル判定。数百万件のURL一覧をわずか数MBで保持。
🗄️ Apache Cassandra / HBase
ディスクアクセス前に「このSSTableにデータがあるか」を高速判定。不要なディスクI/Oを削減。
📱 Medium
ユーザーごとに既読記事を管理。数千〜数万件の記事IDをわずかなメモリで管理し、推薦から除外。
📧 メールサーバー
スパムフィルタでブラックリスト照合。迷惑メールアドレスの高速チェックに使用。

🎯
このツールの値は何を表している?

上のツールで挿入する「apple」「banana」などは任意の文字列キーです。ブルームフィルタは「このキーがセットに含まれているか?」を高速・省メモリで判定する確率的データ構造です。実際のシステムでは以下のようなデータがキーとして使われます。

🛡️"http://evil-site.com/phishing" → 悪意あるURL

Google Chromeのセーフブラウジング。数百万件の危険URLをブルームフィルタに登録し、ユーザーがアクセスするたびにO(1)でチェック。ブルームフィルタが「ない」と言えば100%安全(偽陰性なし)。「ある」と言った場合だけサーバーに問い合わせて精密判定する。全URLリストをダウンロードせずに数MBで判定できるのがポイント。

📧"spammer@evil.com" → スパムメールアドレス

メールサーバーがスパム送信元のメールアドレスを数百万件ブルームフィルタに登録。受信のたびにO(1)でチェック。偽陽性(正常なメールがスパム判定される)が稀に起きるが、確率は0.1%以下に設定可能。

📰"article-id-12345" → 既読記事のID

Mediumの「あなたへのおすすめ」。ユーザーが読んだ記事IDをブルームフィルタに登録し、おすすめ表示時に「この記事は読んだか?」をO(1)で判定。数千件の既読情報を数KBで保持できる。既読記事が再表示されるよりも、たまに未読記事がスキップされる方がマシ(偽陽性の許容)。

💾"row-key-abc" → データベースの行キー

Cassandra/HBaseのSSTファイル。ディスク上のデータファイルに目的のキーが存在するかをブルームフィルタで事前チェック。「ない」とわかればディスクI/Oを完全にスキップできる。数テラバイトのデータでも、不要なディスク読み込みを90%以上削減。

なぜ文字列でなくても良い?

ブルームフィルタの入力は最終的にハッシュ関数に通すだけなので、文字列に限らず整数・バイト列・オブジェクトなど何でもキーにできます。ハッシュインデックスと同様、ハッシュ関数が内部で文字列→整数の変換を行います。このツールでは文字列を使っていますが、実際には任意のデータ型が対象です。

ブルームフィルタのパラメータの意味

ビット配列のサイズ(m):大きいほど偽陽性率が下がるがメモリ消費が増える。
ハッシュ関数の数(k):多いほど精度が上がるが、ある点を超えると逆にビットが埋まりすぎて偽陽性が増える。
最適な関係:k = (m/n) × ln(2) ≈ 0.693 × m/n(nは要素数)

上のツールのスライダーでm(ビット配列サイズ)とk(ハッシュ関数数)を変えて、偽陽性率の変化を観察してみてください。

💡 tip:「"apple", "banana", "cherry" を挿入した後、"grape" や "melon"(挿入していない単語)を検索してみてください。ほとんどは "存在しない" と正しく判定されますが、まれに "おそらく存在する"(偽陽性)と誤判定されることがあります。ビット配列サイズを小さくすると偽陽性が起きやすくなります。」

⚙️
仕組み

1. ビット配列 + 複数ハッシュ関数

ブルームフィルタはm個のビット(0/1)からなる配列と、k個の独立したハッシュ関数で構成されます。初期状態ではすべてのビットが0です。

00010203040506070809bits初期状態:すべて0

2. 挿入の流れ

キーを挿入すると、k個のハッシュ関数がそれぞれビット位置を計算し、その位置のビットを1にします。例えば "apple" を3つのハッシュ関数でハッシュすると、位置2, 5, 8が得られ、それらを1にします。

"apple"h12h25h3800011203041506071809

3. 検索の流れ(偽陽性の仕組み)

検索時も同じk個のハッシュ関数でビット位置を計算します。すべてのビットが1なら「たぶんある」1つでも0なら「確実にない」と判定します。

偽陽性が起きる理由:異なるキーが同じビット位置を使うことがあります。例えば "apple" と "banana" を挿入した後に "grape" を検索すると、"grape" のハッシュ位置がたまたま "apple" と "banana" によって既に1になっていた位置と一致し、「たぶんある」と誤判定されることがあります。

挿入済: "apple"→2,5,8挿入済: "banana"→1,4,70110110110検索: "grape" → h1=1, h2=5, h3=7すべて1 → "たぶんある"と判定しかし実際には挿入されていない = 偽陽性!偽陽性は他のキーが偶然同じビットを埋めたときに起こる

特徴

  • 🎲確率的判定「確実にない」は100%正確。「たぶんある」は偽陽性の可能性がある。完全に正確ではないが、その代わりに超高速・超省メモリ。
  • 🚫偽陽性あり、偽陰性なし挿入していないキーを「ある」と誤判定することはあるが、挿入したキーを「ない」と誤判定することは絶対にない。この非対称性が強力。
  • 🗑️削除不可(通常のブルームフィルタ)ビットを0に戻すと他のキーの判定に影響するため、通常は削除できない。削除が必要な場合はCounting Bloom Filterを使う。
  • 💾超メモリ効率1要素あたり約10ビット(約1.2バイト)で1%の偽陽性率を実現。HashSetの数十分の1〜数百分の1のメモリで済む。
  • O(k) の挿入・検索k個のハッシュ関数を計算するだけ。要素数nに関係なく一定時間で完了。kは通常3〜7程度の小さな定数。

🛠️
ユースケース

🌐 悪意あるURL判定
Google Chromeはフィッシングサイトや悪意あるURLのリストをブルームフィルタとしてブラウザに同梱。URLにアクセスする前にローカルで高速チェックし、ヒットした場合のみサーバーに問い合わせる。数百万件のURLをわずか数MBで保持。
🗄️ DB不要クエリ回避
Apache CassandraやHBaseでは、各SSTable(データファイル)にブルームフィルタを持たせている。クエリ時に「このファイルに該当データがあるか」を高速判定し、存在しないファイルへの不要なディスクI/Oを回避。読み取り性能が劇的に向上。
📝 重複排除
WebクローラーがURLの重複訪問を回避するために使用。数億件のURLを少ないメモリで管理。メールシステムでの重複メッセージ検知にも使われる。
📦 キャッシュフィルタ
CDNやキャッシュシステムで「このオブジェクトはキャッシュにあるか」を高速判定。キャッシュミスの無駄な検索を回避し、レイテンシを短縮。Akamai等で採用。

📖
用語解説

偽陽性(False Positive)
実際には集合に存在しないのに「ある」と誤判定されること。例:病気でないのに検査で「陽性」と出るケースに似ている。ブルームフィルタでは、異なるキーが偶然同じビット位置を全て埋めてしまったときに発生する。ビット配列が小さいほど、またキーが多いほど発生しやすくなる。偽陽性確率 ≈ (1 - e^(-kn/m))^k(k=ハッシュ関数数, n=挿入数, m=ビット配列サイズ)で計算できる。
偽陰性(False Negative)
実際には集合に存在するのに「ない」と誤判定されること。ブルームフィルタでは偽陰性は絶対に起こらない。なぜなら、挿入時に必ず対応するビットを1にするため、挿入済みのキーのビットが0になることはないから。これがブルームフィルタの最も重要な保証である。
ビット配列(Bit Array)
0と1だけを格納するm個の配列。ブルームフィルタの実体はこれ1つだけ。初期値はすべて0。キーの挿入によりビットが0→1に変わる(1→0に戻すことはない)。サイズmが大きいほど偽陽性率が低くなるが、メモリ使用量は増える。最適なmは挿入予定数nに対して m ≈ -n*ln(p) / (ln2)^2(pは許容偽陽性率)で求められる。
ハッシュ関数群(Hash Functions)
k個の独立したハッシュ関数。各関数は入力キーからビット配列のインデックス(0〜m-1)を1つ出力する。関数ごとに異なるシード値を使い、独立した出力を生成する。最適なkは k = (m/n) * ln2 で求められる。kが少なすぎると偽陽性率が上がり、多すぎるとビットが早く埋まって逆に偽陽性率が上がる。
充填率(Fill Rate)
ビット配列中の1になっているビットの割合。充填率が高いほど偽陽性率が上がる。充填率が50%を超えると偽陽性が急増するため、それ以下に保つことが推奨される。上のビジュアライザで充填率バーが赤くなったら要注意。
最適パラメータ(m, k, n)
m = ビット配列サイズ、k = ハッシュ関数の数、n = 挿入する要素数。偽陽性率1%を目標にする場合、1要素あたり約9.6ビット(m/n ≈ 9.6)、ハッシュ関数は約7個(k ≈ 7)が最適。偽陽性率0.1%なら1要素あたり約14.4ビット、k ≈ 10が最適。

📋
手順

挿入(Insert)

  1. キー(例:"apple")を受け取る
  2. k個のハッシュ関数を適用し、k個のビット位置を計算する(例:h1("apple")=2, h2("apple")=5, h3("apple")=8)
  3. 計算された各ビット位置を1にセットする(すでに1なら変化なし)
  4. 完了。キー自体は保存しない(ビット配列のみ更新)

検索(Lookup)

  1. キー(例:"apple")を受け取る
  2. 挿入時と同じk個のハッシュ関数で、k個のビット位置を計算する
  3. 計算された各ビット位置を確認する
  4. 1つでも0のビットがあれば → 「確実に存在しない」(100%正確)
  5. すべてのビットが1であれば → 「おそらく存在する」(偽陽性の可能性あり)

偽陽性が起きる具体例

ビット配列サイズ m=8、ハッシュ関数 k=2 の場合を考えます。

Step 1: "apple" を挿入 → h1=1, h2=4 → ビット位置1,4を1にセット

Step 2: "banana" を挿入 → h1=3, h2=6 → ビット位置3,6を1にセット

Step 3: "grape" を検索 → h1=3, h2=4 → ビット位置3(=1),4(=1) → すべて1!

結果: "grape" は挿入されていないのに「たぶんある」と判定される。位置3は "banana" が、位置4は "apple" が埋めていた。これが偽陽性。

⚖️
Bloom Filter vs HashSet vs B-Tree

100万件のデータを管理する場合のメモリ使用量の違いに注目してください。ブルームフィルタは圧倒的に少ないメモリで動作します。

特性Bloom FilterHashSetB-Tree
メモリ(100万件)~1.2 MB~64 MB~32 MB
検索速度O(k) 定数O(1) 平均O(log n)
挿入速度O(k) 定数O(1) 平均O(log n)
削除不可O(1)O(log n)
正確性偽陽性あり100%正確100%正確
要素の列挙不可可能可能(ソート順)
1要素あたり~10 bit~64 byte~32 byte
メモリの差は圧倒的です。100万件のURL(平均50バイト)をHashSetで管理すると約64MB必要ですが、ブルームフィルタなら偽陽性率1%でわずか約1.2MB。約50分の1のメモリで済みます。この差がGoogle Chromeのような大規模クライアントアプリで採用される理由です。ただし、「この要素を取り出す」「全要素を一覧する」といった操作はブルームフィルタではできないため、用途に応じて使い分ける必要があります。

関連コンテンツ