数据存在?-‘布隆过滤器’

布隆过滤器是一种能够在大量数据中判断数据是否存在的算法。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。在介绍‘布隆过滤器’之前,先介绍一下‘位图’的思想:

这里有这样一个问题:给40亿个没有排序、不重复的无符号整数,如何快速的判断一个数据是否在这40亿个数据之中?

--对于40亿个数据,如果我们将这40亿个数据都放入内存中,我们需要多大的存储空间呢?假设每个数据都是char类型的,这样消耗多少的空间?如果是int类型呢?或者是更多的数据呢?不难知道对于大量的数据,如果采用将数据放入内存中,这种方式是很不理智的。这里介绍一种方法—‘位图’。

位图:主要算法思想就是充分的利用bit位,假设数据都是int类型,每个int类型都占32个bit位。将一个int类型数据的32个bit用来表示32个数据是否存在, 0表示不存在,1表示存在(能够极大地缩小空间)。先计算出数据在哪一个int类型的空间中,然后计算在这个int类型的第几个bit位上,然后将此位置更改为1,表明这个位置上存在数据。

下面是‘位图’的实现:

class BitMap
{
public:
     BitMap(size_t size = 0)     //构造
      :_size(0)
     {
          _a.resize((size >> 5) + 1);       //resize开辟空间(int类型的个数),并进行初始化
     }
     
     void set(size_t x)    //插入数据
     {
          size_t index = x >> 5;     //index表示的是数据存在哪一个int类型的位置上
          size_t num = x % 32;     //num表示数据存在32bit的具体位置
          if (!(_a[index] & (1 << num))) //1<<num表示数据x的位置下标,&结果为0,表示此位置上没有数据
          {
               ++_size;
               _a[index] |= (1 << num);    //利用按位或关系将位置更改为1,表示此位置上现在存在数据
          }
     }
     
     void Reset(size_t x)      //删除数据
     {
          size_t index = x >> 5;
          size_t num = index % 32;
          _a[index] &= (~(1 << num));
          --_size;
     }
     
     bool Test(size_t x)   //判断数据是否在40亿数据中
     {
          size_t index = x >> 5;
          size_t num = x % 32;
          if (_a[index] & (1 << num))
               return true;
          return false;
     }
     
     size_t size()     //求数据的有效个数
     {
          return _size;
     }
     
     void Resize(size_t size)    //开辟空间
     {
          _a.resize((size >> 5) + 1);
     }
     
protected:
     vector<size_t> _a;
     size_t _size;
};

‘布隆过滤器’也是利用位图的思想,它有一个m个比特个数的空间,每一个bit位都初始化为0,通过k种不同的hash函数,每个函数都确定出元素所在的不同位置,将这k个位置的bit位置为1,则将这个元素添加到m个bit的空间中。当需要对数据进行查找时,将k中hash函数得到的k个位置的bit位进行检查,若k个位置都为1,则数据存在,否则数据不存在。布隆过滤器是不允许进行删除数据的,因为那样会将k个位置置为0,可能会影响其他数据的存在性,从而存在错误。

下面是‘布隆过滤器’的实现:

//实现布隆过滤器
template <class K>
//使用搜索到的5种Hash函数
struct _HashFunc1
{
     size_t DJBHash(const char *str)
     {
          if (!*str)
               return 0;
          register size_t hash = 5381;
          while (size_t ch = (size_t)*str++)
          {
               hash += (hash << 5) + ch;
          }
          return hash;
     }
     
     size_t operator()(const K& str)
     {
          return DJBHash(str.c_str());
     }
};

template <class K>
struct _HashFunc2
{
     size_t SDBMHash(const char *str)
     {
          register size_t hash = 0;
          while (size_t ch = (size_t)*str++)
          {
               hash = 65599 * hash + ch;
          }
          return hash;
     }
     
     size_t operator()(const K& str)
     {
          return SDBMHash(str.c_str());
     }
};

template <class K>
struct _HashFunc3
{
     size_t RSHash(const char *str)
     {
          register size_t hash = 0;
          size_t magic = 63689;
          while (size_t ch = (size_t)*str++)
          {
               hash = hash * magic + ch;
               magic *= 378551;
          }
          return hash;
     }
     
     size_t operator()(const K& str)
     {
          return RSHash(str.c_str());
     }
};

template <class K>
struct _HashFunc4
{
     size_t APHash(const char *str)
     {
          register size_t hash = 0;
          size_t ch;
          for (long i = 0; ch = (size_t)*str++; i++)
          {
               if ((i & 1) == 0)
               {
                    hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
               }
               else
               {
                    hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
               }
          }
          return hash;
     }
     
     size_t operator()(const K& str)
     {
          return APHash(str.c_str());
     }
};

template <class K>
struct _HashFunc5
{
     size_t JSHash(const char *str)
     {
          if (!*str)
               return 0;
          register size_t hash = 1315423911;
          while (size_t ch = (size_t)*str++)
          {
               hash ^= ((hash << 5) + ch + (hash >> 2));
          }
          return hash;
     }
     size_t operator()(const K& str)
     {
          return JSHash(str.c_str());
     }
};

size_t GetPrimeSize(size_t size) //求大于等于size的最小素数
{
     static const int _prime = 28;
     static const unsigned long _PrimeList[_prime] =
     {
          53ul, 97ul, 193ul, 389ul, 769ul,
          1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
          49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
          1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
          50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
          1610612741ul, 3221225473ul, 4294967291ul
     };
     
     for (size_t i = 0; i < _prime; i++)
     {
          if (_PrimeList[i] >= size)
          {
               return _PrimeList[i];
          }
     }
     return _PrimeList[_prime - 1];
}

template <class K = string, 
          class HashFunc1 = _HashFunc1<K>,
          class HashFunc2 = _HashFunc2<K>,
          class HashFunc3 = _HashFunc3<K>,
          class HashFunc4 = _HashFunc4<K>,
          class HashFunc5 = _HashFunc5<K>>
class BloomFilter
{
public:
     BloomFilter(size_t size = 0)    //构造
     {
          _capacity = GetPrimeSize(size);
          _bm.Resize(_capacity);
     }
     
     void set(const K& key)
     {
          size_t index1 = HashFunc1()(key);
          size_t index2 = HashFunc2()(key);
          size_t index3 = HashFunc3()(key);
          size_t index4 = HashFunc4()(key);
          size_t index5 = HashFunc5()(key);
          
          _bm.set((index1) % _capacity);
          _bm.set((index2) % _capacity);
          _bm.set((index3) % _capacity);
          _bm.set((index4) % _capacity);
          _bm.set((index5) % _capacity);
     }
     
     bool Test(const K& key)    //测试数据是否存在
     {
          size_t index1 = HashFunc1()(key);
          if (!_bm.Test((index1) % _capacity))
          {
               return false;
          }
          
          size_t index2 = HashFunc2()(key);
          if (!_bm.Test((index2) % _capacity))
          {
               return false;
          }
          
          size_t index3 = HashFunc3()(key);
          if (!_bm.Test((index3) % _capacity))
          {
               return false;
          }
          
          size_t index4 = HashFunc4()(key);
          if (!_bm.Test((index4) % _capacity))
          {
               return false;
          }
          
          size_t index5 = HashFunc5()(key);
          if (!_bm.Test((index5) % _capacity))
          {
               return false;
          }
          return true;
     }
     
protected:
     BitMap _bm;
     size_t _capacity;
};
时间: 2024-12-05 14:39:08

数据存在?-‘布隆过滤器’的相关文章

布隆过滤器相关知识

最近看流式系统的时候有提到Exactly-Once 策略 可以使用布隆过滤器(Bloom Filter) 优化, 所以今天来整理一下与其相关的知识 (非科班, 底子比较薄). 应用原理: Bloom Filter can return false positives but never false negatives. 布隆过滤器能判断数据是否一定不存在, 而无法判断数据是否一定存在. 因此它可以用于对数据进行查重, 从而保证 Exactly-Once 的策略 什么是布隆过滤器? 他它本身是一个

大数据下-巧用位数组排序和判重及布隆过滤器的简单应用

技巧一:对无重复的数据集排序 对于给定的数据集,(2,4,1,12,9,7,6)如何对它排序? 第一种方式.使用最基本的冒泡,快排.基数排序等,最小时间复杂度 0(nlogn). 第二种方式.使用位数组排序算法. 对于数据集排序,相信大多数都会在第一时间便能想起来,而对于方法二,就需要一定的思考了. 对于我们给定的数据集,最大数值为 12,那么我们可以开辟一个长度为 12 的字节数组,如下图,都初始化为0, 然后读取数据,例如,读取到 2 的时候,就将下标为 1 的位置为 1,即 a[2-1]

[算法系列之十]大数据量处理利器:布隆过滤器

[引言] 在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中.比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断 它是否在已知的字典中):在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上:在网络爬虫里,一个网址是否被访问过等等.最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新 元素时,将它和集合中的元素直接比较即可.一般来讲,计算机中的集合是用哈希表(hash table)来存储的.它的好处是快速准确,缺点是费存储空间.当集合比较小时,这个问题

从另一个角度看大数据量处理利器 布隆过滤器

思路:从简单的排序谈到BitMap算法,再谈到数据去重问题,谈到大数据量处理利器:布隆过滤器. 情景1:对无重复的数据进行排序 @给定数据(2,4,1,12,9,7,6)如何对它排序? 方法1:基本的排序方法包括冒泡,快排等. 方法2:使用BitMap算法 方法1就不介绍了,方法2中所谓的BitMap是一个位数组,跟平时使用的数组的唯一差别在于操作的是位. 首先是开辟2个字节大小的位数组,长度为16(该长度由上述数据中最大的数字12决定的)如图         然后,读取数据,2存放在位数组中下

布隆过滤器:高效、大概的判断数据是否存在

1      什么是布隆过滤器 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,或者说“判断一个元素是否存在一个集合中”,比如: 字处理软件中,需要检查一个英语单词是否拼写正确 在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上 在网络爬虫里,一个网址是否被访问过 yahoo, gmail等邮箱垃圾邮件过滤功能 相比于传统的 List.Set.Map 等

布隆过滤器(Bloom Filter)的原理和实现

什么情况下需要布隆过滤器? 先来看几个比较常见的例子 字处理软件中,需要检查一个英语单词是否拼写正确 在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上 在网络爬虫里,一个网址是否被访问过 yahoo, gmail等邮箱垃圾邮件过滤功能 这几个例子有一个共同的特点: 如何判断一个元素是否存在一个集合中? 常规思路 数组 链表 树.平衡二叉树.Trie Map (红黑树) 哈希表 虽然上面描述的这几种数据结构配合常见的排序.二分搜索可以快速高效的处理绝大部分判断元素是否存在集合中的需求.但是当集合里

[转载] 布隆过滤器(Bloom Filter)详解

转载自http://www.cnblogs.com/haippy/archive/2012/07/13/2590351.html   布隆过滤器[1](Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的.它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中.它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率(假正例False positives,即Bloom Filter报告某一

布隆过滤器的简易实现

布隆过滤器(Bloom Filter): 是由布隆(Burton Howard Bloom)提出的.它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器用于检索一个元素是否在一个集合中.底层是利用哈希表来实现的,它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit Array)中的一个点.这样一来,我们只要看看这个点是不是 1 就知道可以集合中有没有它了.这就是布隆过滤器的基本思想. 优点:空间效率和查询时间相比于其他数据结构有很大的优势 缺点:有一定的误识别率,删除困难

网络爬虫:URL去重策略之布隆过滤器(BloomFilter)的使用

前言: 最近被网络爬虫中的去重策略所困扰.使用一些其他的"理想"的去重策略,不过在运行过程中总是会不太听话.不过当我发现了BloomFilter这个东西的时候,的确,这里是我目前找到的最靠谱的一种方法. 如果,你说URL去重嘛,有什么难的.那么你可以看完下面的一些问题再说这句话. 关于BloomFilter: Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员.如果检测