位图、布隆过滤器、大数据查找

一、位图

        位图是哈希映射思想的一种体现形式。一个比特位只能存储0/1,如果我们用0表示某个数据存在 1表示某个数据不存在,那么我们只用一个比特位就可以知道某个数据的存在与否。

#include<iostream>
#include<vector>
using namespace std;
 

    //N代表数据范围
	template<size_t N>
	class bit_set
	{
	public:
		bit_set()
		{
			_bits.resize(N / 8 + 1, 0);
		}
		void set(size_t x)
		{
			//由于一个组是char,所以x/8是计算在哪个char组里
			size_t i = x / 8;
			//一个char里有8位,x%8是计算出在char组里面的具体哪一位
			size_t j = x % 8;
			//这个建议大家画图理解,首先_bits[i]是对应的char组,然后1<<j,其实是将1向左移动了j位,然后再或等,这样就把对应位置上的数置为1了,可以画图理解.
			_bits[i] |= (1 << j);
		}
		void reset(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;
			//也是先1向左移动j位,然后再按位取反,此时除了目标位是0,别的位都是1.然后再&上这个数,任何数&1都是它本身,&0都是0.所以此时将目标位设置为0了
			_bits[i] &= ~(1 << j);
		}
		bool test(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;
			//任何数&1都是它本身,因为我们可以利用这一点来判断目标位是0还是1.
			//注意此时不再需要加等于号了,因为这是判断,不是修改。
			return _bits[i] & (1 << j);
		}
	private:
		vector<char> _bits;
	};
大数据查找:
1.给定100亿个整数,如何查找某个数X是否在这100亿个整数中?

     一个比特位就可以表示一个整数存在与否,那么100亿个整数就需要100亿个比特位存储信息,我们只需要知道X所对应的比特位是否为1就可以知道X在没在100亿个整数中。

2.给定100亿个整数,设计算法找到只出现一次的整数?

        由于一个比特位只能存储0/1表示两种状态,而题目只出现一次意味着一个数字因该有三种状态:一次都没出现(出现0次)、出现1次、出现两次及以上,因此一个比特位无法完全表述信息此时可以考虑 两个比特位 存储一个数据的三种信息。

        两个比特位组合为 00、01、10、11可表示四种情况,而我们只需要三种即可,因此我这里取00、01、10分别表示 一次都没出现(出现0次)、出现1次、出现两次及以上。因此我们创建两个位图对象bit_set set1 与bit_set set2 即可,set1表示第一个比特位 set1表示第二个比特位 这样就可以靠复用bit_set类解决问题了

3. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集? 

        这个是第一个问题的延申,自然而然我们能想到给每个文件的100亿个整数创建一个位图,这样就有了两个位图set1 与 set2

那么我们如何找交集?自然而然如果两个位图中某个比特位都为1那么说明 这个比特位对应的数字在两个文件均出现了,也就是交集中的元素。因此只要让两个位图set1 与 set2按位与以下,然后按照位与结果找比特位为1的位置对应的数字即可。

4. 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数.

        这个题是2题的延申。不超过2次的整数对应四种情况,没出现过,出现1一次、出现两次、出现两次及以上 用两个比特位组合为 00、01、10、11对应这四种情况即可。

二、布隆过滤器

        布隆过滤器本质是哈希冲突的应用(ps 大佬就是大佬,变废为宝)。

假设我们用下面这种方式 0表示某个数据不在 1 表示某个数据在

假设插入7按照7%6(0~5一共6个位置)=1映射关系,映射到下标1

        此时我们查询7在不在就很简单,但是问题来了,当当我们要查询13在不在时由于位置1存储的是1所以我们会误判,认为13存在,但实际上这个1表示的是7在,这就是哈希冲突。

        如果我们反过来想,我想知道6在不在,看一下0位置是0,所以6不在,我想知道,12在不在看一下0位置是0,所以12不在。因此我们可以得到一个重要结论 如果我们要查询某个数据X在不在,如果查询结果表示数据不在,则是可靠的,如果查询结果表示在,结果不一定可靠(查询结果表示在,有可能是别的数据也映射到了同一位置)。简单来说就是,哈希搜索结果说不在就不在,说在可能是假的。

        那么这个结论有什么用呢?显然,过滤作用,我们有一批数据B要看看哪些不在数据A中(假设AB均为无序数据 也不让排序),我们可以创建A的哈希映射,然后拥B的数据在这个哈希映射中找,如果不在就不在,如果在就把这个数据在A中暴力顺序查找一遍,就知道到底在不在。也就是说我们过滤了一批不用暴力找的数据,大大提升了效率。

        当然一个位置表示一个数据的话哈希冲突情况很严重,误判率很高。因此布隆过滤器使用三种哈希映射来对比表示一个数据到底在不在,但这种方式只是降低了误判率,误判是不可能消除的

        

        如图,假设“hehe” 的三种映射方式分别映射到了0 1 2 “haha 的三种映射方式分别映射到了2 4 5,那么         如过我们要查询的“hei”映射到 0 1 4 ,结果就会说明“hei”存在,但实际上“hei”不存在,这就是误判了。但如果“hei”映射到 0 1 3 因为3的位是0 就表示“hei”没被存储过,结果可靠。这就是布隆过滤器

struct HashBKDR
{
	// BKDR
	size_t operator()(const string& key)
	{
		size_t val = 0;
		for (auto ch : key)
		{
			val *= 131;
			val += ch;
		}
 
		return val;
	}
};
struct HashAP
{
	// BKDR
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ key[i] ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ key[i] ^ (hash >> 5)));
			}
		}
		return hash;
	}
};
 
struct HashDJB
{
	// BKDR
	size_t operator()(const string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}
 
		return hash;
	}
};
 
//N表示准备要映射N个值
template<size_t N,class K=string,class Hash1=HashBKDR, class Hash2=HashAP, class Hash3=HashDJB>
class BloomFilter
{
public:
	void Set(const K& key)//一个值对应多个位置
	{
		size_t hash1 = Hash1()(key) % (_ratio * N);
		_bits.set(hash1);
		size_t hash2 = Hash2()(key) % (_ratio * N);
		_bits.set(hash2);
		size_t hash3 = Hash3()(key) % (_ratio * N);
		_bits.set(hash3);
	}
	bool Test(const K& key)//只要有一个位为0,就return false。
	{
		size_t hash1 = Hash1()(key) % (_ratio * N);
		if (!_bits.set(hash1))
			return false;
		size_t hash2 = Hash2()(key) % (_ratio * N);
		if (!_bits.set(hash2))
			return false;
		size_t hash3 = Hash3()(key) % (_ratio * N);
		if (!_bits.set(hash3))
			return false;
		return true;//返回真也可能存在误判
	}
private:
	const static size_t ratio = 5;//比例
	bitset<_ratio*N> _bits;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值