海量数据处理(位图和布隆过滤器)

哈希切割

给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址? 与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现

解决思路

找到出现次数最多的IP地址

要找到前TopK的IP地址,就是要统计每个IP地址出现多少次
分割大文件:如果能将相同IP地址放到同一个文件中
哈希分割: 从源文件中获取一个IP地址---->IP%文件份数

  1. 每拿到一个IP地址后,用函数把IP地址转化为整型数据,再%上文件分数,就知道把IP地址放到哪个文件中去
  2. 这样,就可以统计每个IP地址出现多少次
//构建键值对
<IP地址的整型数据,次数>
  1. 统计哪个IP地址出现的次数比较多,用unordered_map,m[ip]++;每拿到一个IP地址的++。每个IP地址出现的次数,已经在unordered_map中保存起来
  2. 按类似的方法,统计每个文件中的IP地址的次数,最后用一个for()---->找出出现最多的IP地址

top K的IP

堆---->最多前K个IP地址—><次数,IP地址>

位图

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

  1. 遍历,时间复杂度O(N)
  2. 排序(O(NlogN)),利用二分查找: logN

位图解决

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比
特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
40亿的整型数据大概是16G的数据
用位图来映射的话 大概就是232-23=512M
在这里插入图片描述

解决

C++中提供了位图的类,bitset
在这里插入图片描述

位图的实现

#pragma once
#include<vector>
#include<iostream>
using namespace std;
namespace LXY
{
	class bitset
	{
	public:
		bitset(size_t bitCount)
			:_set(bitCount/8 + 1)
			, _bitCount(bitCount)
		{}
		//置1操作
		void set(size_t which)
		{
			//如果位集合给出100个比特位,那么你给100,就表示不了,范围为0~99
			if(which >= _bitCount)
				return;
			//计算对应的字节
			size_t index = (which >> 3);//除以8
			size_t pos = which % 8;

			//先将1移到对应的比特位上,再或上对应位上的数字
			_set[index] |= (1 << pos);
		}
		//置0操作
		void reset(size_t which)
		{
			if (which >= _bitCount)
				return;
			//计算对应的字节
			size_t index = (which >> 3);//除以8
			size_t pos = which % 8;

			//先将1的取反0移到对应的比特位上,再与上对应位上的数字
			_set[index] &= ~(1 << pos);
		}

		//检测which比特位是否为1
		bool test(size_t which)
		{
			if (which >= _bitCount)
				return false;
			//计算对应的字节
			size_t index = (which >> 3);//除以8
			size_t pos = which % 8;

			//与上1不等于0就代表存在
			return 0 != (_set[index] & (1 << pos));
		}
		//返回比特位总的个数
		size_t size()
		{
			return _bitCount;
		}

		//返回为1的比特位的总数
		size_t count()
		{
			//查表
			int bitCnttable[256] = {
				0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2,
				3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
				3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
				4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,
				3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
				6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4,
				4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
				6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,
				3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3,
				4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6,
				6, 7, 6, 7, 7, 8 };
			size_t szcount = 0;
			for (size_t i = 0; i < _set.size(); ++i)
				szcount += bitCnttable[_set[i]];
			return szcount;
		}
	public:
		std::vector<unsigned char> _set;
		size_t _bitCount;
	};
}

void TestBitSet()
{
	LXY::bitset bt(100);
	bt.set(10);
	bt.set(20);
	bt.set(30);
	bt.set(40);
	bt.set(41);
	cout << bt.size() << endl;
	cout << bt.count() << endl;

	if (bt.test(40))
		cout << "40bite is 1" << endl;
	else
		cout << "40bite is not 1" << endl;

	bt.reset(40);
	cout << bt.count() << endl;

	if (bt.test(40))
		cout << "40bite is 1" << endl;
	else
		cout << "40bite is not 1" << endl;
}

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

位图的应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序 (数据不能有重复)
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记

位图的题

  1. 给定100亿个整数,设计算法找到只出现一次的整数?
    用两个比特位表示一个数据,8/2=4;那么位图中一个字节只能表示4个数据,232/22=1G。
    取一个数据:哪个字节 哪两个比特位
	if(00) //出现过0次
	01
	else if(01) //出现过多次
	10
  1. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

布隆过滤器

位图+哈希函数
特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。多个比特位代表数据的状态信息

布隆过滤器插入

如果向布隆过滤器中插入baidu,我们用三个哈希函数将三个位置置为1
在这里插入图片描述
在这里插入图片描述
“baidu”---->1 4 7
“tecent”---->3 4 8
hash1,hash2,hash3----->三个位置
检测三个位置的状态
如果三个位置只要有一个为0,说明数据一定不存在

布隆过滤器的查找

布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中
布隆过滤器如果告诉你数据不存在,那么一定不存在,如果告诉你存在,则有可能存在。

布隆过滤器的插入和查找的实现

#pragma once
#include"biteset.hpp"
#include<iostream>
#include"Common.hpp"
using namespace std;

//BloomFilter:位图+多个哈希
template<class T,class HF1 = Str2INT ,class HF2 = Str2INT2,class HF3 = Str2INT3,
				class HF4 =Str2INT4,class HF5 = Str2INT5>
//哈希函数给的越多,将来产生误报的概率就也就越小
class BloomFilter
{
public:
	BloomFilter(size_t size = 10)
		:_bt(10 * size)
		, _size(0)
	{}
	bool Insert(const T& data)
	{
		//HF1()(data)可能回越界,要%上位图的比特位数
		size_t index1 = HF1()(data) % _bt.size();
		size_t index2 = HF2()(data) % _bt.size();
		size_t index3 = HF3()(data) % _bt.size();
		size_t index4 = HF4()(data) % _bt.size();
		size_t index5 = HF5()(data) % _bt.size();

		_bt.set(index1);
		_bt.set(index2);
		_bt.set(index3);
		_bt.set(index4);
		_bt.set(index5);
		_size++;
		return true;
	}

	//检测是否存在,每个哈希函数都得检测
	bool IsIn(const T&data)
	{
		size_t index = HF1()(data) % _bt.size();
		if (!_bt.test(index))
			return false;

		index = HF2()(data) % _bt.size();
		if (!_bt.test(index))
			return false;

		index = HF3()(data) % _bt.size();
		if (!_bt.test(index))
			return false;

		index = HF4()(data) % _bt.size();
		if (!_bt.test(index))
			return false;

		index = HF5()(data) % _bt.size();
		if (!_bt.test(index))
			return false;

		//元素可能存在
		return true;
	}

	//存储多少个元素
	size_t count()const
	{
		return _size;
	}
private:
	LXY::bitset _bt;
	size_t _size;
};

布隆过滤器的删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器(整型数组),插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

缺陷:
  1. 无法确认元素是否真正在布隆过滤器中
  2. 存在计数回绕

布隆过滤器优点

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  2. 哈希函数相互之间没有关系,方便硬件并行运算
  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

布隆过滤器缺陷

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
  2. 不能获取元素本身
  3. 一般情况下不能从布隆过滤器中删除元素
  4. 如果采用计数方式删除,可能会存在计数回绕问题

布隆过滤器

  1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
    答:给一个布隆过滤器,将一个文件的数据映射里面去。如果整体映射不完,先映射一部分。从另外一个文件拿一个数据在布隆过滤器里面找,如果有,数据存在就算是两个文件的交集。

倒排索引

给上千个文件,每个文件大小为1K—100M。给n个词,设计算法对每个词找到所有包含它的文件,你只有100K内存

哈希加密

发布了253 篇原创文章 · 获赞 41 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/liuyuchen282828/article/details/103952119