bitmap与bloomfilter(比较清晰的讲解)

转载地址:http://blog.csdn.net/gugemichael/article/details/8013150

今天,有个同学向我咨询大数据的一些面试题,其中一类比较有代表性比如判断是否在集合内,比如10个url,判断一个url是否在集合内,还比如有个1~100万个连续无序数字,随机取出里面的N个,求这N个数字等等。这类问题都需要一个大的数据集合,而且每个数据单元都很小,比如一个int 。很大程度上,这类问题可以用Bitmap或者Bloomfilter来做,基本思想就是开辟一块大内存,然后利用一个byte里的8个bit来实现按位标记元素。因为地址空间都是连续的,所以查找都是O(1)的。这里需要说的是,BloomFilter判断属不属于集合,在理论上是存在误判的,如果要求数据100%正确,则不要使用BloomFilter。

       进入正题,Bitmap正如其名,就是一块内存,内存是一个一个连续的位图,每一个位通过0、1代表一个元素的有无。比如数字为N的数字对应到Bitmap就是第N/8个byte的字节,和第N%8个01位,这么映射。所以通过检测对应的bit位即可知道数据在不在集合内,而且能保证正确。直接上代码 :

[cpp]  view plain  copy
 print ?
  1. #include <cstdlib>  
  2. #include <iostream>  
  3. #include <algorithm>  
  4. #include <vector>  
  5. #include <stddef.h>  
  6.   
  7. #include <memory.h>  
  8.   
  9. #define BYTES 12500  
  10.   
  11. int main()  
  12. {  
  13.     srand((unsigned int)time(NULL));      
  14.   
  15.     size_t total_numbers = 100000;  
  16.   
  17.     typedef std::vector<int> SetContainer;  
  18.     typedef std::vector<int>::iterator SetIterator;  
  19.   
  20.     SetContainer numbers;  
  21.     numbers.reserve(total_numbers);  
  22.   
  23.     int r1 = rand() % total_numbers;  
  24.     int r2 = r1 + 1000;  
  25.   
  26.     // generate total_numbers-2 numbers  
  27.     for(int i=0;i!=total_numbers;++i) {  
  28.         if (i!=r1 && i!= r2)  
  29.             numbers.push_back(i);  
  30.     }  
  31.   
  32.     std::cout<<"["<<numbers.size()<<"] insert ok";      
  33.     std::cin.get();  
  34.   
  35.     // shuffle  
  36.     std::random_shuffle(numbers.begin(),numbers.end());  
  37.   
  38.     unsigned char *bitmap = (unsigned char*)malloc(BYTES);  
  39.     memset(bitmap,0,BYTES);  
  40.     for (SetIterator itr=numbers.begin();itr!=numbers.end();++itr) {  
  41.         ptrdiff_t forward = (*itr) / 8;  
  42.         size_t offset = (*itr) % 8;  
  43.         bitmap[forward] |= (0x80UL >> offset);   
  44.     }  
  45.   
  46.     std::cout<<"Bitmap build ok";   
  47.     std::cin.get();  
  48.   
  49.     for (int j=0;j!=BYTES;++j) {  
  50.         if (bitmap[j]!=0xFF) {  
  51.             std::cout<<"FIND ";  
  52.             unsigned long num = j * 8;  
  53.             unsigned char check = bitmap[j];  
  54.             unsigned char bit = 0;  
  55.             while(bit!=8) {  
  56.                 if (0 == (check&(0x80UL>>bit)))  
  57.                     std::cout<<"["<<(num+bit)<<"] ";  
  58.                 bit++;  
  59.             }  
  60.             std::cout<<std::endl;  
  61.         }  
  62.     }  
  63.   
  64.     std::cout<<"DONE";  
  65.   
  66.     std::cin.get();  
  67.   
  68.     free(bitmap);  
  69.   
  70.     return 0;  
  71. }  

      BloomFilter,是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,适合与比Bitmap更多量的数据,通过图片看一下方法流程 :
      1、初始化一块大内存用于存放01标志位:
       

      2、通过使用N个hash函数(N==3),对同一个值Hash多次哈希,然后同Bitmap一样映射到Bloomfilter中去,

        
 
      3、检测时,同样通过N次哈希,在映射的位中去找,并要保持映射的每一位都是1的情况下,即检测处包含关系。正如前面说的,BloomFilter可能有误判,误判的几率取决于Hash函数的个数,Hash函数冲撞的概率,以及Bloomfilter开开辟的内存大小。Hash函数的个数要取个合适的值,大了会造成效率问题,少了可能误判高,理论5~10个之间,工程里用3~5个,具体多少可以视需求而定。

       

        代码 :

    

[cpp]  view plain  copy
 print ?
  1. #include <cstdlib>  
  2. #include <cstdio>  
  3. #include <iostream>  
  4. #include <algorithm>  
  5. #include <vector>  
  6. #include <stddef.h>  
  7.   
  8. #include <memory.h>  
  9.   
  10. #define BLOOM (1024UL*1024UL*1024UL) // 1G  
  11. #define HASH_RESULT 3  
  12.   
  13. typedef unsigned char BloomFilter;  
  14.   
  15. typedef struct __hash_result {  
  16.     size_t N;   // how many result  
  17.     size_t result[0];  
  18. }HashResult;  
  19.   
  20. /* Brian Kernighan & Dennis Ritchie hashfunction , used in Java */  
  21. size_t BKDR_hash(const char* str)    
  22. {    
  23.     register size_t hash = 0;    
  24.     while (size_t ch = (size_t)*str++)  {           
  25.         hash = hash * 131 + ch;   
  26.     }  
  27.     return hash;  
  28. }  
  29.   
  30. /* Unix System Hashfunction , also used in Microsoft's hash_map */  
  31. size_t FNV_hash(const char* str)    
  32. {    
  33.     if(!*str)  
  34.         return 0;    
  35.     register size_t hash = 2166136261;    
  36.     while (size_t ch = (size_t)*str++) {    
  37.         hash *= 16777619;    
  38.         hash ^= ch;    
  39.     }    
  40.     return hash;    
  41. }    
  42.   
  43. /* Donald Knuth Hashfunction , presented in book <Art of Computer Programming> */  
  44. size_t DEK_hash(const char* str)    
  45. {    
  46.     if(!*str)    
  47.         return 0;    
  48.     register size_t hash = 1315423911;    
  49.     while (size_t ch = (size_t)*str++)  {    
  50.         hash = ((hash << 5) ^ (hash >> 27)) ^ ch;    
  51.     }    
  52.     return hash;    
  53. }    
  54.   
  55. typedef size_t (*HASH_FUNC)(const char*);  
  56.   
  57. HASH_FUNC HASH[] = {  
  58.     BKDR_hash,FNV_hash,DEK_hash  
  59. };  
  60.   
  61.   
  62. void bloom_filter_mark(BloomFilter* bf, const char* v)  
  63. {  
  64.     HashResult *hr = (HashResult*)calloc(1,sizeof(HashResult)+(sizeof(size_t)*HASH_RESULT));  
  65.   
  66.     for (int i=0;i!=HASH_RESULT;++i) {  
  67.         hr->result[i] = (HASH[i](v)) % BLOOM;  
  68.         // set the binary bit to 1  
  69.         bf[hr->result[i]/8] |= 0x80UL >> (hr->result[i]%8);  
  70.         //printf("**%lu|hash-%d[%lu]|offset[%X]\n",HASH[i](v),i,hr->result[i],bf[hr->result[i]/8]);  
  71.     }  
  72.   
  73.     free(hr);  
  74. }  
  75.   
  76. bool bloom_filter_check(BloomFilter* bf, const char* v)  
  77. {  
  78.     HashResult *hr = (HashResult*)calloc(1,sizeof(HashResult)+(sizeof(size_t)*HASH_RESULT));  
  79.   
  80.     size_t in = HASH_RESULT;  
  81.     for (int i=0;i!=HASH_RESULT;++i) {  
  82.         hr->result[i] = HASH[i](v) % BLOOM;  
  83.         //printf("**%lu|%X\n",hr->result[i],bf[hr->result[i]/8]);  
  84.         // check this bit is "1" or not  
  85.         if (bf[hr->result[i]/8] & (0x80UL >> (hr->result[i]%8)))  
  86.             in--;  
  87.     }  
  88.   
  89.     free(hr);  
  90.     return in == 0;  
  91.   
  92. }  
  93.   
  94. int main()  
  95. {  
  96. //  std::cout<<BKDR_hash("0")<<std::endl;  
  97. //  std::cout<<DEK_hash("0")<<std::endl;  
  98. //  std::cout<<FNV_hash("0")<<std::endl;  
  99.   
  100.     BloomFilter* bloom = new (std::nothrow) BloomFilter[BLOOM];  
  101.     if (NULL == bloom)  
  102.         printf("No Space to build BloomFilter\n"),exit(0);  
  103.   
  104.     printf("BloomFilter Calloc Memory Ok\n");  
  105.   
  106.     for(int i=0;i!=1000000;i++) {  
  107.         char buf[16] = {0};  
  108.         sprintf(buf,"%d",i);  
  109.         bloom_filter_mark(bloom,buf);  
  110.     }  
  111.     printf("BloomFilter Build Ok\n");  
  112.   
  113.     for(int i=999995;i!=1000010;i++) {  
  114.         char buf[16] = {0};  
  115.         sprintf(buf,"%d",i);  
  116.         if (bloom_filter_check(bloom,buf))  
  117.             printf("[FOUND] %d\n",i);  
  118.     }  
  119.   
  120.     delete bloom;  
  121.   
  122.     return 0;  
  123. }  


         以上就是解决大数据判断在不在集合的通用方法,特定场景可以做一些优化。大家可以思考一下,本文开头的10个数字取出两个的问题,如果不用bitmap或者bloomfilter,可以怎么解决?引导一下,可以把所有数字乘起来和加起来,再用10000的阶乘和总和去减一个,就得到两个方程 x+y = M; x*y= N;不过这需要用string大整数阶乘的问题,还有比较耗内存,大家可以思议考一下更好的。。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值