我们先给出之前我看过的腾讯公司的一道笔试题,引出位图BitMap。
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
这个问题怎么解决呢?
1)将40亿数据保存起来(保存在数组、链表、树中),再和该数判断是否相等。
那我们思考一下需要多少内存:
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;
}
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。