Counting Bloom Filterを試す
はじめに
悩み多き年頃なので、進捗ダメです。
KVS見てるときに出てきた、次元圧縮っぽさがあるBloom Filterを試してみる。
Bloom Filterとは
コード
文字列を要素に、Counting Bloom Filterで要素の追加・検索を試してみる。
#include <iostream> #include <vector> #include <cmath> class CountingBloomFilterForString { int M; //配列のサイズ int N; //現在登録されている数 std::vector<int> base; //ハッシュ関数のBASE値 std::vector<int> bit; //保存用の配列 //ハッシュ関数 unsigned long long hash(const std::string& str, int b){ unsigned long long ret = 0; for(size_t i=0; i<str.length(); i++){ ret = ret * b + (char)(str[i]); } return ret; } public: //M : 配列のサイズ //h_base : ハッシュ関数のBASE値 CountingBloomFilterForString(int M, const std::vector<int>& h_base): M(M),bit(M,0),base(h_base),N(0) {} //配列の状態をダンプ void dump_bit(){ int zero = 0, nonzero = 0; for(int i=0; i<bit.size(); i++){ std::cout << bit[i]; if(bit[i] > 0) nonzero++; else zero++; } std::cout << std::endl; std::cout << "zero: " << zero << std::endl; std::cout << "nonzero: " << nonzero << std::endl; } //要素を追加 void insert(const std::string& str){ for(int k=0; k<base.size(); k++){ unsigned long long h = hash(str, base[k]); bit[ h%M ]++; } N++; } //要素を削除 bool remove(const std::string& str){ if(!find(str)) return false; for(int k=0; k<base.size(); k++){ unsigned long long h = hash(str, base[k]); bit[ h%M ]--; } return true; } //要素を検索 bool find(const std::string& str){ for(int k=0; k<base.size(); k++){ unsigned long long h = hash(str, base[k]); if(bit[ h%M ] <= 0) return false; } return true; } //find()が間違って「ある」と判定する確率 double prob_fp(int n){ int K = base.size(); return pow(1.0-exp(-K*(double)n/M), K); } }; //乱数 // 注意: longではなくint(32bit)にすべき unsigned long xor128(){ static unsigned long x=123456789,y=362436069,z=521288629,w=88675123; unsigned long t; t=(x^(x<<11));x=y;y=z;z=w; return( w=(w^(w>>19))^(t^(t>>8)) ); } //適当な文字列を返す std::string get_random_text(){ std::string ret = ""; for(int i=0; i<10; i++){ ret += 'a' + xor128() % 26; } return ret; } int main(){ int M = 100000; //配列のサイズ int N = 10000; //入力文字列の数 std::vector<int> base; //ハッシュのBASE値 base.push_back(41); CountingBloomFilterForString bf(M, base); //ランダムな文字列を生成 std::vector<std::string> strs; for(int i=0; i<(int)(N); i++){ strs.push_back(get_random_text()); } //生成した文字列を追加 for(int i=0; i<strs.size(); i++){ std::cout << strs[i] << std::endl; bf.insert(strs[i]); } //追加された状態を表示 bf.dump_bit(); //追加した文字列がすべて問題なく入っているか確認 for(int i=0; i<strs.size(); i++){ if(bf.find(strs[i]) != true){ std::cout << "Error" << std::endl; } } //現在のFP率を出力 std::cout << bf.prob_fp(strs.size()) << std::endl; //関係ない文字列を検索してみる std::cout << (bf.find("cba123")?"find!":"none") << std::endl; return 0; }
結果
適当な文字列をN個入れたとき、(M,BASE)なBloomFilterの配列(ゼロと非ゼロの個数)、FP率、適当な別の文字列を検索したときの結果、を表示。
- M=100000, N=10000, BASE=41
... zero: 90479 nonzero: 9521 0.0951626 none
- M=100000, N=10000, BASE=41,57
... zero: 81899 nonzero: 18101 0.0328585 none
- M=10000, 10000, BASE=41
... zero: 3657 nonzero: 6343 0.632121 none
- M=10000, 10000, BASE=41,57
... zero: 1310 nonzero: 8690 0.747645 none
できてるっぽい。MとKでFP率の変化の仕方が変わる。
ハッシュ関数はどういうのがよいのだろうか。。。
参照
- Mitzenmacher et al., 確率と計算 乱択アルゴリズムと確率的解析, 共立出版
- http://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AB%E3%83%BC%E3%83%A0%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF
- http://www.slideshare.net/kumagi/bloom-filter-4178952
- http://www.gologo13.com/2012/09/03/bloom-filter-implemented-in-c/
- http://research.preferred.jp/2011/03/bloom-filter/
- http://d.hatena.ne.jp/takeda25/20110509/1304948935