哈希的应用 -- 布隆过滤器与海量数据处理

news2024/9/21 22:48:03

文章目录

  • 布隆过滤器概念
  • 布隆过滤器设计思路
  • 布隆过滤器的应用
  • 布隆过滤器模拟实现
    • 布隆过滤器的基本框架
    • 布隆过滤器的插入
    • 布隆过滤器的探测
    • 布隆过滤器的删除
  • 布隆过滤器优点
  • 布隆过滤器缺陷
  • 布隆过滤器模拟实现代码及测试代码
  • 海量数据处理
    • 哈希切割
    • 哈希切分

布隆过滤器概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概
率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存
”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也
可以节省大量的内存空间

布隆过滤器设计思路

在面对海量整数数据时,使用位图不但效率高还节省空间.但是位图对数据类型有限制,只能映射处理整数类型数据.

可是如果处理含量字符串数据时,该怎么处理呢?

此时布隆过滤器便由此而生.

由于位图采用的是直接定址法,不存在哈希冲突.
而布隆过滤器实质采用的是通过哈希算法转换成整数映射一个位置进行进行标记,此时便难免会产生哈希冲突,并且哈希冲突概率较高.
例如以下图示:
在这里插入图片描述
但是我们通过布隆过滤器了解的是:
如果该字符串显示存在,说明是不准确的,存在误判.
如果该字符串显示不存在,说明是准确的,不存在误判.

那么通过哈希算法计算映射位置是不可能完全除去误判,但是可以降低误判率,那么怎么降低布隆过滤器的误判率?

我们可以将么一个字符串多映射几个位(调用不同的哈希算法让同一个字符串映射不同位置),理论而言,一个字符串映射的位越多,则误判效率越低,但是也不能映射太多位置,因为映射的位置数越多,消耗的空间就越大,进而会导致布隆过滤器的优势降低.

所以我们可以给每个字符串设置3个映射位,例如以下图示,假如黄瓜与香蕉和西瓜分别有一个映射位置发生了哈希冲突,但是它还有一个映射位置:
如果这个位置被设置了,说明它确实存在.
如果这个位置没有被设置,说明它确实不在.
所以,只有三个映射位置都跟别的数据发生冲突才可以造成误判,可是误判的的概率相较之前极大降低.
在这里插入图片描述

布隆过滤器的应用

一:黑名单应用
当我们给出大量数据名单来检测是存在于黑名单中时,我们便可以使用布隆过滤器进行筛选:
如果给出名单经过布隆过滤器检测显示存在(此时可能有误判),那么我们需要到黑名单数据库中进一步检测查找.
如果给出名单经过布隆过滤器检测后显示不存在(没有误判),那么我们可以直接返回检测结果,不需要到数据库中查询.
布隆过滤器将名单中不存在于黑名单的过滤,让大量数据只有有限数据能够到数据库中检测,进而提高了查找效率.
在这里插入图片描述
二:注册昵称检查
当我们在注册页面中输入注册昵称时,显示昵称是否被占用时:
如果提示被占用,可能存在误判,但是可以允许,因为误判的概率很小,那么我们不可以使用该名称注册.
如果提示没被占用,说明我们可以使用该名称注册.

综合来讲,以上应用场景适合允许误判的情况下,提高了查找效率.

布隆过滤器模拟实现

布隆过滤器的基本框架

布隆过滤器由一个位图构成,其中N表示要映射N个数据.此外为了准确开辟N个数据所需要合适大小的布隆过滤器,有人通过检测研究得出了以下关系式:
在这里插入图片描述

其中,n代表哈希函数个数,m为布隆过滤器长度,n为需要插入的元素个数,p为误报率.
在模拟实现中,我们所需要的哈希函数为3个,所以通过计算得出布隆过滤器中插入一个元素需要4.2个位长度的位图.(为了防止映射位置不够,我们设置为5).

由于布隆过滤器一般用于处理字符串类型的数据,所以将模板参数的K缺省值设为string.

//三个哈希函数

//布隆过滤器框架
template<size_t N, class K = string, class Hash1 = BKDRHash, class Hash2 = APHash, class Hash3 = DJBHash>
class BloomFilter
{
public:
	private:
	const static size_t _ratio = 5;  //const static 可以直接定义;
	std::bit_set<_ratio* N> _bits;
};

此外,为了能够将字符串转换成整型,我们采取了经过测试,综合评分最高的HashBKDR,HashAP,HashDJB算法计算元素哈希映射位置,进而极大避免了哈希冲突的概率.

struct HashBKDR
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (auto ch : s)
		{
			value = value * 131 + ch;
		}
		return value;
	}
};
struct HashAP
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (size_t i = 0; i < s.size(); i++)
		{
			if ((i & 1) == 0)
			{
				value ^= ((value << 7) ^ s[i] ^ (value >> 3));
			}
			else
			{
				value ^= (~((value << 11) ^ s[i] ^ (value >> 5)));
			}
		}
		return value;
	}
};
struct HashDJB
{
	size_t operator()(const string& s)
	{
		if (s.empty())
			return 0;
		size_t value = 5381;
		for (auto ch : s)
		{
			value += (value << 5) + ch;
		}
		return value;
	}
};

布隆过滤器的插入

布隆过滤器的插入实则是在位图中,通过三个哈希函数分别计算出该元素的映射位置,然后再复用位图中的set函数将对应映射位置置为1.

   	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);
	}

注意:
只有当该元素映射的三个位置都被设置为1,才能说明该元素存在(也有可能这三个位置都发生哈希冲突,但这概率较低).

布隆过滤器的探测

布隆过滤器用于探测某个元素是否存在于布隆过滤器中,检测时,我们只要通过该元素分别找到该元素对应的三个比特位,然后再分别判断这三个比特位的状态:
如果这三个比特位全部被设置,说明该元素存在,返回true.(可能存在误判).
如果这三个比特位有一个位没被设置,就说明该元素一定不存在.(三个位置有的其他位被设置可能发生哈希冲突).

bool test(const K& key)
	{
		size_t hash1 = Hash1()(key) %(_ratio * N)
		if ( _bits.test(hash1) == false)
			return false;         //该元素一定不存在.

		size_t hash2 = Hash2()(key) % (_ratio * N);
		if ( _bits.test(hash2) == false )
			return false;

		size_t hash3 = Hash3()(key) % (_ratio * N);
		if ( _bits.test(hash3) == false )
			return false;

		return true;            //所以就表明在.(可能存在误判)
    }

注意:
1:由于一个比特位存在并不能说明该元素存在过,但是有一个比特位不存在却能说明该元素不存在,所以我们不能判断比特位存在的情况,而是要判断该比特位不存在的情况.
2:为了防止计算哈希映射位置范围超过比特位的范围造成越界,我们%布隆过滤器的长度来控制计算出来的结果在布隆过滤器范围内.

布隆过滤器的删除

布隆过滤器一般不支持删除,原因如下:
1:因为布隆过滤器判断一个元素存在时会存在误判,因此我们不能保证删除的元素存在于布隆过滤器中,此时通过该元素将计算出的映射为设置为0可能会影响其他数据.
2:当删除的数据确实在布隆过滤器中,但是也有可能该元素的三个映射位中有其它映射位发生了哈希冲突,此时,将这些映射位设置为0,也会影响到其他元素的检测.
例如以下图示:
在这里插入图片描述

那么如何让布隆过滤器支持删除呢?

1: 我们必须要保证删除的元素存在于不容过滤器中,例如在昵称应用时,我们要删除一个名称在删除之前我们可以提前设置一个test函数检测筛选出可能存在的名称,如果结果为存在(还不能判断真正存在),那么我们就需要到名称数据库中查找该名称,确定该名称是否真正存在.

2: 我们要保证删除该元素后不会影响到其他元素,所以我们可以在位图的每一个比特位中设置一个计数值,如果铀元素插入到对应的比特位,那么该比特位的计数器就++,在删除时,我们只需要将该元素对应比特位计数器–就行.

例如以下图示:
在这里插入图片描述
但是,布隆过滤器还是没有提供删除函数,因为布隆过滤器的优势本来就是调高查找效率和节省空间,如果删除时要确认该元素是否存在还要在数据库中查找,消耗时间. 且还需要在每个比特位中设置一个计数变量,这又要多占用几倍的存储代价.

布隆过滤器优点

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

布隆过滤器缺陷

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

布隆过滤器模拟实现代码及测试代码

using namespace std;
struct HashBKDR
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (auto ch : s)
		{
			value = value * 131 + ch;
		}
		return value;
	}
};
struct HashAP
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (size_t i = 0; i < s.size(); i++)
		{
			if ((i & 1) == 0)
			{
				value ^= ((value << 7) ^ s[i] ^ (value >> 3));
			}
			else
			{
				value ^= (~((value << 11) ^ s[i] ^ (value >> 5)));
			}
		}
		return value;
	}
};
struct HashDJB
{
	size_t operator()(const string& s)
	{
		if (s.empty())
			return 0;
		size_t value = 5381;
		for (auto ch : s)
		{
			value += (value << 5) + ch;
		}
		return value;
	}
};




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);
	//	cout << hash1 << endl;
		_bits.set(hash1);

		size_t hash2 = Hash2()(key) % (_ratio * N);
	//	cout << hash2 << endl;
		_bits.set(hash2);
		
		size_t hash3 = Hash3()(key) % (_ratio * N);
	//	cout << hash3 << endl;
		_bits.set(hash3);


	}
	bool test(const K& key)
	{
		size_t hash1 = Hash1()(key) %(_ratio * N);
	//	cout << hash1 << endl;
		if ( _bits.test(hash1) == false)
			return false;


		size_t hash2 = Hash2()(key) % (_ratio * N);
	//	cout << hash2 << endl;
		if ( _bits.test(hash2) == false )
			return false;


		size_t hash3 = Hash3()(key) % (_ratio * N);
//		cout << hash3 << endl;
		if ( _bits.test(hash3) == false )
			return false;

		//走到这里,说明三个探测为不在.

		return true;            //所以就表明在.(可能存在误判)
		
    }
private:
	const static size_t _ratio = 5;  //const static 可以直接定义;
	myBit::bit_set<_ratio* N> _bits;
};

void  TestBloomFilter()
{
	BloomFilter<10> bf;


	string arr[] = { "苹果","西瓜22","苹果111","西瓜22","苹果2222","香蕉3333","西瓜3333","美团333","阿里333","字节"};

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

	string arr1[] = { "苹果","西瓜","苹果111","西瓜22" };

	for (auto& str : arr1)
	{
		cout << bf.test(str) << endl;
	}

}

海量数据处理

哈希切割

题目一: 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件的交集?给出近似算法。

题目中要求给出近似算法,意味着可以允许存在一些误判,这时,我们便可以采用使用布隆过滤器:
1: 首先遍历读取到一个文件中的querry,将该文件的querr全部插入到布隆过滤器中.

2: 然后再遍历读取另外一个文件的querry,使用test()函数分别判断每个querry是否存在与布隆过滤器中,如果存在,则说明该querry是交集,如果不存在,说明该querry不是交集.

哈希切分

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

如果这道题要求我们给出的是精确算法,那么我们就不能使用布隆过滤器了,此时应该用上哈希切分.

首先,我们先来计算以下内存空间计算题:

假设每个查询30byte,100亿个查询需要多少个空间?

进制转换如下:
在这里插入图片描述
综合以上进度转换:
10 亿字节 = 1GB;

3000亿字节 = 300GB = 300000MB;

当我们清楚内存进制转换后,此时我们便可以对更加方便的使用哈希切分思想解题,步骤如下:
1:依次读取文件A中的querry, 使用哈希算法 i = Hash(querry) % 1000,分别计算每个querry对应的映射位置( i 的范围为0-999).这里实则是将文件A中的querry分成了1000个小文件,每个小文件的大小为300MB,然后让每个querry放进对应编号为Ai的小文件中.

2:依次读取文件A中的querry, 使用哈希算法 i = Hash(querry) % 1000,分别计算每个querry对应的映射位置( i 的范围为0-999).这里实则是将文件A中的querry分成了1000个小文件,每个小文件的大小为300MB,然后让每个querry放进对应编号为Ai的小文件中.

3: 我们知道在AB两个文件中,相同的querry一定被放进相同编号i的小文件中,我们可以依次将Ai和Bi(编号相同)的小文件分别放进两个set容器中,set容器会对该小文件中的querry的进行去重,然后依次遍历这两个容器,如果querry相同即为交集.
图示如下:
在这里插入图片描述

有没有可能某个小文件由于哈希冲突很多导致文件太大了,加载不到程序中?

我们可以将这个算法思想写成递归,再对这个文件进行哈希切分成一个个小文件,但是我们为了防止该query的映射位置相同,我们要换一个哈希算法来计算该query的映射位置,并且我们要根据该文件的大小合理分配每个小文件的大小.

题目二:给一个超过100G大小的log file,log中存着IP地址,设计算法找到出现次数最多的IP地址?

我们也可以将这100GB大小的log file 分成一个个小文件,这里我们选择分成500个,每个文件200MB,这样就会让相同的ip进入到同一个小文件中.
我们知道,虽然相同的ip一定会进入同一个小文件,但是同一个小文件中有可能会有不同的ip:
1: 有可能ip不同,但是通过哈希函数计算出来的映射位置相同,即哈希冲突.
2: 有可能哈希函数计算出来的结果不相同相同,但是%500之后计算出来的映射位置是相同的.
但是这些问题并不重要不重要,且概率较小,我们能保证大部分的小文件中的ip相同就行.

然后使用map<string,int>对每个小文件的ip统计出现次数,找出每个小文件中出现次数最多的ip,然后依次遍历对比,就可以获取log file文件中次数出现最多的ip了(当然也有可能出现小文件因哈希冲突过多导致内存过大,我们只要对其递归进行哈希切分即可).

针对本题如何找到top K的IP?

如果要找到出现topK的IP地址,我们可以先将一个小文件加载到内存中,选出该次数最多的K个IP地址建一个k值pair<ip,count>类型的小堆,然后依次将剩下的小文件加载内存,如果该小文件中IP统计次数大于堆顶IP统计次数,则将堆顶IP与该文件中的IP进行交换,然后再进行向下调整,使其认为小堆,此时,小堆的K个IP即为这两个小文件中的统计次数最多的K个IP,如果对比完所有小文件中的ip后,此时的小堆中K个IP即为log file 文件中统计次数最多的K个的IP.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/480519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】HOG+SVM实现行人检测

文章目录 一、准备工作1. 下载数据集2. 解压数据集 二、HOG特征简介1. 梯度&#xff08;Gradient&#xff09;2. 格子&#xff08;Cell&#xff09;3. 块归一化&#xff08;Block Normalization&#xff09;4. HOG特征&#xff08;HOG Feature&#xff09;5. 使用skimage.featu…

“五一”假期消防安全知识要牢记之消防安全知识答题活动

“五一”期间&#xff0c;容易出现哪些安全隐患&#xff0c;生产生活中要注意哪些安全事项&#xff0c;一起来看&#xff01; 森林防火&#xff1a; 1.禁止将火柴、汽油等易燃物带入山林&#xff0c;禁止乱扔火种。 2.景区管理单位要加强防火巡逻&#xff0c;禁止野外火源&am…

smbms项目搭建

目录 1.搭建一个maven web项目 2.配置Tomcat 3.测试项目是否能够跑起来 4.导入项目中会遇到的Jar包 5.项目结构搭建 6.项目实体类搭建 7.编写基础公共类 1.数据库配置文件 2.编写数据库的公共类 3.编写字符编码过滤器 3.1web配置注册 4.导入静态资源 1.搭建一个maven web项目 …

C++前置声明的理解

知识补充 在C/C中引入一个头文件时&#xff0c;在编译器预处理的时候会将引入头文件的地方简单替换成头文件的内容。这样做的后果是很容易引起头文件的重复引用。所以我们在编写头文件是一般有以下规定来防止头文件被重复包含。 MyWidget.h #ifndef MyWidget_H_ #define MyWi…

实验四、彩色图像处理

实验目的 使用MatLab软件对图像进行彩色处理&#xff0c;熟悉使用MatLab软件进行图像彩色处理的有关方法&#xff0c;并体会到图像彩色处理技术以及对图像处理的效果。 作业1&#xff1a;生成一副256*256的RGB图像&#xff0c;使得该图像左上角为黄色或者青色&#xff0c;左下…

day04_基本数据类型丶变量丶类型转换

前置知识 计算机世界中只有二进制。那么在计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。 进制 进制也就是进位计数制&#xff0c;是人为定义的带进位的计数方法 。不同的进制可以按照一定的规则进行转换。 进制的分类 十进制&#x…

Seurat -- Perform linear dimensional reduction

brief 什么是线性降维&#xff1f; 这里是一个很形象的网页演示&#xff0c;其中包括了一个视频链接。 这里是如何用R 包psych做线性降维的演示&#xff0c;其中也有原理的简述。 为什么要做线性降维&#xff1f; 因为下一步的聚类分析需要这里的降维结果作为输入。降维做的好…

14-3-进程间通信-消息队列

前面提到的管道pipe和fifo是半双工的&#xff0c;在某些场景不能发挥作用&#xff1b; 接下来描述的是消息队列&#xff08;一种全双工的通信方式&#xff09;&#xff1b; 比如消息队列可以实现两个进程互发消息&#xff08;不像管道&#xff0c;只能1个进程发消息&#xff…

vulnhub靶机Misdirection

环境准备 下载链接&#xff1a;https://download.vulnhub.com/misdirection/Misdirection.zip 解压后双击ovf文件导入虚拟机 网络&#xff1a;DHCP、NAT、192.168.100.0/24网段 信息收集 主机发现 192.168.100.133是新增的ip 端口扫描 发现开放了以上端口&#xff0c;继续…

【Java笔试强训 28】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;猴子分桃…

【Python入门】Python搭建编程环境-安装Python3解释器(内含Windows版本、MacOS版本、Linux版本)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

与其焦虑被 AI 取代或猜测前端是否已死, 不如看看 vertical-align 扎实你的基础!!!

与其焦虑被 AI 取代或猜测前端是否已死, 不如看看 vertical-align 扎实你的基础!!! vertical-align 设置 display 值为 inline, inline-block 和 table-cell 的元素竖直对齐方式. 从 line-height: normal 究竟是多高说起 我们先来看一段代码, 分析一下为什么第二行的行高, 也就…

D. Mysterious Crime(单个位置贡献)

Problem - D - Codeforces Acingel是一个小镇。这里只有一位医生——Miss Ada。她非常友善&#xff0c;没有人曾经对她说过坏话&#xff0c;所以谁能想到Ada会在她的房子里被发现死亡&#xff1f;世界著名侦探Gawry先生被任命查找罪犯。他询问Ada的邻居关于那个不幸的日子里拜访…

Java回收垃圾的基本过程与常用算法

目录 一、基本概述 二、垃圾分类 基本背景 举例说明各种引用类型的作用 强引用&#xff08;Strong Reference&#xff09; 软引用&#xff08;Soft Reference&#xff09; 弱引用&#xff08;Weak Reference&#xff09; 虚引用&#xff08;Phantom Reference&#xff…

广搜的优化技巧(备赛中)

A.电路维修 这道题我们对于每一个点都有四个方向&#xff0c;分别为 char op[]{"\\/\\/"}; 如果我们当前点到下一个点的方向不是对应的方向时我们的distance就加1&#xff0c;因为我们要求最优距离&#xff0c;所以我们采取一个小贪心的法则&#xff0c;每一次我们将…

「神州数码DCN」SAVI在IPV6环境下的应用

前言 介绍 ISIS&#xff0c;中间系统到中间系统的网络协议&#xff0c;最初是OSI组织为了他的CLNP&#xff08;类似于TCP/IP中的IP网络&#xff09;而设计的动态路由协议&#xff0c;后IETF对其进行修改和填充&#xff0c;现可以在TCP/IP和OSI环境中使用&#xff0c;称为&…

JavaWeb学习------jQuery

JavaWeb学习------jQuery jQuery函数库下载 jQuery函数库下载官网&#xff1a;Download jQuery | jQuery配套资料&#xff0c;免费下载 链接&#xff1a;https://pan.baidu.com/s/1aXBfItEYG4uM53u6PUEMTg 提取码&#xff1a;6c9i 然后下载&#xff1f; 来到官网&#xf…

Spark 1:Spark基础入门

Spark是什么 定义&#xff1a;Apache Spark是用于大规模数据&#xff08;large-scala data&#xff09;处理的统一&#xff08;unified&#xff09;分析引擎。 Spark 借鉴了 MapReduce 思想发展而来&#xff0c;保留了其分布式并行计算的优点并改进了其明显的缺陷。让中间数据存…

Winform从入门到精通(36)—ColorDialog(史上最全)

文章目录 前言一、属性1、AllowFullOpen2、AnyColor3、Color4、FullOpen5、ShowHelp6、SolidColorOnly7、Tag二、事件1、HelpRequest前言 当我们需要设置某个控件的颜色时,并且需要弹出一个可以选择颜色的对话框时,这时候就需要使用ColorDialog 一、属性 1、AllowFullOpen…

详解正则化

&#xff08;一&#xff09;正则化目的 防止过拟合现象&#xff0c;通过降低模型在训练集上的精度来提高其泛化能力&#xff0c;从而增加正则项 常见的降低过拟合方法 ■增加数据集的数据个数。数据量太小时&#xff0c;非常容易过拟合&#xff0c;因为 小数据集很容易精确拟…