哈希扩展---布隆过滤器(bloom filter)
1>认识布隆过滤器:
可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。
使用多个Hash,如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的。
布隆过滤器的应用:网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速(比如基于key-value的存储系统)
注意:对于布隆过滤器来说,存在不一定存在,但不存在一定不存在。
2>布隆过滤器实现:
BitMap.h
typedef struct BitMap { int * Bit_bset;//位的集合 int _capacity; int _size;//有效bit位的个数 }BitMap; void BitMapInit(BitMap * bmp,int capacity); void BitMapSet_zero(BitMap * bmp,int pos); void BitMapSet_one(BitMap *bmp,int pos); int BitMapTest(BitMap *bmp, int pos);//测试bit位为1还是为0; int BitMapSize(BitMap *bmp); int BitMapCount(BitMap *bmp); void BitMapDestroy(BitMap *bmp);
BitMap.c
#include "BitMap.h" #include<stdlib.h> #include<assert.h> #include<malloc.h> void BitMapInit(BitMap * bmp, int total_Bit){ assert(bmp); //total_Bit代表总共的bit位数 bmp->_capacity = (total_Bit >> 5 )+ 1;//代表需要的整形空间的个数 bmp->Bit_bset = calloc(bmp->_capacity, sizeof(int));//void *calloc(空间的个数,每个空间的字节数); bmp->_size = total_Bit;//有效比特位的个数 } //将pos的位置0; void BitMapSet_zero(BitMap * bmp, int pos){ assert(bmp); int pos_Byte = pos >> 5; int pos_Bit = pos % 32; if (pos > bmp->_size) return; bmp->Bit_bset[pos_Byte] = bmp->Bit_bset[pos_Byte] &( ~(1 << pos)); } //将pos的位置1; void BitMapSet_one(BitMap *bmp, int pos){ assert(bmp); int pos_Byte = pos >> 5; int pos_Bit = pos % 32; if (pos > bmp->_size) return; bmp->Bit_bset[pos_Byte] = bmp->Bit_bset[pos_Byte] |(1 << pos); } int BitMapTest(BitMap *bmp, int pos)//测试bit位为1还是为0; { assert(bmp); int pos_Byte = pos >> 5; int pos_Bit = pos % 32; if (pos > bmp->_size) return; return bmp->Bit_bset[pos_Byte] & (1 << pos); } int BitMapSize(BitMap *bmp) { assert(bmp); return bmp->_size; } //统计bit位上1的个数 int BitMapCount(BitMap *bmp){ int i = 0; int count = 0; assert(bmp); const char * bit1Count = "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4"; for ( i = 0; i <bmp->_capacity; i++) { int value = bmp->Bit_bset[i]; int j = 0; //一个整数占4个字节,我们按一个字节为单元查看bit位上1的个数 while (j<sizeof(bmp->Bit_bset[0])){ char c = value; //统计一个字节的底4位 count+=bit1Count[c&0x0f]; c >>= 4; //统计一个字节的高4位 count+=bit1Count[c&0x0f]; value >>= 8; j++; } } return count; } void BitMapDestroy(BitMap *bmp){ assert(bmp); if (bmp->Bit_bset) free(bmp->Bit_bset); bmp->_size = 0; bmp->_capacity = 0; }
BloomFilter.h
#include"BitMap.h" typedef char* DataType; typedef int(*pStrToInt)(char *str); typedef struct BloomFilter{ BitMap _bmp; int _size; pStrToInt _StrToInt_method[5]; }BloomFilter; void BloomFilterInit(BloomFilter *bf, int capacity, pStrToInt *_StrToInt); int StrToInt1(char *str); int StrToInt2(char *str); int StrToInt3(char *str); int StrToInt4(char *str); int StrToInt5(char *str); void BloomFilterInsert(BloomFilter *bf, DataType str); int IsIn(BloomFilter *bf, DataType str);
BloomFilter.c
#include "BloomFilter.h" #include<stdlib.h> #include<stddef.h> #include<assert.h> void BloomFilterInit(BloomFilter *bf, int size, pStrToInt _StrtoInt[5]){ int i = 0; assert(bf); BitMapInit(&bf->_bmp,size*5);//一个元素对应5个bit位 bf->_size = 0; for ( i = 0; i < 5; i++) { bf->_StrToInt_method[i] = _StrtoInt[i]; } } int StrToInt1(char *str){ unsigned int seed = 131; // 31 131 1313 13131 131313 unsigned int hash = 0; while (*str) { hash = hash * seed + (*str++); } return (hash & 0x7FFFFFFF); } int StrToInt2(char *str){ unsigned int hash = 0; int i; for (i = 0; *str; i++) { if ((i & 1) == 0) { hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3)); } else { hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5))); } } return (hash & 0x7FFFFFFF); } int StrToInt3(char *str){ unsigned int hash = 5381; while (*str) { hash += (hash << 5) + (*str++); } return (hash & 0x7FFFFFFF); } int StrToInt4(char *str){ unsigned int hash = 1315423911; while (*str) { hash ^= ((hash << 5) + (*str++) + (hash >> 2)); } return (hash & 0x7FFFFFFF); } int StrToInt5(char *str){ unsigned int hash = 0; unsigned int x = 0; while (*str) { hash = (hash << 4) + (*str++); if ((x = hash & 0xF0000000L) != 0) { hash ^= (x >> 24); hash &= ~x; } } return (hash & 0x7FFFFFFF); } void BloomFilterInsert(BloomFilter *bf,DataType str){ int addr1, addr2, addr3, addr4, addr5; int TotalBits = bf->_bmp._size; addr1 = bf->_StrToInt_method[0](str) % TotalBits; BitMapSet_one(&bf->_bmp, addr1); addr2 = bf->_StrToInt_method[1](str) % TotalBits; BitMapSet_one(&bf->_bmp, addr2); addr3 = bf->_StrToInt_method[2](str) % TotalBits; BitMapSet_one(&bf->_bmp, addr3); addr4 = bf->_StrToInt_method[3](str) % TotalBits; BitMapSet_one(&bf->_bmp, addr4); addr5 = bf->_StrToInt_method[4](str) % TotalBits; BitMapSet_one(&bf->_bmp, addr5); bf->_size++; } int IsIn(BloomFilter *bf, DataType str){ int addr1, addr2, addr3, addr4, addr5; int TotalBits = bf->_bmp._size; addr1 = bf->_StrToInt_method[0](str) % TotalBits; if (!BitMapTest(&bf->_bmp, addr1)) return 0; addr2 = bf->_StrToInt_method[1](str) % TotalBits; if (!BitMapTest(&bf->_bmp, addr2)) return 0; addr3 = bf->_StrToInt_method[2](str) % TotalBits; if (!BitMapTest(&bf->_bmp, addr3)) return 0; addr4 = bf->_StrToInt_method[3](str) % TotalBits; if (!BitMapTest(&bf->_bmp, addr4)) return 0; addr5 = bf->_StrToInt_method[4](str) % TotalBits; if (!BitMapTest(&bf->_bmp, addr5)) return 0; return 1; }