Bloom-Filter (布隆过滤器)

我们今天学习一种海量数据的查询过滤算法,就是判断一个元素是否在一个集合中,我们平常的算法,肯定就是遍历比较了,这样对小量数据可以,但对海量数据肯定是不适用的,就算是二叉树其时间复杂度也是O(logn),所以有个叫Burton Bloom 在1970年提出了Bloom Filter 算法,其时间复杂度是O(1)。

   但是这个算法不能保证100%正确,所以不适合那些“零错误”的应用场合。我们现在就简单介绍下其思想:

我们用一个哈希算法(Hash函数)将一个集合元素映射到一个二进制位数组(位数组)中的某一位。如果该位已经被置为1,那么表示该元素已经存在。为了减少hash冲突问题,所以引用了多个哈希函数,如果通过其中的一个Hash值得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。


1。首先要有表示集合的数据结构,在Bloom-Filter中,使用的是一个二进制数组(位数组);

 假设Bloom Filter使用一个m比特的数组来保存信息,初始状态时,Bloom Filter是一个包含m位的位数组,每一位都置为0,即Bloom-Filter整个数组的元素都设置为0

初始状态的bloomfilter

2。如果我们现在有一个集合S={x1, x2,…,xn},包含n个元素。现在我们需要k个hash函数对n个元素进行计算并映射到我们的位数组中。这个计算k的公式为:

 k = ln2· (m/n)

m: bit数组的宽度(bit数)

n:集合中元素的个数

k:使用的hash函数的个数

最优的哈希函数的个数 = ln2*(数组大小/元素个数)

当我们往Bloom Filter中增加任意一个元素x时候,我们使用k个哈希函数得到k个哈希值,然后将数组中对应的比特位设置为1。即第i个哈希函数映射的位置hashi(x)就会被置为1(1≤i≤k)。注意,如果一个位置多次被置为1,那么只有第一次会起作用,后面几次将没有任何效果。在下图中,k=3,且有两个哈希函数选中同一个位置(从左边数第五位,即第二个“1“处)。 

x1和x2哈希后有相同的位置

3。现在我们就可以判断一个元素是否在这个集合中了,比如判断y是否在这个集合中,我们只需要对y使用k个哈希函数得到k个哈希值,如果所有hashi(y)的位置都是1(1≤i≤k),即k个位置都被设置为1了,那么我们就认为y是集合中的元素,否则就认为y不是集合中的元素。下图中y1就不是集合中的元素(因为y1有一处指向了“0”位)。y2或者属于这个集合。或者刚好是一个false positive(假阳性)。

有个hash计算y1的位置是0,表示不在集合中

显然这 个判断并不保证查找的结果是100%正确的,这个假阳性false positiver比率的计算公式为:

False Positive的比率


我们现在用一段代码来看下实现原理,是用php实现的。

BloomFilter类

class BloomFilter {     

protected $m;  //bit数组的宽度(bit数)

protected $k;  //使用的hash函数的个数

protected $n;  //集合中元素的个数

protected $bitset;  //二进制数组

//构造函数初始化

public function __construct($m, $k) {        

  $this->m = $m;      

   $this->k = $k;       

   $this->n = 0;        

  $this->bitset = array_fill(0, $this->m - 1, false);    //初始化二进制数组全部为0

  }  

//计算最优的hash函数个数:当hash函数个数k=(ln2)*(m/n)时错误率最小 

 public static function getHashCount($m, $n) {        

      return ceil(($m / $n) * log(2));      

 } 

//添加一个元素,并计算集合长度n

public function add($key) {     

     if (is_array($key)) {//如果key是一个数组,则回调添加其中的元素

        foreach ($key as $k){        

          $this->add($k);             

         }           

          return;      

       }          

     $this->n++;          

     foreach ($this->getSlots($key) as $slot) {   //将计算得到的位置置为true

           $this->bitset[$slot] = true;      

      }     

 }  

//判断某个元素是否在集合中

public function contains($key) {     

     if (is_array($key)) {      //如果key是数组,则判断数组元素是否在集合中 

       foreach ($key as $k) {        

          if ($this>contains($k) == false) {       

               return false;         

         }           

     }              

      return true;    

     }         

   foreach ($this->getSlots($key) as $slot) {      //判断单个元素是否在集合中

        if ($this>bitset[$slot] == false) {     

             return false;           

        }       

   }       

     return true;     

  }  

//对key元素进行k次随机计算并返回其在二进制数组中的位置

 protected function getSlots($key) {  

    $slots = array();       

    $hash = self::getHashCode($key);         

    mt_srand($hash);      //用哈希值做随机数种子,这样既有一定随机性,对一样的值,也有确定性。

    for ($i = 0; $i < $this->k; $i++) {     

         $slots[] = mt_rand(0, $this->m - 1);      

     }            

    return $slots;    

  } 

/*使用CRC32产生一个32bit(位)的校验值。由于CRC32产生校验值时源数据块的每一bit(位)都会被计算,所以数据块中即使只有一位发生了变化,也会得到不同的CRC32值。*/ 

 protected static function getHashCode($string) {    

      return crc32($string);     

 } 

$items = array("first item", "second item", "third item");  //定义一个集合items

//定义一个BloomFilter对象并将集合元素添加进过滤器中.

$filter = new BloomFilter(100, BloomFilter::getHashCount(100, 3));  

$filter->add($items);  

//判断items1中的元素是否在items集合中

$items1 = array("firsttem", "seconditem", "thirditem"); 

 foreach ($items as $item) { 

  var_dump(($filter->contains($item)));

  }  

}

OK,对Bloom-filter的介绍就到这里,水平有限,更多内容,大家可以再看看其他的文章介绍。

本文参考:http://blog.csdn.net/hguisu/article/details/7866173(海量数据处理算法—Bloom Filter);

作者:区块链研习社比特币源码研读班,black

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容