C++必修:布隆过滤器的提出与实现

news2024/9/22 3:38:27

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. 布隆过滤器的引入

在我们注册游戏或者社交账号时,我们可以自己设置昵称,但为了保证每个用户昵称的唯一性,我们必须检测输入的昵称是否被使用过,这本质其实就是一个key的模型。一般而言,我们有两种解决方案:
方案一:

用红黑树或者哈希表存储相关数据,当判断一个数据是否存在时,可以极快的效率在红黑树或哈希表中查找。

方案二:

用位图将存储相关书籍,虽然位图只能存储整型数据,但我们可以通过一些哈希算法将字符串转换成整型,比如BKDR哈希算法。这种方法同样也能以极快的效率查找数据。

但是这两种方案其实都有一些缺点,当数量太大时因为红黑树与哈希表要存储相关信息,内存会不足,而如果用位图存储,虽然节约了大量空间,但是一个无符号整数最大值为4294967295,而字符串的种类却是无限的,以无限对有限,无论哪种哈希算法都必然会导致哈希冲突。
所以为了解决这个问题,就有人提出一种结构——布隆过滤器。

2. 布隆过滤器的概念

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

  • 布隆过滤器是位图的变形与延伸,虽无法避免哈希冲突,但可降低误判概率。
  • 在布隆过滤器中,当一个数据映射到位图时,会使用多个哈希函数将其映射到多个比特位。判断数据是否在位图中,需依据这些哈希函数计算对应的比特位,若这些比特位全为 1,则判定数据存在,否则判定数据不存在。
  • 布隆过滤器采用多个哈希函数进行映射,目的是降低哈希冲突概率。单个哈希函数产生冲突的概率可能较大,而多个哈希函数同时产生冲突的概率则较小。
  • 布隆过滤器在判断一个数据是否存在时如果存在可能出现误判,但是如果不存在那一定没有误判。

比如说我们分别将Betty1Betty2Betty3利用三个哈希函数映射进位图中,其分布可能为:

其中Betty1Betty2Betty3这三个字符串都没有发生冲突。而如果三个哈希函数的计算结果都相同的话,那就可能造成哈希冲突,比如接下来的Betty1Betty2

其中如果某个位置是0的话,那该数据一定不存在因为没有任何数据指向这个位置。
为了降低布隆过滤器的误判率,有人就对布隆过滤器的长度与哈希函数的个数做了研究。得到一个公式 m = − n ln ⁡ p / ( l n 2 ) 2 , k = m l n 2 / n m=-n \ln p/(ln 2)^2 , k = mln 2/n m=nlnp/(ln2)2,k=mln2/n。其中k为哈希函数个数,m为布隆过滤器长度,n为插入的元素个数,p为误判率。
如果使用的哈希函数为3个,那么根据公式 m = n k / ln ⁡ 2 ≈ 4 n m=nk/\ln 2≈4n m=nk/ln24n,也就是说当布隆过滤器的长度是插入元素个数的4倍时误差最小。

3. 布隆过滤器的实现

3.1 布隆过滤器的结构

首先布隆过滤器肯定是一个模版类,有一个非类型模版参数控制长度,默认处理的对象为string。默认也提供三个哈希函数,其成员变量也是一个位图。

template<size_t N, class K=string,class Hash1=BKDRHash, 
class Hash2 = APHash, class Hash3 = DJBHash>
class BloomFilter
{
public:
	//成员函数
private:
	bitset<N> _bit;
};

3.2 布隆过滤器的插入

布隆过滤器的插入即通过不同的哈希函数计算对应的下标,然后进行相应的映射关系。

//成员函数
void set(const K& key)
{
    //计算机对应的下标
    size_t hashi1 = Hash1()(key)% N;
    size_t hashi2 = Hash2()(key)% N;
    size_t hashi3 = Hash3()(key)% N;
    _bit.set(hashi1);
    _bit.set(hashi2);
    _bit.set(hashi3);
}

3.3 布隆过滤器的测试

布隆过滤器的删除即通过不同的哈希函数计算对应的下标,然后检测相应下标的状态。如果有一个下标不存在,那么这个数据肯定不存在,但是如果都存在也有可能有误差。

bool test(const K& key)
{
    size_t hashi1 = Hash1()(key) % N;
    size_t hashi2 = Hash2()(key) % N;
    size_t hashi3 = Hash3()(key) % N;
    if (!_bit.test(hashi1)||!_bit.test(hashi2)||!_bit.test(hashi3))
    {
        //一定不存在
        return false;
    }
    return true;//可能存在误判
}

3.4 布隆过滤器的删除

布隆过滤器一般不能直接支持删除工作,原因是删除一个元素可能影响其他元素,如删除Betty1可能导致Betty2也被误删,因为二者可能在多个哈希函数计算出的比特位上有重叠。
如果一定要支持删除操作的话,一种支持删除的方法是将布隆过滤器的每个比特位扩展成小计数器(一个下标对应多个比特位),插入元素时给相应位置计数器加一,删除时减一,以多占用几倍存储空间为代价实现删除操作。但是仍然会存在几个问题:

  • 如果你的位数给的不合适,可能某一次次数更新之后就会溢出,造成计数回绕(计数器值增加到达其最大范围后,再次增加会导致计数器值重新回到初始状态)。
  • 并且查找一个元素的时候无法确认该元素是否真的存于布隆过滤器中。因为我们删除一个元素的时候一定要确保它是存在的,再去删除(减去对应位置的次数),不存在是不能删除的,但是判断一个元素是否在布隆过滤器中是可能误判的。所以我们在删除一个元素的时候无法确认它是否存在

所以一般而言布隆过滤器的删除操作是不可行的。

4. 布隆过滤器的优缺点

一、布隆过滤器的优点

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

二、布隆过滤器的缺点

  1. 存在误判率:有假阳性,不能准确判断元素是否在集合中,可通过建立白名单补救。
  2. 不能获取元素本身。
  3. 一般情况下不能删除元素,采用计数方式删除可能会存在计数回绕问题。

5. 布隆过滤器的应用场景

一般而言使用布隆过滤器的前提是,布隆过滤器的误判不会对业务逻辑造成影响。以下是一个布隆过滤器的具体使用场景:

在电商平台的商品推荐系统中,当用户浏览商品时,系统会根据用户的历史浏览记录和购买行为进行个性化推荐。 假设用户的历史浏览记录和购买行为数据存储在数据库中,直接遍历数据库进行推荐计算会非常耗时,影响用户体验。这时可以使用布隆过滤器,将用户已经浏览过或购买过的商品 ID 全部添加到布隆过滤器当中。 当用户打开某个商品页面时,系统首先在布隆过滤器中查找该商品 ID。如果在布隆过滤器中查找后发现该商品 ID 不存在,说明用户没有浏览过或购买过这个商品,可以将其作为潜在的推荐商品进行初步推荐,避免了磁盘 IO。如果在布隆过滤器中查找后发现该商品 ID 存在,此时还需要进一步访问磁盘,复核用户是否真的浏览过或购买过该商品,因为布隆过滤器可能会有误判。 由于大部分情况下,系统推荐给用户的商品都是用户没有接触过的,所以在布隆过滤器中查找后通常都是找不到的,此时就避免了进行磁盘 IO。而只有在布隆过滤器误判或用户忘记自己浏览过或购买过某个商品的情况下,才需要访问磁盘进行复核。

6. 源码

#pragma once
#include<bitset>
struct BKDRHash
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (auto ch : s)
		{
			value = value * 131 + ch;
		}
		return value;
	}
};
struct APHash
{
	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 DJBHash
{
	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=BKDRHash,
	class Hash2 = APHash, class Hash3 = DJBHash>
class BloomFilter
{
public:
	//成员函数
	void set(const K& key)
	{
		//计算机对应的下标
		size_t hashi1 = Hash1()(key)% N;
		size_t hashi2 = Hash2()(key)% N;
		size_t hashi3 = Hash3()(key)% N;
		_bit.set(hashi1);
		_bit.set(hashi2);
		_bit.set(hashi3);
	}
	bool test(const K& key)
	{
		size_t hashi1 = Hash1()(key) % N;
		size_t hashi2 = Hash2()(key) % N;
		size_t hashi3 = Hash3()(key) % N;
		if (!_bit.test(hashi1)||!_bit.test(hashi2)||!_bit.test(hashi3))
		{
			//一定不存在
			return false;
		}
		return true;//可能存在误判
	}
private:
	bitset<N> _bit;
};

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

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

相关文章

科学重温柯南TV版:基于B站视频数据分析

麻鸭&#xff0c;四年过去了&#xff0c;失踪人口回归。 第一篇就决定是你了。 看了柯南M27剧场版后&#xff0c;萌生了重温TV版的念头&#xff0c;但是1191集(截止24/8/26)的体量太恐怖了&#xff0c;遂取点巧&#xff0c;综合大V建议(知乎&#xff1b;公众号)和视频网站数据…

基于asp.net的驾校管理系统附源码

这是一个基于asp.net的webform框架开发的BS架构的系统&#xff0c;详情如下&#xff1a; 项目下载链接 链接&#xff1a;https://pan.quark.cn/s/0679e783ef71

【设计模式】创建型模式——抽象工厂模式

抽象工厂模式 1. 模式定义2. 模式结构3. 实现3.1 实现抽象产品接口3.2 定义具体产品3.3 定义抽象工厂接口3.4 定义具体工厂3.5 客户端代码 4. 模式分析4.1 抽象工厂模式退化为工厂方法模式4.2 工厂方法模式退化为简单工厂模式 5. 模式特点5.1 优点5.2 缺点 6. 适用场景6.1 需要…

深入理解OJ编程中的输入输出:11个经典题目详解与技巧分享及stringstream,sort详解

文章目录 1.多组输入计算ab2.给定组数计算ab3.给定组数计算ab&#xff08;如果为0则结束&#xff09;4.计算一些列数的和(第一个数为0时结束)5.计算一些列数的和&#xff08;告诉了有几组&#xff09;6.计算一系列数的和&#xff08;不告知几组和何时结束&#xff0c;每一组第一…

如何评估云服务器提供商可靠性与信誉度

在云计算时代&#xff0c;选择一个可靠和信誉良好的云服务器提供商对于个人用户和企业来说至关重要。以下是评估云服务器提供商可靠性与信誉度的关键指标和方法&#xff1a; 1. 服务水平协议&#xff08;SLA&#xff09;&#xff1a; 可用性承诺&#xff1a; 查看云服务器提供…

服务器内存飙升分析小记

1. 写在最前面 这个繁忙的八月真的是转瞬即逝&#xff0c;我明明感觉似乎好像才八月刚开始&#xff0c;但是其实已经到了八月的尾巴。这个月本来想抽空整理一下学习 AI 模型相关的东西&#xff0c;奈何每天不是在查问题就是在查问题的路上&#xff0c;不是在修 Bug 就是在写 B…

AI Lossless Zoomer v3.1.0.0 — 超实用的AI无损图片放大工具

AI Lossless Zoomer 是一款基于腾讯开源 Real-ESRGAN 算法的 AI 图片无损放大工具&#xff0c;支持多线程和批量处理&#xff0c;具备自定义输出格式和路径等高级设置选项&#xff0c;并允许用户选择不同的 AI 引擎进行图片放大处理。此版本修复了一些小 bug&#xff0c;并增加…

Jhipster应用,cdn加速方案。

Jhipster, 采用springbootwebfluxreacttypescript技术栈。项目部署是采用k8shelm 部署在GCP上的&#xff0c;所以这个单体项目幕后是跑在pod上的。 项目上线后&#xff0c;发现单页面应用加载速度很慢&#xff0c;如图所示长时间处于加载状态&#xff1a; 仔细分析一下原因&am…

ESXi服务器无法安装Windows11:“不符合此版本的Windows所需最低系统要求“

目录 一、问题描述1.使用环境2.问题截图3.问题解析 二、解决方法Ⅰ1.按 ShiftF10 弹出命令提示符2.在弹出的Dos框中输入regedit&#xff0c;回车&#xff0c;进入注册表。3.打开HKEY_LOCAL_MACHINE\SYSTEM\Setup&#xff0c;并新建 LabConfig 的项&#xff0c;在 LabConfig 下创…

51单片机-静态数码管显示

时间&#xff1a;2024.8.29 作者&#xff1a;Whappy 目的&#xff1a;学习51单片机 代码&#xff1a; #include <REGX52.H> #include "intrins.h"unsigned char NixieTable[] {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79…

C++系列-STL容器之deque

STL容器之deque deque概括deque与vector内存管理的区别vector内存分配方式deque内存分配方式 deque与vector随机访问效率的区别deque与vector插入和删除操作的区别deque与vector适用场景 deque的构造函数deque的构造函数举例 deque的赋值操作deque容器的大小操作deque容器的插入…

瑞芯微RK3566开发板USB OTG模式介绍及命令切换,触觉智能EVB3566主板鸿蒙硬件厂商

一、USB OTG的模式 host模式&#xff08;下行&#xff09;&#xff1a;为u盘等设备供电&#xff0c;不可以进行调试&#xff0c;连接adb或者烧录等操作。 device模式&#xff08;上行&#xff09;&#xff1a;可以进行调试&#xff0c;连接adb或者烧录等操作&#xff0c;即US…

Delphi5实现主要——明细型数据库应用

文章目录 效果图主要——明细型数据库特点 数据库实现方式完整代码 效果图 主要——明细型数据库 在Delphi中&#xff0c;主要——明细型数据库是一种数据库应用程序的设计模式&#xff0c;它涉及到多个数据库表之间的关联操作&#xff0c;以实现对复杂数据结构的有效管理。这…

数据结构(邓俊辉)学习笔记】串 16——Karp-Rabin算法:串即是数

文章目录 1. 化串为数2. 凡物皆数3. 亦是数 1. 化串为数 接下来的这节&#xff0c;我们再来讨论一种十分另类的串匹配算法&#xff0c;也就是所谓的 Karp-Rabin 算法。回顾此前所介绍的几种串匹配算法&#xff0c;我们所面临的难题是一样的。也就是说在这里&#xff0c;我们每次…

ES配合高德地图JS-API实现地理位置查询

目录 实现功能点 技术选型 具体实现 Vue3整合高德地图JS API-2.0 添加商户&#xff1a;前端 添加商户&#xff1a;后端/ES 查询用户当前地理坐标 获取附近&#xff08;指定距离&#xff09;的商户 总结/测试Demo代码地址 测试概述&#xff1a;用户使用高德地图组件获取商户…

GPT实现的adb shell命令实现某音自动点赞和关注

摘要:这个可能是没啥用的自动点赞和关注功能,自娱自乐为主哈 具体可行性步骤如下: 1.打开手机的开发者选项,将指针位置的设置开关打开,目的是看触屏时的坐标值 2.随便打开一个抖音,找到点赞的爱心图标的坐标轴,并记下来,待会有用 备注:尽量获取爱心尖端的坐标值,由…

erlang学习:用OTP构建系统2,警报管理

今日学习用OTP构建系统的警报管理&#xff0c; 首先进行配置错误记录器 [{sasl,[{sasl_error_logger, false},{error_logger_mf_dir, "/code/erlang/erlangstudy"},{error_logger_mf_maxbytes, 10485760},{error_logger_mf_maxfiles, 10}]} ].警报处理器gen_event的…

【Material-UI】Slider 组件中的 Discrete Sliders 详解

文章目录 一、Slider 组件概述1. 组件介绍2. Discrete Sliders 的特点 二、Discrete Sliders 的基本用法1. step 属性2. marks 属性3. valueLabelDisplay 属性 三、深入理解 Discrete Sliders 的配置1. 自定义刻度标记2. 限制可选值3. 设置较小的步长4. 始终显示值标签 四、应用…

Win10+GTX1050Ti安装Pytorch

目的 本文主要记录自己安装pytorch过程。 环境&#xff1a;win10 pycharm 显卡&#xff1a;GTX1050Ti 过程记录 1、确认pytorch版本 打开pytorch官网&#xff1a;https://pytorch.org/ 选择stable 2.3.0 版本&#xff0c;CUDA有11.8及12.1&#xff0c;我们看看GTX1050Ti支持…

发红包案例(java)

User类创建 public class User {private String name;private int money;public User(){}public User(String name,int money){this.namename;this.moneymoney;}public void show(){System.out.println("Name:"name" Money:"money);}public String getNam…