哈希扩展之布隆过滤器

1. 布隆过滤器的引入

有这样一个题:判断一个元素是不是在其指定集合中?

我们一般的操作是将该集合先保存起来,再拿着指定元素与保存起来的集合中元素一一比较从而判断它是否存在。可是随着集合中元素的增加,我们需要的存储空间越来越大,检索的速度也越来越慢,这时我们想到了哈希表这种数据结构。哈希表通过一个哈希函数将一个元素映射成位阵列(Bit Array)中的一个点,这样我们只要查看该元素对应的点是否为1就可以知道这个集合中是否存在它。但是哈希表面临哈希冲突的问题,假设我们的位阵列长度为m个点,那么如果要将哈希冲突降低为1%,即在哈希表中保存n个元素需要满足:n/m<1%,则这个哈希表只能容纳m/100个元素。但是这样做的话,仅仅是提高了检索的速度,内存空间做不到有效存储,那么应该怎么解决呢?其实,我们可以利用多个Hash函数解决哈希冲突问题,如果有一个Hash函数计算出该元素不在指定集合中,则不是真的不存在;如果所有哈希函数均计算出该元素在指定集合中,则表示极有可能存在,这样一来就大大解决了哈希冲突问题。

而一般使用布隆过滤器是用来处理字符串是否存在在集合中问题,通过多个哈希函数计算出该字符串的多个哈希值,从而将该字符串对应的所有哈希值对应的bit位设置为1。利用这样的方式对字符串进行查找时,通过多个哈希表计算哈希值,并在哈希值对应的bit位中判断该bit位是否为1,若全为1则表示该字符串是存在的,若有一个哈希函数对应的哈希值的bit位为0则表示该字符串不存在。但是需要注意的是布隆过滤器不能进行删除操作,因为该字符串对应的bit位有可能是其他字符串也对应的bit位,故不可以进行删除操作。

2. 布隆过滤器的结构体定义

通过上面分析布隆过滤器的操作可以知道,布隆过滤器是将位图与多个哈希函数相结合实现的,故设计布隆过滤器的结构体类型时需要定义位图的结构体以及多个哈希函数。代码如下:

#include"Bitmap.h"                                                                                                       
//定义关于哈希函数指针
typedef BitMapType(*BloomHash)(const char* str);
//定义一个标识符用于表示哈希函数个数
#define BloomHashCount 2
typedef struct BloomFilter{
    BitMap bm; 
    BloomHash bloom_hash[BloomHashCount];
}BloomFilter;

3. 布隆过滤器的初始化与销毁

3.1 初始化

通过布隆过滤器的结构体定义知:初始化布隆过滤器即初始化位图以及多个哈希函数。代码如下所示:

//0.哈希函数
BitMapType HashFunc1(const char* str)
{                                                                                                                        
    BitMapType hash=0;
    BitMapType ch=0;
    while(ch=(BitMapType)*str++)
    {   
        hash=65599*hash+ch;
    }   
    return hash;
}
BitMapType HashFunc2(const char* str)
{
    BitMapType hash=0;
    BitMapType ch=0;
    while(ch=(BitMapType)*str++)
    {
        hash=32233*hash+ch;
    }
    return hash;
}
//1.初始化
void BloomFilterInit(BloomFilter* bf)
{
    //非法输入
    if(bf==NULL)
        return;
    //初始化位图
    BitmapInit(&bf->bm,10000);                                                                                           
    //初始化哈希函数数组
    bf->bloom_hash[0]=HashFunc1;
    bf->bloom_hash[1]=HashFunc2;
}

3.2 销毁

销毁布隆过滤器即将布隆过滤器重置为原始状态,故直接将位图销毁并将哈希函数指针置为NULL即可。代码如下:

//2.销毁
void BloomFilterDestroy(BloomFilter* bf)
{
    if(bf==NULL)
        return;
    BitmapDestroy(&bf->bm);
    bf->bloom_hash[0]=NULL;
    bf->bloom_hash[1]=NULL;
}

4. 布隆过滤器的插入操作

将字符串进行插入的步骤:(1)通过多个哈希函数计算出该字符串对应的所有的哈希值;(2)将该哈希值对应的bit位设置为1即可。

代码如下:

void BloomFilterInsert(BloomFilter* bf,const char* str)
{
    //非法输入
    if(bf==NULL||str==NULL)
        return;
    size_t i=0;
    for(i=0;i<BloomHashCount;i++)
    {
        BitMapType hash=bf->bloom_hash[i](str)%(bf->bm.capacity);
        BitmapSet(&bf->bm,hash);
    }
}
5. 布隆过滤器的查找操作

对指定字符串进行查找的步骤:(1)通过每个哈希函数计算出指定字符串的哈希值;(2)判断该哈希值对应的bit位是1还是0:若有某一个哈希函数计算出对应bit位为0则表示该字符串不存在;若所有哈希函数计算出的对应的bit位均为1则表示该字符串存在。

代码如下:

int BloomFilterIsExit(BloomFilter* bf,const char* str)
{
    //非法输入
    if(bf==NULL||str==NULL)
        return 0;
    size_t i=0;
    for(;i<BloomHashCount;i++)
    {
        size_t hash=bf->bloom_hash[i](str)%(bf->bm.capacity);
        int ret=BitmapTest(&(bf->bm),hash);
        if(ret==0)
        {
            return 0;
        }                                                                                                                
    }
    return 1;
}

以上就是关于处理字符串问题的布隆过滤器的基本操作!



猜你喜欢

转载自blog.csdn.net/tongxuexie/article/details/80494324