首页 > 其他分享 >位图和布隆过滤器

位图和布隆过滤器

时间:2024-11-24 11:32:23浏览次数:6  
标签:hash str 布隆 哈希 过滤器 位图 size

目录

一.位图

1.位图的概念

2.位图的实现

3.位图的应用

二.布隆过滤器

1.布隆过滤器的概念

2.布隆过滤器的实现

3.布隆过滤器的优缺点

三.整体代码

1.bitset.h

2.BloomFilter

3.Hash.cpp


一.位图

1.位图的概念

1.面试题

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

  1. 遍历,时间复杂度O(N)
  2. 排序(O(N*logN)),利用二分查找: logN
  3. 位图解决

        数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一 个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在

2.位图概念

        所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的

3.位图的优缺点

优点:节省空间.效率高

缺点:只能处理整形

2.位图的实现

class bitset
{
public:
	bitset(size_t N)
	{
		_bits.resize(N / 32 + 1, 0);
		_num = 0;
	}

	void set(size_t x)
	{
		size_t index = x / 32;//算出映射的位置在第几个整形
		size_t pos = x % 32;//算出x在整形的第几个位置
		_bits[index] |= (1 << pos);//第pos位置为1
		++_num;
	}

	void reset(size_t x)
	{
		size_t index = x / 32;
		size_t pos = x % 32;
		_bits[index] &= ~(1 << pos);//第pos位置为0
		--_num;
	}

	//判断x在不在(x的映射位是否为1)
	bool test(size_t x)
	{
		size_t index = x / 32;
		size_t pos = x % 32;
		return _bits[index] & (1 << pos);
	}
private:
	std::vector<int> _bits;
	size_t _num;//映射存储的多少个数据
};

3.位图的应用

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

二.布隆过滤器

        我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?

  1. 用哈希表存储用户记录,缺点:浪费空间
  2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理 了
  3. 将哈希与位图结合,即布隆过滤器

1.布隆过滤器的概念

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

2.布隆过滤器的实现

布隆过滤器的插入

        布隆过滤器是通过映射到位图中,但是有时候映射的位置可能相同,所以我们通过多个哈希函数来映射位置,不同的哈希函数映射的位置不同,但是误判还是存在的,通过不同的哈希映射只是降低了误判的概率,并不能彻底避免

在这里我们使用三种哈希函数来映射

struct Hashstr1
{
	size_t operator()(const string& str)
	{
		//BKDRHash
		size_t hash = 0;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash *= 131;
			hash += str[i];
		}
		return hash;
	}
};

struct Hashstr2
{
	size_t operator()(const string& str)
	{
		//RSHash
		size_t hash = 0;
		size_t magic = 63689;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash *= magic;
			hash += str[i];
			magic *= 378551;
		}
		return hash;
	}
};

struct Hashstr3
{
	//SDBMHash
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash *= 65599;
			hash += str[i];
		}
		return hash;
	}
};
void set(const K& key)
{
	size_t index1 = Hash1()(key) % _N;
	size_t index2 = Hash2()(key) % _N;
	size_t index3 = Hash3()(key) % _N;

	cout << index1 << endl;
	cout << index2 << endl;
	cout << index3 << endl << endl;

	_bs.set(index1);
	_bs.set(index2);
	_bs.set(index3);
}

bf.set("abcd");
bf.set("aadd");
bf.set("cbad");

我们可以看到同一个字符串通过不同的哈希函数映射的位置是不同的,即便abcd,aadd,cbad他们的ASCII值是相同的

布隆过滤器判断是否存在

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

        注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判

bool test(const K& key)
{
	size_t index1 = Hash1()(key) % _N;
	if (_bs.test(index1) == false)
		return false;

	size_t index2 = Hash2()(key) % _N;
	if (_bs.test(index2) == false)
		return false;

	size_t index3 = Hash3()(key) % _N;
	if (_bs.test(index3) == false)
		return false;

	return true;
}

布隆过滤器删除

        布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素,因为通过多个哈希函数映射,可能会有同一个位置有多个映射,删除了一个对其他的会造成影响

        一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作

缺陷:

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

3.布隆过滤器的优缺点

优点:

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

缺点:

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

三.整体代码

1.bitset.h

#pragma once
#include<vector>
namespace wzyl
{
	class bitset
	{
	public:
		bitset(size_t N)
		{
			_bits.resize(N / 32 + 1, 0);
			_num = 0;
		}

		void set(size_t x)
		{
			size_t index = x / 32;//算出映射的位置在第几个整形
			size_t pos = x % 32;//算出x在整形的第几个位置
			_bits[index] |= (1 << pos);//第pos位置为1
			++_num;
		}

		void reset(size_t x)
		{
			size_t index = x / 32;
			size_t pos = x % 32;
			_bits[index] &= ~(1 << pos);//第pos位置为0
			--_num;
		}

		//判断x在不在(x的映射位是否为1)
		bool test(size_t x)
		{
			size_t index = x / 32;
			size_t pos = x % 32;
			return _bits[index] & (1 << pos);
		}
	private:
		std::vector<int> _bits;
		size_t _num;//映射存储的多少个数据
	};

	void test_bitset()
	{
		bitset bs(100);
		bs.set(99);
		bs.set(98);
		bs.set(97);
		bs.set(1);
		bs.reset(98);

		for (size_t i = 0; i < 100; ++i)
		{
			cout << "[" << i << "]" << ":" << bs.test(i) << endl;
		}
	}
}

2.BloomFilter

#pragma once
#include"bitset.h"
#include<string>
namespace wzyl
{
	struct Hashstr1
	{
		size_t operator()(const string& str)
		{
			//BKDRHash
			size_t hash = 0;
			for (size_t i = 0; i < str.size(); ++i)
			{
				hash *= 131;
				hash += str[i];
			}
			return hash;
		}
	};

	struct Hashstr2
	{
		size_t operator()(const string& str)
		{
			//RSHash
			size_t hash = 0;
			size_t magic = 63689;
			for (size_t i = 0; i < str.size(); ++i)
			{
				hash *= magic;
				hash += str[i];
				magic *= 378551;
			}
			return hash;
		}
	};

	struct Hashstr3
	{
		//SDBMHash
		size_t operator()(const string& str)
		{
			size_t hash = 0;
			for (size_t i = 0; i < str.size(); ++i)
			{
				hash *= 65599;
				hash += str[i];
			}
			return hash;
		}
	};

	template<class K = string, class Hash1 = Hashstr1, class Hash2 = Hashstr2, class Hash3 = Hashstr3>
	class bloomfilter
	{
	public:
		bloomfilter(size_t num) :_bs(5 * num), _N(5 * num) { }

		void set(const K& key)
		{
			size_t index1 = Hash1()(key) % _N;
			size_t index2 = Hash2()(key) % _N;
			size_t index3 = Hash3()(key) % _N;

			cout << index1 << endl;
			cout << index2 << endl;
			cout << index3 << endl << endl;

			_bs.set(index1);
			_bs.set(index2);
			_bs.set(index3);
		}

		bool test(const K& key)
		{
			size_t index1 = Hash1()(key) % _N;
			if (_bs.test(index1) == false)
				return false;

			size_t index2 = Hash2()(key) % _N;
			if (_bs.test(index2) == false)
				return false;

			size_t index3 = Hash3()(key) % _N;
			if (_bs.test(index3) == false)
				return false;

			return true;//这里也不一定真的在,还是存在误判
			//判断在是不准确的,可能存在误判
			//判断不在一定准确
		}
	private:
		bitset _bs;
		size_t _N;
	};

	void test_bloomfilter()
	{
		bloomfilter<string> bf(100);
		bf.set("abcd");
		bf.set("aadd");
		bf.set("cbad");
		cout << bf.test("abcd") << endl;
		cout << bf.test("aadd") << endl;
		cout << bf.test("cbad") << endl;
		cout << bf.test("cabd") << endl;
	}
}

3.Hash.cpp

#include<iostream>
using namespace std;
#include"bitset.h"
#include"BloomFilter.h"
int main()
{
	wzyl::test_bitset();
	wzyl::test_bloomfilter();
	return 0;
}

标签:hash,str,布隆,哈希,过滤器,位图,size
From: https://blog.csdn.net/w200514/article/details/143999919

相关文章

  • dotnet学习笔记-专题06-过滤器和中间件-01
    1.基本概念在ASP.NETCore中,中间件和过滤器都是处理HTTP请求的重要组件,但它们在应用中的位置、作用范围以及使用方式有所不同。1.1中间件和过滤器的区别1.1.1中间件位置与作用范围:中间件位于ASP.NETCore应用程序请求处理管道的核心位置,它可以处理进入应用程序的每一个HT......
  • 本地路由的ip过滤器完成了 不要用公有服务器挂载,只供大家程序研究观看
    ////////////////////////////我有windows开发者免责///////////////////////////////编译release’发布版本‘ #include<Windows.h>#include<stdlib.h>#pragmacomment(lib,"WS2_32.lib")#include<corecrt_wstdlib.h>#include<iostream>#include&......
  • Qt开发技巧(十九):定时器的调用问题,控件的透明问题,慎用事件过滤器,依赖库的路径链接,对话框
    继续讲一些Qt开发中的技巧操作:1.定时器的调用问题有一个场景经常遇到,那就是在符合某个条件下,延时一段时间去执行一段代码,如果短时间内触发多次又不需要频繁执行,只需要执行一次就行。如果选择用QTimer::singleShot无法终止已经触发的,这个时候就要主动实例化一个单次定时器,......
  • JavaWeb合集17-拦截器(Interceptor)和过滤器(Filter)
    十七、拦截器和过滤器在JavaWeb开发中,拦截器(Interceptor)和过滤器(Filter)都是用于在请求处理前后执行某些操作的机制。虽然它们的功能相似,但在实现方式、使用场景和灵活性方面有一些重要的区别。1、拦截器和过滤器的区别及选择1.1拦截器定义:拦截器是Spring框架提供......
  • Spring Cloud --- Gateway自定义全局过滤器
    Gateway自定义全局过滤器介绍全局过滤器对所有路由生效。新建类MyGlobalFilter并实现GlobalFilter,Ordered两个接口自定义接口调用耗时统计的全局过滤器@Component@Slf4jpublicclassMyGlobalFilterimplementsGlobalFilter,Ordered{publicstaticfinalS......
  • springmv的过滤器和拦截器的区别是什么
    一个请求进来的时候,先交给web服务器提供的过滤器,例如springboot项目,提供的服务器也就是内置的tomcat,过滤器处理完成以后,然后会来到servlet,有一个dispatcherServlet会调用拦截器,再由dispatcherServlet分发给对应的Controller,处理完请求后,会原路返回,再回到拦截器,再回到过滤器,最终响......
  • Spingboot 定时任务与过滤器,拦截器
     在boot环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用Spring自带的定时任务处理器@Scheduled注解,另一种就是使用第三方框架Quartz,SpringBoot源自Spring+SpringMVC,因此天然具备这两个Spring中的定时任务实现策略,当然也支持Quartz1.定时任务@Sched......
  • Spring Cloud Gateway关键点全局Token过滤器,局部过滤器接口耗时,全链路跟踪TraceId日志
    一.全局Token过滤器在SpringCloudGateway中,实现全局过滤器的目的是对所有进入系统的请求或响应进行统一处理,比如添加日志、鉴权等。下面是如何创建一个全局过滤器的基本步骤:步骤1:创建过滤器类首先,你需要创建一个实现了GlobalFilter接口,创建一个全局token过滤器。@Slf......
  • Gateway全局过滤器
    目录全局过滤器GlobalFilter案例定义全局过滤器,拦截并判断用户身份需求步骤自定义过滤器运行效果总结全局过滤器GlobalFilter全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是......
  • Gateway过滤器执行顺序以及跨域问题
    执行顺序请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执......