位图与布隆过滤器 的说明 介绍

在平常的编程中 ,我们经常要有一些判断,某个数据  ,,在不在一个文件或者什么中  ???、

如果 是 以前 我们 ,没有 学习 数据 结构 之前,,,我们 一看到这种题目 ,,,我们所能 想到的第一种 方法;就是  使用 二分查找 ,,,但是 这种方法   有限制  成,,,必须是 有序的数据 ,,,,才可使用 这种方法 。。。。
但是 ,,,,在学习数据 结构 之后,我们 就有很多 的 方法来 解决这种 问题 了  。。。。
就比如  ,,,,说我们 可使用 搜索二叉树来 查找数据 ,也可是使用 红黑树来查找 ,,,
算法的时间复杂度都是O(log2 N);;;
那么今天我们就来说一说 同样应用于搜索的两类结构;;;;

1、位图  

之前,,,某个公司的面试 出了这么一道题  ;;;;
有40亿个无符号整型的值,,,,我们要早其中找到某个整型 是否存在 ????假设我们的运行内存为 1G。。
当遇到这种问题的时候 ,,我们就不能 使用上述的方法来实现了 。。。。
为什么呢???
40亿个    unsigned   int     在内存中的容量大致计算得到的   是     大约    16G的内存 ;;;
如果要是使用上面的二叉树来查找的话、、、、、
那么,,,建立的树的大小,,,,远远大于运行 内存  ,,,,,根本无法实现     。。。。
所以,,,在这里我们   引入了一个新的结构————位图————————;  
对于上面说说的那类题型 ,, ,我们要了解到,,我们 所要知道的仅仅是    ,,,,某个数在不在  ,,,,一个集合中 。。。。
所以,,对于这类数据 ,,,,我们 只需要两种 状态来  ,,标记 这些   。。。
所以,,我们 可以使用 一个bit为来表示 这俩种  状态  1或者是  0;;
这种方法就是 我们所说的位图 。。。。。
所以 ,,,对于 位图 的,,结构 ,我们可以只是 使用一个   vector内部存放的数据类型 为size_t ,
也就是 说 一个元素可以表示 8个数据的存在与否 ;;;;
图形结构 显示:::

其中 0表示 ,,,不存在,1表示   存在  ,,
如果要是 使用的是 这种方法的话 ,,,,那么所有的整型值,,,所有建立的vector容器的大小才500M
并且,,,查找的时间复杂度大致相当于O(1);
代码实现
#pragma once

#include<vector>
class BitMap
{
public:
	BitMap(const size_t & n)//n内部表示数据的个数  
	{
		_bt.resize((n>>5)+1);//开辟空间
	}
	void  Set(const size_t  value)//设置内部值
	{
		size_t idex = value>>5;//找到该数存储在那个整型的范围内
		size_t num  = value %32; //找到在整形的哪个位上
		_bt[idex] |= (1<<num);//将这位置设置成  1;
	}
	void ReSet(const size_t value)//重置
	{
		size_t idex =value>>5;
		size_t num  =value%32;
		_bt[idex] &= ~(1<<num);//将位置设置 成 0 ;
	}
	bool Test(const size_t value)
	{
		size_t idex = value>>5;
		size_t num = value%32;
		if(_bt[idex] & (1<<num))//如果位置上的量是  1  
			return true;//表示 存在
		return false;//表示 不存在
	}
	
protected:
	vector<size_t> _bt;
};



void TestBitMap()
{
	BitMap bt((size_t ) -1);
	bt.Set(1);
	bt.Set(10432);
	bt.Set(1034);
	bt.Set(320);
	cout<<"1 ?"<<bt.Test(1)<<endl;
	cout<<"10432 ?"<<bt.Test(10432)<<endl;
	cout<<"1034 ?"<<bt.Test(1034)<<endl;
	cout<<"321 ?"<<bt.Test(321)<<endl;
	bt.ReSet(1034);
	cout<<"1034 ?"<<bt.Test(1034)<<endl;
}
位图的使用大致就是 这个样子了 。。。。。、
但是,我们在 平常的编程中 是用的大部分都是 字符串 ,,,,,但是这里有不能来处理 ,,,,,
所以 ,,就要引入我们的新的结构-------布隆过滤器

布隆过滤器

在平常的生活中我们经常要来判断,,一个元素是否在一个集合中,,,;;;;
就好像是,我们要早电话本中查看是否有某人的号码,,,,还有就是 检查一个英文单词的拼写是不是很正确。。
我们经常会想到是,使用的是哈希表,,,,这种数据结构的主要优点就是 :::快速准确,,但又很浪费空间 。。。 如果要是,,查找的数据量十分的庞大的话 ,,哈希表的存储效率就会大大的下降  。。。。
如果要是使用的是位图的话,,就可以将这个解决,,但是位图,,无法用来表示 字符串。。。
所以我们 就引入了布隆过滤器这个东西。。。。。

所谓的布隆就是将位图 的特点与哈希表来结合起来 ,,,
但是 ,,,,哈希表有 哈希冲突 ;;
所以 ,,我们 就可以使用 5个位来表示这个字符串,,,,,所以这里就要  熟悉字符串的哈希算法;;;
但是 这样。。还是会有冲突的,,,,但是 几率是 十分低的。。。
所以 说 ,,,,布隆只能拿到的是 近似值。。。
换句话说就是,,,,,
在布隆里找一个数据   ,,,,找到的话 ,,,不一定准确,,,但没找到,,一定准确
代码的实现   
#pragma once 
#include"BitMap.h"
#include"HashFunc.h"
#include<string>
typedef long long  LongType;
struct __HashFunc1
{
	template<class T>  
	size_t BKDRHash(const T *str)  
{  
	register size_t hash = 0;  
	while (size_t ch = (size_t)*str++)  
	{         
		hash = hash * 131 + ch;   // 也可以乘以31、131、1313、13131、131313..  
		// 有人说将乘法分解为位运算及加减法可以提高效率,如将上式表达为:hash = hash << 7 + hash << 1 + hash + ch;  
		// 但其实在Intel平台上,CPU内部对二者的处理效率都是差不多的,  
		// 我分别进行了100亿次的上述两种运算,发现二者时间差距基本为0(如果是Debug版,分解成位运算后的耗时还要高1/3);  
		// 在ARM这类RISC系统上没有测试过,由于ARM内部使用Booth's Algorithm来模拟32位整数乘法运算,它的效率与乘数有关:  
		// 当乘数8-31位都为1或0时,需要1个时钟周期  
		// 当乘数16-31位都为1或0时,需要2个时钟周期  
		// 当乘数24-31位都为1或0时,需要3个时钟周期  
		// 否则,需要4个时钟周期  
		// 因此,虽然我没有实际测试,但是我依然认为二者效率上差别不大          
	}  
	return hash;  
}  
	size_t operator()(const string& key)
	{
		return  BKDRHash(key.c_str());
	}
};
struct __HashFunc2
{
	template<class T>  
	size_t SDBMHash(const T *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 string& key)
	{
		return  SDBMHash(key.c_str());
	}
};
struct __HashFunc3
{
	template<class T>  
	size_t RSHash(const T *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 string& key)
	{
		return  RSHash(key.c_str());
	}
};
struct __HashFunc4
{
	template<class T>  
	size_t APHash(const T *str)  
{  
	register size_t hash = 0;  
	size_t ch;  
	for (long i = 0; ch = (size_t)*str++; i++)  
	{  
		if ((i & 1) == 0)  
		{  
			hash ^= ((hash << 7) ^ ch ^ (hash >> 3));  
		}  
		else  
		{  
			hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));  
		}  
	}  
	return hash;  
}  
	size_t operator()(const string& key)
	{
		return  APHash(key.c_str());
	}
};;
struct __HashFunc5
{
	template<class T>  
	size_t JSHash(const T *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 string& key)
	{
		return  JSHash(key.c_str());
	}
};
template<class K,
	class HashFunc1 = __HashFunc1,
	class HashFunc2 = __HashFunc2,
	class HashFunc3 = __HashFunc3,
	class HashFunc4 = __HashFunc4,
	class HashFunc5 = __HashFunc5>
class BloomFilter
{
	
public:
	BloomFilter(const LongType num)
		:_size(num*10)
		,_bt(num*10)
	{}
	void  Set(const  K& key)
	{
		size_t hash1 = HashFunc1()(key);
		size_t hash2 = HashFunc2()(key);
		size_t hash3 = HashFunc3()(key);
		size_t hash4 = HashFunc4()(key);
		size_t hash5 = HashFunc5()(key);
		_bt.Set(hash1);
		_bt.Set(hash2);
		_bt.Set(hash3);
		_bt.Set(hash4);
		_bt.Set(hash5);
	}
	bool Test(const  K& key)
	{
		size_t hash1 = HashFunc1()(key);
		size_t hash2 = HashFunc2()(key);
		size_t hash3 = HashFunc3()(key);
		size_t hash4 = HashFunc4()(key);
		size_t hash5 = HashFunc5()(key);
		if(_bt.Test(hash1) == false)
			return false;
		if(_bt.Test(hash2) == false)
			return false;
		if(_bt.Test(hash3) == false)
			return false;
		if(_bt.Test(hash4) == false)
			return false;
		return _bt.Test(hash5);
	}

protected:
	BitMap _bt;
	LongType _size;

};

void  TestBloomFilter()
{
	BloomFilter<string> bt(-1);
	char  * url1 = "http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
	char  * url2 = "http://www.cnblogs.com/-clq/archive/2012/05/31/2528111.html";
	char  * url3 = "http://www.cnblogs.com/-clq/archive/2012/05/31/2358111.html";
	char  * url4  = "http://www.cnblogs.com/-clq/archive/2012/05/31/24328111.html";
	bt.Set(url1);
	bt.Set(url2);
	bt.Set(url3);
	bt.Set("xiao");
	bt.Set("xiao1");
	cout<<url1<<"  "<<bt.Test(url1)<<endl;
	cout<<url2<<"  "<<bt.Test(url2)<<endl;
	cout<<url3<<"  "<<bt.Test(url3)<<endl;
	cout<<url4<<"  "<<bt.Test(url4)<<endl;
	cout<<"xiao ?"<<" "<<bt.Test("xiao")<<endl;
	cout<<"xiao1 ?"<<" "<<bt.Test("xiao1")<<endl;

}







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值