哈希扩展——位图&布隆过滤器

位图

来看一道题:

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。 

要快速判断一个数是否在一组数中:比较快的算法有:平衡搜索树、哈希表等。而这里如果直接建立一个哈希表的话,每个节点存一个数据,对于40亿的数据需要40亿个整型单元,这相当于100+G内存,显然这种方法不可行。
那么,我们便想到能不能用一个bit位来表示一个数存在或者不存在状态呢,
这,就是位图
这里写图片描述

位图的代码实现:
BitMap.h

#pragma once
#include <assert.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>

typedef struct BitMap
{
    size_t* _bits;
    size_t _range;
}BitMap;

void BitMapInit(BitMap* bm, size_t range);
void BitMapSet(BitMap* bm, size_t x);
void BitMapReset(BitMap* bm, size_t x);
int BitMapTest(BitMap* bm,size_t x);

void BitMapInit(BitMap* bm, size_t range)
{
    assert(bm);
    bm->_range = range;
    bm->_bits = (size_t*)malloc(sizeof(size_t)*((range>>5)+1));
    assert(bm->_bits);
    memset(bm->_bits, 0, sizeof(size_t)*((range >> 5) + 1));
}

void BitMapSet(BitMap* bm, size_t x)
{
    assert(bm);
    size_t index = (x >> 5);
    size_t num = x % 32;
    bm->_bits[index] |= (1 << num);

}

void BitMapReset(BitMap* bm, size_t x)
{
    assert(bm);
    size_t index = (x >> 5);
    size_t num = x % 32;
    bm->_bits[index] ^= (1 << num);
}

//存在返回0,不存在返回-1
int BitMapTest(BitMap* bm, size_t x)
{
    assert(bm);
    size_t index = (x >> 5);
    if (bm->_bits[index] & (1 << (x % 32)))
    {
        return 0;
    }
    else
        return -1;
}

void BitMapDestroy(BitMap* bm)
{
    free(bm->_bits);
}

布隆过滤器 (bloom filter)

如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。
Hash面临的问题就是冲突。假设Hash函数是良好的,如果我们的位阵列长度为m个点,那么如果我们想将冲突率降低到例如 1%, 这个散列表就只能容纳m / 100个元素。显然这就不叫空间效率了(Space-efficient)了。解决方法也简单,就是使用多个Hash,如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的。
这里写图片描述

布隆过滤器实现:
BloomFilter.h

#pragma once
#include "BitMap.h"

typedef char* KeyType;
typedef size_t(*HASH_FUNC)(KeyType str);
/// @brief BKDR Hash Function  
/// @detail 本 算法由于在Brian Kernighan与Dennis Ritchie的《The C Programming Language》一书被展
//示而得名,是一种简单快捷的hash算法,也是Java目前采用的字符串的Hash算法(累乘因子为31)。
size_t BKDRHash(KeyType str)
{
    size_t hash = 0;
    size_t ch;
    while ( ch = (size_t)*str++)
    {
        hash = hash * 131 + ch;   // 也可以乘以31、131、1313、13131、131313.         
    }
    return hash;
}
/// @brief SDBM Hash Function  
/// @detail 本算法是由于在开源项目SDBM(一种简单的数据库引擎)中被应用而得名
//,它与BKDRHash思想一致,只是种子不同而已。
size_t SDBMHash(KeyType str)
{
    size_t hash = 0;
    size_t ch;
    while (ch = (size_t)*str++)
    {
        hash = 65599 * hash + ch;
        //hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;  
    }
    return hash;
}

/// @brief RS Hash Function  
/// @detail 因Robert Sedgwicks在其《Algorithms in C》一书中展示而得名。  
size_t RSHash(KeyType str)
{
    size_t hash = 0;
    size_t magic = 63689;
    size_t ch;
    while (ch = (size_t)*str++)
    {
        hash = hash * magic + ch;
        magic *= 378551;
    }
    return hash;
}

typedef struct BloomFilter
{
    BitMap _bm;

    HASH_FUNC hashfunc1;
    HASH_FUNC hashfunc2;
    HASH_FUNC hashfunc3;
}BloomFilter;

void BloomFilterInit(BloomFilter* bf,size_t range)
{
    assert(bf);
    BitMapInit(&bf->_bm,range);

    bf->hashfunc1 = BKDRHash;
    bf->hashfunc2 = SDBMHash;
    bf->hashfunc3 = RSHash;
}
void BloomFilterSet(BloomFilter* bf, KeyType key)
{
    assert(bf);

    size_t range = bf->_bm._range;
    size_t hash1 = bf->hashfunc1(key);
    size_t hash2 = bf->hashfunc2(key);
    size_t hash3 = bf->hashfunc3(key);

    BitMapSet(&bf->_bm,hash1%range);
    BitMapSet(&bf->_bm,hash2%range);
    BitMapSet(&bf->_bm,hash3%range);
}
//存在返回0,不存在返回-1
int BloomFilterTest(BloomFilter* bf, KeyType key)
{
    assert(bf);

    size_t range = bf->_bm._range;

    if (BitMapTest(&bf->_bm, bf->hashfunc1(key) % range) == -1)
        return -1;

    if (BitMapTest(&bf->_bm, bf->hashfunc2(key) % range) == -1)
        return -1;

    if (BitMapTest(&bf->_bm, bf->hashfunc3(key) % range) == -1)
        return -1;
    return 0;
}

void BloomFilterDestroy(BloomFilter* bf)
{
    BitMapDestroy(&bf->_bm);
}

void TestBloomFilter()
{
    BloomFilter bf;
    BloomFilterInit(&bf, 10000);
    BloomFilterSet(&bf, "sort");

    BloomFilterSet(&bf, "insert");
    size_t x = 0;
    printf("%d\n",BloomFilterTest(&bf,"sort"));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值