位图(布隆过滤器)

 bitset/bitmap

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

 

​
#pragma once

#include <vector>
#include <string>
#include <time.h>

template<size_t N>
class bitset
{
public:
	bitset()
	{
		_bits.resize(N/8 + 1, 0);
	}     

	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] |= (1 << j);//将对应的比特位设置成为1
	}

	void reset(size_t x)
	{
		size_t i = x / 8;//计算x映射的位在第i个char数组位置
		size_t j = x % 8;//计算x映射的位在这个char的第j个比特位

		_bits[i] &= ~(1 << j);//和0与是0,和1与不变。
	}

	bool test(size_t x)//查看x在不在
	{
		size_t i = x / 8;
		size_t j = x % 8;

		return _bits[i] & (1 << j);
	}

private:
	vector<char> _bits;
};

void test_bitset1()
{
	bitset<100> bs;
	bs.set(10);
	bs.set(11);
	bs.set(15);
	cout << bs.test(10) << endl;
	cout << bs.test(15) << endl;

	bs.reset(10);

	cout << bs.test(10) << endl;
	cout << bs.test(15) << endl;

	bs.reset(10);
	bs.reset(15);

	cout << bs.test(10) << endl;
	cout << bs.test(15) << endl;
}

void test_bitset2()
{
	//bitset<-1> bs1;//传-1可以开最大的空间42亿多
	bitset<0xFFFFFFFF> bs1;//这样写也可以开最大的空间42亿多
}

 twobitset

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

//用两个bitset封装
template<size_t N>
class twobitset
{
public:
	void set(size_t x)
	{
		// 00 -> 01
		if (_bs1.test(x) == false
		&& _bs2.test(x) == false)
		{
			_bs2.set(x);
		}
		else if (_bs1.test(x) == false
			&& _bs2.test(x) == true)
		{
		// 01 -> 10
			_bs1.set(x);
			_bs2.reset(x);
		}
		// 10代表至少出现两次,就不变化了。
	}

	void Print()//打印只出现一次的整数
	{
		for (size_t i = 0; i < N; ++i)
		{
			if (_bs2.test(i))
			{
				cout << i << endl;
			}
		}
	}

public:
	bitset<N> _bs1;
	bitset<N> _bs2;
};

void test_twobitset()
{
	int a[] = { 3, 45, 53, 32, 32, 43, 3, 2, 5, 2, 32, 55, 5, 53,43,9,8,7,8 };
	twobitset<100> bs;
	for (auto e : a)
	{
		bs.set(e);
	}

	bs.Print();
}

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

方法1:其中一个文件的值,读到内存的一个位图中再读取另一个文件,判断在不在上面位图中,在就是交集!存在问题:找出来的交集存在重复的值。

 方法2:

遍历,if(bs1.test(i)&&bs2.test(i))就是交集。

2.1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。和twobitset问题类似,00(出现0次)->01(出现1次)->10(出现2次)->11(出现3次)

位图的优点就是速度快、节省空间;缺点是只能映射整形,其他类型如:浮点数、string等等不能存储映射。

布隆过滤器

布隆过滤器减少磁盘IO或者网络请求,因为一旦一个值必定不存在的话,我们可以不用进行后续昂贵的查询请求。布隆过滤器正常情况下是不支持reset的。

struct BKDRHash//仿函数,将一个字符串转成整型。
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto ch : s)
		{
			hash += ch;
			hash *= 31;
		}

		return hash;
	}
};

struct APHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (long i = 0; i < s.size(); i++)
		{
			size_t ch = s[i];
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
			}
		}
		return hash;
	}
};


struct DJBHash
{
	size_t operator()(const string& s)
	{
		size_t hash = 5381;
		for (auto ch : s)
		{
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};

// N最多会插入key数据的个数
template<size_t N,
class K = string,
class Hash1 = BKDRHash,
class Hash2 = APHash,
class Hash3 = DJBHash>//Hash是将K转换成为整型,这里是一个key映射到三个位置。
class BloomFilter
{
public:
	void set(const K& key)
	{
		size_t len = N*_X;
		size_t hash1 = Hash1()(key) % len;
		_bs.set(hash1);

		size_t hash2 = Hash2()(key) % len;
		_bs.set(hash2);

		size_t hash3 = Hash3()(key) % len;
		_bs.set(hash3);

		//cout << hash1 << " " << hash2 << " " << hash3 << " " << endl << endl;
	 }

	bool test(const K& key)//需要三个位置在才能证明这个key值存在
	{
		size_t len = N*_X;

		size_t hash1 = Hash1()(key) % len;
		if (!_bs.test(hash1))
		{
			return false;
		}

		size_t hash2 = Hash2()(key) % len;
		if (!_bs.test(hash2))
		{
			return false;
		}

		size_t hash3 = Hash3()(key) % len;
		if (!_bs.test(hash3))
		{
			return false;
		}

		// 在      不准确的,存在误判
		// 不在    准确的

		return true;
	}
private:
	static const size_t _X = 6;
	bitset<N*_X> _bs;
};

void test_bloomfilter1()
{
	BloomFilter<100> bs;
	bs.set("sort");
	bs.set("bloom");
	bs.set("hello world hello bit");
	bs.set("test");
	bs.set("etst");
	bs.set("estt");

	cout << bs.test("sort") << endl;
	cout << bs.test("bloom") << endl;
	cout << bs.test("hello world hello bit") << endl;
	cout << bs.test("etst") << endl;
	cout << bs.test("test") << endl;
	cout << bs.test("estt") << endl;

	cout << bs.test("ssort") << endl;
	cout << bs.test("tors") << endl;
	cout << bs.test("ttes") << endl;
}


void test_bloomfilter2()
{
	srand(time(0));
	const size_t N = 10000;
	BloomFilter<N> bf;

	std::vector<std::string> v1;
	std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";

	for (size_t i = 0; i < N; ++i)
	{
		v1.push_back(url + std::to_string(i));
	}

	for (auto& str : v1)
	{
		bf.set(str);
	}

	// v2跟v1是相似字符串集,但是不一样
	std::vector<std::string> v2;
	for (size_t i = 0; i < N; ++i)
	{
		std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
		url += std::to_string(999999 + i);
		v2.push_back(url);
	}

	size_t n2 = 0;
	for (auto& str : v2)
	{
		if (bf.test(str))
		{
			++n2;
		}
	}
	cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;

	// 不相似字符串集
	std::vector<std::string> v3;
	for (size_t i = 0; i < N; ++i)
	{
		string url = "zhihu.com";
		//string url = "https://www.cctalk.com/m/statistics/live/16845432622875";
		url += std::to_string(i + rand());
		v3.push_back(url);
	}

	size_t n3 = 0;
	for (auto& str : v3)
	{
		if (bf.test(str))
		{
			++n3;
		}
	}
	cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
}

​

 布隆过滤器使用场景是什么?
1、能容忍误判的场景。比如:注册时,快速判断昵称是否使用过。因为昵称在布隆过滤器判断的结果是没有用过就一定是没有用过,布隆过滤器判断结果是用过了则实际上有一定的概率并没有被用过。

2、给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法。

 造成上述问题的原因:1、单个文件中,有大量不同的query;2、单个文件中,有某个大量重复的query;

3. 给一个超过100G大小的log file, log中存着IP地址,设计算法找到出现次数最多的IP地址?
如何找到top K的IP?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值