【数据结构】位图BitMap、布隆过滤器的算法实现

原创 2016年05月30日 17:19:16

我们先给出之前我看过的腾讯公司的一道笔试题,引出位图BitMap。


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


这个问题怎么解决呢?


1)将40亿数据保存起来(保存在数组、链表、树中),再和该数判断是否相等。

那我们思考一下需要多少内存:

wKioL1c1du-CIGvrAABXJBMCxrU651.png

wKiom1c1de7B09ciAABXJBMCxrU543.png

2)借助位图BitMap解决。


位图(BitMap)

是用一个数组中的每个数据的每个二进制位表示一个数是否存在。1表示存在,0表示不存在。

相当于把数组分成很多块的空间,每一块是32个比特位。

原来32个比特位放一个数据,现在一个位就可以放一个数据。16GB/32=0.5GB=512MB。


 位图的实现:


#ifndef __BITMAP_H__
#define __BITMAP_H__
#include<iostream>
using namespace std;

#include<vector>

class BitMap
{
public:
    BitMap(size_t size = 0)
        :_size(0)
    {
        //_a开辟多一个空间,如size=36/32=1,需要两块空间才能放下
        _a.resize((size >> 5) + 1);
    }


    void Set(size_t x)
    {
        //size_t index = x / 32;
        size_t index = (x >> 5);
        size_t num = x % 32;

        //if(!(_a[index] & (1 << num))表示该二进制位不存在,则该位二进制置成1
        if (!(_a[index] & (1 << num)))
        {
            _a[index] |= (1 << num);
            ++_size;
        }
    }


    void Reset(size_t x)
    {
        //size_t index = x / 32;
        size_t index = x >> 5;
        size_t num = x % 32;

        //该位存在则将该位二进制置为0
        if (_a[index] & (1 << num))
        {
            _a[index] &= ~(1 << num);
            --_size;
        }
    }


    bool Test(size_t x)
    {
        //size_t index = x / 32;
        size_t index = x >> 5;
        size_t num = x % 32;
        if (_a[index] & (1 << num))
        {
            return true;
        }
        return false;
    }


    void Resize(size_t size)
    {
        _a.resize(size);
    }
private:
    vector<size_t> _a;
    size_t _size;
};

#endif //__BITMAP_H__



布隆过滤器(BloomFilter)

它是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中。那我们可以利用哈希函数计算出它具体的存放位置。

它的优点是空间效率和查询时间都远远超过一般的算法,将这40亿的数据内存由16GB变成500MB,可见其强大。

缺点是有一定的误识别率、不便于删除。布隆过滤器会出现:检测存在,而实际中却不存在。而不会出现:实际中不存在,而检测存在。


代码实现(仿函数实现,选取5个位图):


#define _CRT_SECURE_NO_WARNINGS 1
#ifndef __COMMON__
#define __COMMON__

size_t _GetnewSize(size_t _size)
{
    static const int _PrimeSize = 28;
    static const unsigned long _PrimeList[_PrimeSize] =
    {
        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 (int i = 0; i < _PrimeSize; i++)
    {
        if (_PrimeList[i]> _size)
        {
            return _PrimeList[i];
        }
    }
    return _PrimeList[_PrimeSize - 1];
}


template<class T>
struct __HashFunc1
{
    size_t BKDRHash(const char *str)
    {
        register size_t hash = 0;
        while (size_t ch = (size_t)*str++)
        {
            hash = hash * 131 + ch;   // 也可以乘以31、131、1313、13131、131313..  

        }
        return hash;
    }

    size_t operator()(const T& key)
    {
        return BKDRHash(key.c_str());
    }
};

template<class T>
struct __HashFunc2
{
    size_t SDBMHash(const char *str)
    {
        register size_t hash = 0;
        while (size_t ch = (size_t)*str++)
        {
            hash = 65599 * hash + ch;
            //hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;  
        }
        return hash;
    }

    size_t operator()(const T& key)
    {
        return SDBMHash(key.c_str());
    }
};


template<class T>
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 T& key)
    {
        return RSHash(key.c_str());
    }
};


template<class T>
struct __HashFunc4
{
    size_t JSHash(const char *str)
    {
        if (!*str)        // 这是由本人添加,以保证空字符串返回哈希值0  
            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 T& key)
    {
        return JSHash(key.c_str());
    }
};


template<class T>
struct __HashFunc5
{
    size_t DEKHash(const char* str)
    {
        if (!*str)        // 这是由本人添加,以保证空字符串返回哈希值0  
            return 0;
        register size_t hash = 1315423911;
        while (size_t ch = (size_t)*str++)
        {
            hash = ((hash << 5) ^ (hash >> 27)) ^ ch;
        }
        return hash;
    }

    size_t operator()(const T& key)
    {
        return DEKHash(key.c_str());
    }
};

#endif//__COMMON__



布隆过滤器代码实现(借助素数表获取下一个素数,选取合适的容量--》hash函数)::


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include <string>

#include "Common.h"
#include "BitMap.h"

template<class T=string,
        class HashFunc1 = __HashFunc1<T>,
        class HashFunc2 = __HashFunc2<T>,
        class HashFunc3 = __HashFunc3<T>,
        class HashFunc4 = __HashFunc4<T>,
        class HashFunc5 = __HashFunc5<T>>
class BloomFilter
{
public:

    BloomFilter(size_t capacity =0)
    {
        _capacity = _GetnewSize(capacity);
        _bm.Resize(capacity);
    }


    void Set(const T& 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 T& 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;
    }
private:
    BitMap _bm;
    size_t _capacity;//布隆过滤器的容量
};



void TestBloomFilter()
{
    BloomFilter<> bf(100);
    bf.Set("Just Do IT!");
    bf.Set("布隆过滤器");
    bf.Set("https://mail.google.com/mail/#inbox");


    cout << "Is exist?  :" << bf.Test("测试工程师") << endl;
    cout << "Is exist?  :" << bf.Test("开发工程师") << endl;
    cout << "Is exist?  :" << bf.Test("IT") << endl;
    cout << "Is exist?  :" << bf.Test("布隆过滤器") << endl;
    cout << "Is exist?  :" << bf.Test("BloomFilter") << endl;
    cout << "Is exist?  :" << bf.Test("https://mail.google.com/mail/#inbox") << endl;
    cout << "Is exist?  :" << bf.Test("https://mail.google.com/mail/#inbox111111") << endl;

}


int main()
{
    TestBloomFilter();
    system("pause");
    return 0;
}



本文出自 “Han Jing's Blog” 博客,请务必保留此出处http://10740184.blog.51cto.com/10730184/1773023

【数据结构】位图BitMap与布隆过滤器BloomFilter

首先先看一下下面这个腾讯的面试题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。 【腾讯】思路一:  最容易想到的解法就是遍历所有的40多亿个整数,...
  • zangyuanan320
  • zangyuanan320
  • 2016年05月30日 15:04
  • 803

JAVA实现较完善的布隆过滤器

布隆过滤器是可以用于判断一个元素是不是在一个集合里,并且相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数。但是它也是拥有一定的缺点:布隆过滤器...
  • u014653197
  • u014653197
  • 2017年07月30日 17:17
  • 1070

布隆过滤器总结(三)Java代码实现

/** * 项目名:SpiderCrawler * 文件名:BloomFilterTest.java * 作者:zhouyh * 时间:2014-8-29 下午02:54:56 *...
  • lifuxiangcaohui
  • lifuxiangcaohui
  • 2014年12月19日 11:24
  • 1252

基于Redis的布隆过滤器的实现

项目简介包含一个基于Redis的布隆过滤器的实现,以及应用到Scrapy中的Demo。地址:BloomFilterRedis布隆过滤器网上有很多介绍,推荐《数学之美》,介绍的很详尽,此处不再赘述。哈希...
  • qq_30242609
  • qq_30242609
  • 2017年04月30日 17:57
  • 2945

布隆过滤器Bloom Filter算法的Java实现(用于去重)

在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个 集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中);在 FBI,一个嫌疑人的名字是...
  • jdsjlzx
  • jdsjlzx
  • 2015年02月23日 13:54
  • 6426

大量数据去重:Bitmap和布隆过滤器(Bloom Filter)

5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解决?如果是64bit的呢? 在面试时遇到的问题,问题的解决方案十分典型,但对于海量数据处理接触少...
  • zdxiq000
  • zdxiq000
  • 2017年02月27日 17:46
  • 1568

布隆过滤器的简易实现

布隆过滤器(Bloom Filter): 是由布隆(Burton Howard Bloom)提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器用于检索一个元素是否在一个集合...
  • mi_rencontre
  • mi_rencontre
  • 2016年05月08日 16:41
  • 2107

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

【引言】 在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断 它是否在已知的字典中);在 FBI,一个嫌疑...
  • SunnyYoona
  • SunnyYoona
  • 2015年02月04日 12:39
  • 4441

布隆过滤器总结(二)原理和例子

布隆过滤器用于字符串去重复,比如网络爬虫抓取时URL去重、邮件提供商反垃圾黑名单Email地址去重。等等。用哈希表也可以用于元素去重,但是占用空间比较大,而且空间使用率只有50%。   布隆过滤器只...
  • lifuxiangcaohui
  • lifuxiangcaohui
  • 2014年12月19日 11:19
  • 3346

【布隆过滤器】实现一个简单的布隆过滤器

原理布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远...
  • wenqiang1208
  • wenqiang1208
  • 2017年08月06日 14:27
  • 295
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【数据结构】位图BitMap、布隆过滤器的算法实现
举报原因:
原因补充:

(最多只允许输入30个字)