哈希应用之布隆过滤器

news2024/11/17 6:21:09

文章目录

  • 1.介绍
    • 1.1百度搜索
    • 1.2知乎好文
    • 1.3自身理解
  • 2.模拟实现
    • 2.1文档阅读
    • 2.2代码剖析
  • 3.误判率的研究
  • 4.布隆过滤器的应用
    • 4.1如何找到两个分别有100亿个字符串的文件的交集[只有1G内存].分别给出精确算法和近似算法
    • 4.2如何扩展BloomFilter使得它支持删除元素的操作
  • 5.整体代码

在这里插入图片描述

1.介绍

1.1百度搜索

在这里插入图片描述
在这里插入图片描述

1.2知乎好文

详解布隆过滤器的原理,使用场景和注意事项

在这里插入图片描述

1.3自身理解

有了哈希和位图 为什么还要搞一个布隆过滤器?

位图只能处理海量整形的数据 当数据为字符串类型且大量时 目前还没有学习一个可以处理的结构

布隆过滤器的思想

当数据为字符串时 数据是很难处理的 例如当大量字符串需要处理 假定每个字符串的个数都为10 那么这些字符串可能有256^10种可能存在 [ASCII表共有256种字符] 如果用位图的思想 总共能存2的32次方种状态 怎么解决? 我们想到用2个甚至更多的比特位来标识一种字符串 但是这样仍不能避免冲突

布隆过滤器的核心

  1. 布隆过滤器最大限度减少了冲突 一个数据存在可能是误判的 但一个数据不存在一定是不存在
  2. 以一个值映射多个位置的这种方法来降低误判率
  3. 一个值映射多个位置是用哈希函数来映射的 即用3个值来映射就需要3个哈希函数
  4. 哈希函数越多 误判率越低 但所用空间越大

用一个简单的场景来介绍

在生活中我们经常会遇到这样一个页面
在这里插入图片描述
在这里插入图片描述

这里输入的就是一个字符串类型 如果我们用之前的算法处理 效率太过低下 因为这个游戏要求昵称单一即不能重复 当用户输入一个昵称[字符串] 游戏要把这个字符串和王者荣耀现有的所有字符串比较一遍查看用户输入的这个字符串是否被允许使用 而王者荣耀现有用户为在这里插入图片描述
如果仅仅开始注册输入一个昵称就需要这么长时间 我想大多数人骂一句之后直接卸载 在这里插入图片描述
那怎么处理呢? 此时布隆过滤器的作用就大展身手了 布隆过滤器的查询效率是O(1)当用户输入的昵称不可用时 通过布隆过滤器可以明确的得到==[您输入的昵称不可用]==这样一个结果 但是有人问了 布隆过滤器虽然可以肯定的判断数据不存在 但是会误判存在 那怎么办?面对此种情况 大佬这样设计 当输入的数据使用布隆过滤器查询时 得到了false的结果 下一半会再到磁盘上的数据库查询以便得到确切结果 又有人说了 不如直接去数据库 一步到位 我们来回答这个问题:1.昵称查询状态是不存在即可用时 这个结果是确切的 不存在就是不存在 可以用 当昵称判断结果是存在即不可用的 即便这个结果可能误判 一个昵称 用户再输入一个就完了 2.不同的是 用户输入一个身份证号[以下简称id]时 查询不存在即可用 [这样的情况是大多数的 因为用户知道自己的id] 当查询存在时 设计者此时设计了 再去数据库查找这一步骤就可解决[这样的情况是极少见的 因为用户自己知道到底有没有用过这个id去注册 忘记的情况也很少 即便忘记 设计者也有后续解决办法即去数据库再查找 所以整体看 先使用布隆过滤器"过滤"这样的效率无疑是最优的 "布隆过滤器"的名称想必各位也心中有数]

2.模拟实现

2.1文档阅读

字符串Hash函数对比

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2代码剖析

在这里插入图片描述

在这里插入图片描述

3.误判率的研究

//测试误判率
void test_bloomfilter2()
{
	srand(time(0));
	const size_t N = 10000;
	BloomFilter<N> bf;  //4w个比特位

v1:url1 url2 url3...url9999///
	vector<string> v1;
	string url = "https://www.gitee.com/Ape-LHR/apes-warehouse/547993.html";
	//v1存储内容:url1 url2 url3....url9999共N个
	for (size_t i = 0; i < N; ++i)
	{
		v1.push_back(url + to_string(i));
	}

	//把v1里面的每个字符串插入到布隆过滤器
	for (auto& str : v1)
	{
		bf.insert_setone(str);
	}

/v2:url10000 url10001 url10002...url19999
	//v2存储和v1前缀相同 后缀不同的字符串
	vector<string> v2;
	for (size_t i = N; i < 2 * N; ++i)
	{
		v2.push_back(url + to_string(i));
	}
	size_t count1 = 0;
	for (auto& str : v2)
	{
		if (bf.judge(str))
			++count1;
	}
	double rate1 = (double)count1 / (double)N;
	cout << "相似字符串误判率  :" << rate1 * 100 << "%" << endl;
///
	//v3存储和v1前缀和后缀均有较大差异
	vector<string> v3;
	string url2 = "https://www.csdn.net/?spm=1001.2014.3001.4476";
	for (size_t i = 0; i < N; ++i)
	{
		v3.push_back(url2 + to_string(i + rand()));
	}

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

num增加 开的比特位更多 数据分布越分散 误判率越低 也可以通过增加哈希函数的个数[效果不佳]

在这里插入图片描述

4.布隆过滤器的应用

4.1如何找到两个分别有100亿个字符串的文件的交集[只有1G内存].分别给出精确算法和近似算法

估算大小

假设一个字符串占50字节 100亿个字符串占5000亿字节 ≈ 500G

思路

将每个文件分成1000份 每份0.5G 此时去查找交集

上述思路的问题

每一个A小文件都要去B小文件查找 共1000 * 1000次查找 效率极低

解决办法

对于A文件中的每一个字符串通过特定的函数计算出Hashi = hashfunc(string) % 1000
1000个文件即1000个小容器 字符串依据函数计算出下标 不同的下标进入不同的文件 假定A的1000个小文件分别是a0 a1 a2 a3…a999[B小文件亦然] 此时只需比较a0和b0 a1和b1…ai和bi

为什么只需比比较ai和bi?

A和B中成为交集的要求是string相同 如果string相同 因为hashfunc相同他们进入的文件编号一定相同 若要查找交集 只需查找比较编号相同的文件即可

存在问题

此时的切分不再是平均切分 通过这样的哈希切分 存在这样一个情况 a50号文件大小50M b50号文件5G

能不能将超出预期大小的文件再次进行哈希切分?

不能.因为存在这样一种情况: b50号文件大小5G 但重复字符串有4G 无论怎么二次切分 都会存在一个超出预期大小的文件

解决办法

利用unordered_set/set不可存储重复值的特点 将超出预期的小文件中的字符串依次读取
如果文件中的字符串都可以成功插入 set所能存储字节数有限 超出预期的文件都能插入成功 说明 该文件存在大量重复string
如果插入工程抛出内存异常 则该超出预期的文件中有大量非重复string 以至于没插完内存就不够了

4.2如何扩展BloomFilter使得它支持删除元素的操作

布隆过滤器不支持删除操作 因为一个位置可能是多个数据的子位
假如 x y 存在 且二者位置有交集 删除x后 y也被认为不在 而y实际存在
若非要支持删除 则可以:原来每个位是0/1 现在我们让每3个位作为一个位置
111最大可以标识7 当不存在置0 每存在一次++ 删除则-- 存在的问题 当数据量过大 7不一定能避免重复
如果继续增大 比如用4个位 1111最大标识15 但是所需空间增大 可能溢出

5.整体代码

#pragma once
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <functional>
#include <assert.h>
using namespace std;

//一个比特位变标识两种状态 0 1
template<size_t N>
class bitmap
{
public:

	//构造函数
	bitmap() 
	{
		//开空间 初始化成0
		_bits.resize(N / 8 + 1, 0);
	} 

	//插入: 将数x映射的位 置成1
	void insert_setone(size_t x)
	{
		//第i个字节  0 1 2 3 ...
		size_t i = x / 8;
		//第i个字节的第j个位
		size_t j = x % 8;

		//利用或等 第j位-置1 其余位-不变  
		_bits[i] |= (1 << j);  //左移:并不是向左移而是向高位移
	} 

	//删除: 将数x映射的位 置成0
	void erase_setzero(size_t x)
	{
		//第i个字节  0 1 2 3 ...
		size_t i = x / 8;
		//第i个字节的第j个位
		size_t j = x % 8;

		//利用与等 第j位-置0 其余位-不变 
		_bits[i] &= ~(1 << j);
	}

	//判断: 判断数x是否存在 
	bool judge(size_t x)
	{
		//第i个字节  0 1 2 3 ...
		size_t i = x / 8;
		//第i个字节的第j个位
		size_t j = x % 8;

		//假定数x存在 那么第j位应为1
		//_bits[i]访问到的是 数x所在第i个字节的整体数
		return _bits[i] & (1 << j);
	}

private:
	vector<char> _bits;
};

 测试函数 ///

void test_bitmap1()
{
	bitmap<100> bm;
	bm.insert_setone(10);
	bm.insert_setone(11);
	bm.insert_setone(15);

	cout << bm.judge(10) << endl;
	cout << bm.judge(15) << endl;

	bm.erase_setzero(10);

	cout << bm.judge(10) << endl;
	cout << bm.judge(15) << endl;

	bm.erase_setzero(10);
	bm.erase_setzero(15);

	cout << bm.judge(10) << endl;
	cout << bm.judge(15) << endl;
}

void test_bitmap2()
{
	//4294967295
	//bitset<-1> bm;
	bitmap<0xFFFFFFFF> bm;
}



//Brian Kernighan与Dennis Ritchie《The C Programming Language》
struct BKDR_Hash
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (auto& ch : s)
		{
			value = value * 31 + ch;
		}
		return value;
	}
};

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

//Daniel J. Bernstein教授
struct DJB_Hash
{
	size_t operator()(const string& s)
	{
		size_t value = 5381;
		for (auto ch : s)
		{
			value += (value << 5) + ch;
		}
		return value;
	}
};


template
<
	size_t N,               //插入数据个数
	class K = string,       //数据类型
	class Hash1 = BKDR_Hash,
	class Hash2 = AP_Hash,
	class Hash3 = DJB_Hash
>
class BloomFilter
{
public:
	//插入
	void insert_setone(const K& key)
	{
		size_t len = num * N;
		size_t hash1 = Hash1()(key) % len;
		_bm.insert_setone(hash1);

		size_t hash2 = Hash2()(key) % len;
		_bm.insert_setone(hash2);

		size_t hash3 = Hash3()(key) % len;
		_bm.insert_setone(hash3);

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

	
	//判断是否存在
	bool judge(const K& key)
	{
		//但凡一个位置没有 一定不存在

		size_t len = N * num;

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

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

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

		return true;
	}
private:
	static const size_t num = 4; //倍数
	bitmap<num * N> _bm;         //布隆过滤器长度≈4.33 * 数据个数
};

//测试插入、判断函数
void test_bloomfilter1()
{
	BloomFilter<100> bf;
	//插入
	bf.insert_setone("sort");
	bf.insert_setone("bloom");
	bf.insert_setone("hello world hello bit");
	bf.insert_setone("test");
	bf.insert_setone("etst");
	bf.insert_setone("estt");
	//判断存在[有误判]
	cout << bf.judge("sort") << endl;
	cout << bf.judge("bloom") << endl;
	cout << bf.judge("hello world hello bit") << endl;
	cout << bf.judge("etst") << endl;
	cout << bf.judge("test") << endl;
	cout << bf.judge("estt") << endl;
	//判断不存在[精确]
	cout << bf.judge("ssort") << endl;
	cout << bf.judge("tors") << endl;
	cout << bf.judge("ttes") << endl;
}

//测试误判率
void test_bloomfilter2()
{
	srand(time(0));
	const size_t N = 10000;
	BloomFilter<N> bf;  //4w个比特位

v1:url1 url2 url3...url9999///
	vector<string> v1;
	string url = "https://www.gitee.com/Ape-LHR/apes-warehouse/547993.html";
	//v1存储内容:url1 url2 url3....url9999共N个
	for (size_t i = 0; i < N; ++i)
	{
		v1.push_back(url + to_string(i));
	}

	//把v1里面的每个字符串插入到布隆过滤器
	for (auto& str : v1)
	{
		bf.insert_setone(str);
	}

/v2:url10000 url10001 url10002...url19999
	//v2存储和v1前缀相同 后缀不同的字符串
	vector<string> v2;
	for (size_t i = N; i < 2 * N; ++i)
	{
		v2.push_back(url + to_string(i));
	}
	size_t count1 = 0;
	for (auto& str : v2)
	{
		if (bf.judge(str))
			++count1;
	}
	double rate1 = (double)count1 / (double)N;
	cout << "相似字符串误判率  :" << rate1 * 100 << "%" << endl;
///
	//v3存储和v1前缀和后缀均有较大差异
	vector<string> v3;
	string url2 = "https://www.csdn.net/?spm=1001.2014.3001.4476";
	for (size_t i = 0; i < N; ++i)
	{
		v3.push_back(url2 + to_string(i + rand()));
	}

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

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

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

相关文章

第十章 字符串和日期

1.字符串 1.1.String 1.1.1.String特性 代表字符串。Java 程序中的所有字符串字面值&#xff08;如 "abc" &#xff09;都作为此类的实例实现&#xff1b;String是一个final类&#xff0c;代表不可变的字符序列,不可被继承&#xff1b;字符串是常量&#xff0c;用&…

高级网络调试技巧:使用Charles Proxy捕获和修改HTTP/HTTPS请求

今天我将与大家分享一种强大的网络调试技巧&#xff0c;那就是使用Charles Proxy来捕获和修改HTTP/HTTPS请求。如果您是一位开发人员或者网络调试爱好者&#xff0c;那么这个工具肯定对您有着很大的帮助。接下来&#xff0c;让我们一起来学习如何使用Charles Proxy进行高级网络…

1200*C1. k-LCM (easy version)(找规律)

Problem - 1497C1 - Codeforces 解析&#xff1a; 找规律即可&#xff0c;分为偶数的一半是偶数、偶数的一半是奇数、奇数三种情况 分别为 &#xff08;n/2&#xff0c;n/4&#xff0c;n/4&#xff09;&#xff08;n/2-1&#xff0c;n/2-1&#xff0c;2&#xff09;&#xff08…

canvas基础2 -- 形状

七巧板 七巧板本质上就是 分别由几个直线 拼成一个个图形&#xff0c;再将这些图形结合起来 var tangram [{ p: [{ x: 0, y: 0 }, { x: 800, y: 0 }, { x: 400, y: 400 }], color: "#caff67" },{ p: [{ x: 0, y: 0 }, { x: 400, y: 400 }, { x: 0, y: 800 }], col…

为什么3ds max渲染效果图有噪点?点进来,CG Magic告诉您!

大家在使用3ds max渲染效果图时&#xff0c;可能渲染结果往往会出现的都是不真实&#xff0c;有小伙伴会问如何使3dmax渲染效果图真实呢&#xff1f; 不真实就算了&#xff0c;渲染过程中&#xff0c;会出现3Dmax渲染噪点多这类问题。 什么原因3ds max渲染效果图有噪点呢&a…

满足新能源三电系统气密和电性能测试的E10系列多功能电连接器

在新能源汽车的测试领域中&#xff0c;三电系统的测试是质量管控过程中非常重要的组成部分。无论是防水防尘的气密性测试&#xff0c;还是EOL/DCR等电性能相关的测试&#xff0c;都是确保新能源汽车正常工作中不可缺少的一部分。 在以往的测试中&#xff0c;每种测试都是独立的…

海外问卷调查是不是很枯燥?

嘿&#xff0c;大家好&#xff01;我是橙河&#xff0c;这几年海外问卷调查这个项目很火热&#xff0c;这个项目看起来很高大上&#xff0c;实际上门槛并不高&#xff0c;甚至做的过程很枯燥。 海外问卷调查这个项目&#xff0c;被很多人称为“网络搬砖”&#xff0c;形容的也…

C++day05(运算符重载、静态成员、继承)

今日任务 1> 思维导图 2> 多继承代码实现沙发床 代码&#xff1a; #include <iostream>using namespace std; class Sofa { private:string sitting; public:Sofa() {}Sofa(string s):sitting(s){cout << "Sofa 有参" <<endl;}void show…

基于 AdaFace 提供适合低质量人脸识别的人脸特征向量输出服务

写在前面 工作原因,简单整理理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其它的路都是不完整的&#xff0c;是人的逃避方式&#xff0c;是对大众理想的懦弱回…

如何选择高防CDN和高防IP?

目录 前言 一、对高防CDN的选择 1. 加速性能 2. 抗攻击能力 3. 全球覆盖能力 4. 可靠性和稳定性 二、对高防IP的选择 1. 防御能力 2. 服务质量 3. 安全性 4. 价格 三、高防CDN和高防IP的优缺点对比 1. 高防CDN的优缺点 2. 高防IP的优缺点 总结 前言 随着互联网…

04-RocketMQ源码解读

目录汇总&#xff1a;RocketMQ从入门到精通汇总 上一篇&#xff1a;03-RocketMQ高级原理 这一部分&#xff0c;我们开始深入RocketMQ的源码。源码的解读是个非常困难的过程&#xff0c;每个人的理解程度都会不一样&#xff0c;也不太可能通过讲解把其中的细节全部讲明白。我们今…

【Zabbix】Zabbix学习笔记

现在Zabbix Server存在的问题&#xff1a; 问题1&#xff1a; Zabbix server: Utilization of discoverer processes over 75% 问题2&#xff1a; Zabbix server: Utilization of icmp pinger processes over 75% 优化的解决办法是修改配置文件把Discovery和Pinger进程数量调大…

windows10下 iperf3测试带宽

iperf3下载网址&#xff1a;iPerf - Download iPerf3 and original iPerf pre-compiled binaries 可以用来测试TCP以及UDP带宽质量 通俗来说是用来测试网速的 准备&#xff1a;两台设备 1. 根据自己的设备选择下载工具&#xff08;两台都要有&#xff0c;这里我用的Window…

R语言的计量经济学实践技术应用

计量经济学通常使用较小样本&#xff0c;但这种区别日渐模糊&#xff0c;机器学习在经济学领域、特别是经济学与其它学科的交叉领域表现日益突出&#xff0c;R语言是用于统计建模的主流计算机语言&#xff0c;在本次培训中&#xff0c;我们将从实际应用出发&#xff0c;重点从数…

零售数据分析报表这样做,老板狂点赞!

随着零售数据量的增加和企业精细化管理、数字化运营的转变&#xff0c;零售数据分析报表也需要及时转变&#xff0c;以更加高效、直观、灵活的方式来分析呈现数据&#xff0c;辅助数字化运营决策。接下来要介绍的几张BI零售数据分析就能很好地满足大数据时代&#xff0c;智能零…

如何解决笔记本上有GPU但是torch.cuda.device_count()==0的问题?

安装CUDA Toolkit 查看显卡版本 打开NVIDIA控制面板->帮助->系统信息->组件->NVCUDA64.DLL&#xff0c;查看其版本号。我的是12.0.151。 更新显卡驱动 打开控制面板->所有控制面板项->设备管理器->显示适配器->右键NVIDIA**->选择更新驱动程序…

Chrome 同站策略(SameSite)问题

问题产生 问题复现&#xff1a; A项目页面使用 iframe 引用了B项目 B项目登录页面输入账号密码后点击登录 无法跳转 尝试解决&#xff1a; 在B项目修改了跳转方式 但无论是 this.$router.push 还是 window.herf 都无法实现跳转在iframe中使用 sandbox 沙箱属性 无法更换浏览器…

网页游戏的开发框架

网页游戏开发通常使用不同的开发框架和技术栈&#xff0c;以创建各种类型的游戏&#xff0c;从简单的HTML5游戏到复杂的多人在线游戏&#xff08;MMO&#xff09;等。以下是一些常见的网页游戏开发框架和它们的特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&a…

英国物联网初创公司【FourJaw】完成180万英镑融资

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于英国谢菲尔德的物联网初创公司【FourJaw】今日宣布已完成180万英镑融资。 本轮融资完成后&#xff0c;FourJaw的总融资金额已达400万英镑&#xff0c;本轮融资的投资机构包括&#xff1a;…

华为发布LampSite X室内数字化创新解决方案,释放数字世界无限潜能

【阿联酋&#xff0c;迪拜&#xff0c;2023年10月11日】2023全球移动宽带论坛&#xff08;Global MBB Forum 2022&#xff09;期间&#xff0c;华为董事、ICT产品与解决方案总裁杨超斌重磅发布了全新一代5G室内数字化产品解决方案LampSite X系列&#xff0c;助力运营商打开商业…