實例學習Bloom Filter


0. 科普
1. 為什么需要Bloom Filter
2. 基本原理
3. 如何設計Bloom Filter
4. 實例操作
5. 擴展

 

0. 科普

      Bloom Filter是由Bloom在1970年提出的一種多哈希函數映射快速查找算法。通常應用在一些需要快速判斷某個元素是否屬於集合,但是並不嚴格要求100%正確的場合。

 

1. 為什么需要Bloom Filter

      舉例說明:假設有2000萬個url,現在判斷一個新的url是否在這2000萬個之中。可以有的思路:

  1. 將訪問過的URL保存到數據庫。
  2. 用HashSet將訪問過的URL保存起來。那只需接近O(1)的代價就可以查到一個URL是否被訪問過了。
  3. URL經過MD5等單向哈希后再保存到HashSet或數據庫。
  4. Bit-Map方法。建立一個BitSet,將每個URL經過一個哈希函數映射到某一位。

分析

思路1:當數據量很大時,查詢數據庫變得效率底下

思路2:太消耗內存,還得把字符串全部儲存起來

思路3:字符串經過MD5處理后有128個bit,比思路2省了很多空間

思路4:一個字符串僅用一位來表示,比思路3還節省空間

當然前提是會出現誤判(哈希后表示相同),為了繼承這么好的思路,同時減少誤判的情況,可以來個折衷:一個哈希函數生成一個位,用多個哈希函數生成多個位來存儲一個字符串。這樣比Bit-Map多用了些空間,但是減少了誤判率。

 

2. 基本原理

這樣把大量的字符串存起來。查找時,用同樣的哈希處理待查串,如果對應的各位上都為1,說明該字符串可能在這些字符串中,否則一定不在其中。

 

3. 如何設計Bloom Filter

如何降低誤判率是關鍵,這需要

  • 選取區分度高的哈希函數
  • 根據存儲數組、哈希函數個數、誤判率之間的關系,分配空間、個數

直接利用前人的結論:

其中f'是自己期望的誤判率,m是總共開辟的存儲空間位數,n是待存儲字符串的個數,k是哈希函數的個數,f是真正的誤判率。

 

4. 實例操作

需求:2000萬個已知url,100個待查url

設計

1. 設定誤判率為0.1, n=2000萬,計算

m = n * 1.44 * math.log(1/f)/math.log(2)=287014588
k = 0.693 * m / n= 10
f = (1 - math.exp(-1 * k * n / m)) ** k = 0.00101298781512

哈希函數的選取看這里

參考代碼(c++)

makefile

objects = main.o hash.o bloomfilter.o

main : $(objects)
    g++ -o main $(objects)

main.o : hash.h bloomfilter.h
bloomfilter.o : bloomfilter.h
hash.o : hash.h

clean:
    rm *.o main
View Code

main.cc

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <string>
#include "bloomfilter.h"
using namespace std;

const int MAXSIZE = 400000000; 
int main(int argc, char **argv)
{
    char *poolfile = argv[1];
    char *testfile = argv[2];
    cout << poolfile << endl;
    ifstream fpool(poolfile);
    ifstream ftest(testfile);
    if(!fpool)
    {
        cout << "cannot open the file:" << poolfile << endl;
        return 0;
    }
    if(!ftest)
    {
        cout << "cannot open the file:" << testfile << endl;
        return 0;
    }
    BloomFilter bf(MAXSIZE);
    bf.setBit(fpool);
    cout << "Store OK" << endl;
    bf.checkBit(ftest);
    cout << "Check OK" << endl;
    fpool.close();
    ftest.close();
}
View Code

bloomfilter.h

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include "hash.h"
using namespace std;
class BloomFilter
{
    public:
        BloomFilter(int size) : SIZE(size) { vec.resize(size); };
        void setBit(ifstream &f);
        void setBit(const string &s);
        void setBit(unsigned int count);

        bool checkBit(ifstream &f);
        bool checkBit(const string &s);
        bool checkBit(unsigned int count);
    private:
        vector<char> vec;
        const unsigned int SIZE;
};
View Code

bloomfilter.cc

#include "bloomfilter.h"

void BloomFilter::setBit(ifstream &f)
{
    string line;
    while(f >> line)
    {
        setBit(line);
    }
}

bool BloomFilter::checkBit(ifstream &f)
{
    string line;
    while(f >> line)
    {
        if(!checkBit(line))
            cout << line << endl;
    }
}

void BloomFilter::setBit(const string &s)
{
    unsigned int bitpos = 0;
    const char *str = s.c_str();
    int len = s.size();

    bitpos = RSHash(str, len);
    setBit(bitpos);
    bitpos = JSHash(str, len);
    setBit(bitpos);
    bitpos = PJWHash(str, len);
    setBit(bitpos);
    bitpos = ELFHash(str, len);
    setBit(bitpos);
    bitpos = BKDRHash(str, len);
    setBit(bitpos);
    bitpos = SDBMHash(str, len);
    setBit(bitpos);
    bitpos = DJBHash(str, len);
    setBit(bitpos);
    bitpos = DEKHash(str, len);
    setBit(bitpos);
    bitpos = BPHash(str, len);
    setBit(bitpos);
    bitpos = FNVHash(str, len);
    setBit(bitpos);
}

bool BloomFilter::checkBit(const string &s)
{
    unsigned int bitpos = 0;
    const char *str = s.c_str();
    int len = s.size();
    bool rev = true;

    bitpos = RSHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = JSHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = PJWHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = ELFHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = BKDRHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = SDBMHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = DJBHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = DEKHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = BPHash(str, len);
    rev &= checkBit(bitpos);
    bitpos = FNVHash(str, len);
    rev &= checkBit(bitpos);
    return rev;
}

void BloomFilter::setBit(unsigned int count)
{
    count = count % (SIZE * 8);
    vec[count / 8] |= (1 << (count % 8));
}

bool BloomFilter::checkBit(unsigned int count)
{
    count = count % (SIZE * 8);
    return vec[count / 8] &= (1 << (count % 8));
}
View Code

hash.h

unsigned int RSHash(const char* str, unsigned int len);
unsigned int JSHash(const char* str, unsigned int len);
unsigned int PJWHash(const char* str, unsigned int len);
unsigned int ELFHash(const char* str, unsigned int len);
unsigned int BKDRHash(const char* str, unsigned int len);
unsigned int SDBMHash(const char* str, unsigned int len);
unsigned int DJBHash(const char* str, unsigned int len);
unsigned int DEKHash(const char* str, unsigned int len);
unsigned int BPHash(const char* str, unsigned int len);
unsigned int FNVHash(const char* str, unsigned int len);
View Code

hash.cc

#include "hash.h"
unsigned int RSHash(const char* str, unsigned int len)
{
   unsigned int b = 378551;
   unsigned int a = 63689;
   unsigned int hash = 0;
   unsigned int i = 0;

   for(i=0; i<len; str++, i++)
   {
      hash = hash*a + (*str);
      a = a*b;
   }

   return hash;
}
/* End Of RS Hash Function */


unsigned int JSHash(const char* str, unsigned int len)
{
   unsigned int hash = 1315423911;
   unsigned int i    = 0;

   for(i=0; i<len; str++, i++)
   {
      hash ^= ((hash<<5) + (*str) + (hash>>2));
   }

   return hash;
}
/* End Of JS Hash Function */


unsigned int PJWHash(const char* str, unsigned int len)
{
   const unsigned int BitsInUnsignedInt = (unsigned int)(sizeof(unsigned int) * 8);
   const unsigned int ThreeQuarters = (unsigned int)((BitsInUnsignedInt  * 3) / 4);
   const unsigned int OneEighth = (unsigned int)(BitsInUnsignedInt / 8);
   const unsigned int HighBits = (unsigned int)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);
   unsigned int hash = 0;
   unsigned int test = 0;
   unsigned int i = 0;

   for(i=0;i<len; str++, i++)
   {
      hash = (hash<<OneEighth) + (*str);

      if((test = hash & HighBits)  != 0)
      {
         hash = ((hash ^(test >> ThreeQuarters)) & (~HighBits));
      }
   }

   return hash;
}
/* End Of  P. J. Weinberger Hash Function */


unsigned int ELFHash(const char* str, unsigned int len)
{
   unsigned int hash = 0;
   unsigned int x    = 0;
   unsigned int i    = 0;

   for(i = 0; i < len; str++, i++)
   {
      hash = (hash << 4) + (*str);
      if((x = hash & 0xF0000000L) != 0)
      {
         hash ^= (x >> 24);
      }
      hash &= ~x;
   }

   return hash;
}
/* End Of ELF Hash Function */


unsigned int BKDRHash(const char* str, unsigned int len)
{
   unsigned int seed = 131; /* 31 131 1313 13131 131313 etc.. */
   unsigned int hash = 0;
   unsigned int i    = 0;

   for(i = 0; i < len; str++, i++)
   {
      hash = (hash * seed) + (*str);
   }

   return hash;
}
/* End Of BKDR Hash Function */


unsigned int SDBMHash(const char* str, unsigned int len)
{
   unsigned int hash = 0;
   unsigned int i    = 0;

   for(i = 0; i < len; str++, i++)
   {
      hash = (*str) + (hash << 6) + (hash << 16) - hash;
   }

   return hash;
}
/* End Of SDBM Hash Function */


unsigned int DJBHash(const char* str, unsigned int len)
{
   unsigned int hash = 5381;
   unsigned int i    = 0;

   for(i = 0; i < len; str++, i++)
   {
      hash = ((hash << 5) + hash) + (*str);
   }

   return hash;
}
/* End Of DJB Hash Function */


unsigned int DEKHash(const char* str, unsigned int len)
{
   unsigned int hash = len;
   unsigned int i    = 0;

   for(i = 0; i < len; str++, i++)
   {
      hash = ((hash << 5) ^ (hash >> 27)) ^ (*str);
   }
   return hash;
}
/* End Of DEK Hash Function */


unsigned int BPHash(const char* str, unsigned int len)
{
   unsigned int hash = 0;
   unsigned int i    = 0;
   for(i = 0; i < len; str++, i++)
   {
      hash = hash << 7 ^ (*str);
   }

   return hash;
}
/* End Of BP Hash Function */


unsigned int FNVHash(const char* str, unsigned int len)
{
   const unsigned int fnv_prime = 0x811C9DC5;
   unsigned int hash      = 0;
   unsigned int i         = 0;

   for(i = 0; i < len; str++, i++)
   {
      hash *= fnv_prime;
      hash ^= (*str);
   }

   return hash;
}
/* End Of FNV Hash Function */
View Code

數據下載:http://pan.baidu.com/s/1hqBTks0

Github 地址:https://github.com/jihite/Bloom-Filter

 

5. 擴展

如何刪除存儲數組中的元素?

思路:把存儲數組的每一個元素擴展一下(原來是1b)用來存儲該位置被置1的次數。存儲是,計數次數加一;刪除的時候,計數次數減一。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM