布隆过滤器与倒排索引

本文介绍了布隆过滤器和倒排索引两种数据结构。布隆过滤器是一种概率型数据结构,用于高效地插入和查询,节省内存空间,但存在误判可能。倒排索引则是一种由关键词指向记录的位置的索引结构,常用于快速定位信息。文章讨论了布隆过滤器的优缺点、删除操作及其在实际问题中的应用,并简述了倒排索引的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

布隆过滤器

布隆过滤器提出:

针对推送去重问题提出。
当服务器记录用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?
这里提供了三种方法:

  1. 用哈希表存储用户记录,缺点:浪费空间
  2. 用位图存储用户记录,缺点:不能处理哈希冲突
  3. 将哈希与位图结合,即布隆过滤器
布隆过滤器概念:

是一种紧凑型的、比较巧妙的概率型数据结构。
特点:
高效的插入和查询,因为存在一定的概率型,所以只能告诉用户“某样东西一定不存在或者可能存在”。
实质:
利用多个哈希函数,讲一个数据映射到位图结构中。
优点:
提升查询效率,节省大量的内存空间。

在这里插入图片描述

布隆过滤器的插入、查找和缺陷:

将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位为1.
查找方法:
分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。

插入baidu和tencent
baidu置1位为:1 4 7
tencent置1位为:3 4 8

若我们新插入alibaba
alibaba置1位为:1 3 8
此时插入的数据与之前已有的数据发生了重叠。

若此时要寻找baidu,只需找到1 4 7位置1的即可。
若此时要寻找toutiao,toutiao的置1位为:4 7 8
因为4 7 8此时均被置1,所以布隆过滤器会显示toutiao在这个布隆过滤器内。实际上并不存在。
这就是布隆过滤器的缺陷。

注意:
因为有些哈希函数存在一定的误判,所以会出现意外情况:即布隆过滤器如果说某个元素不存在时,该元素一定不存在。
如果说某个元素存在时,该元素可能存在。
在这里插入图片描述

布隆过滤器的删除:

以上图为例:
若要求删除tencent元素,如果直接将tencent的3 4 8位直接置为0,此时baidu元素与tencent元素在比特位上有重叠,所以baidu元素也将被删除。会造成误删。

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

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

布隆过滤器的优点:
  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  2. 哈希函数相互之间没有关系,方便硬件并行运算
  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
布隆过滤器的缺陷:
  1. 有误判率,即存在假阳性,即不能准确判断元素是否在集合中(补救方法:再建立一个白
    名单,存储可能会误判的数据)
  2. 不能获取元素本身
  3. 一般情况下不能从布隆过滤器中删除元素
  4. 如果采用计数方式删除,可能会存在计数回绕问题
#pragma	once

namespace bite
{
	template<size_t N, class T,
		               class HF1,
		               class HF2, 
		               class HF3, 
	                   class HF4, 
		               class HF5>

class BloomFilter
{
public:
	BloomFilter()
		:_size(0)
	{}
	void Insert(const T& data)
	{
		size_t index1 = HF1()(data) % N;
		size_t index2 = HF2()(data) % N;
		size_t index3 = HF3()(data) % N;
		size_t index4 = HF4()(data) % N;
		size_t index5 = HF5()(data) % N;

		_bs.set(index1);
		_bs.set(index2);
		_bs.set(index3);
		_bs.set(index4);
		_bs.set(index5);

		++_size;
	}

	bool IsIn(const T& data)
	{
		size_t index = HF1()(data);
		if (_bs.text(index))
			return false;

		size_t index = HF2()(data);
		if (_bs.text(index))
			return false;

		size_t index = HF3()(data);
		if (_bs.text(index))
			return false;

		size_t index = HF4()(data);
		if (_bs.text(index))
			return false;

		size_t index = HF5()(data);
		if (_bs.text(index))
			return false;
	}
private:
	bitset<N> _bs;
};
}
题:

1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法。
100亿*4字节=40G

精确算法:哈希切分
对两个文件进行哈希切分,使用相同的散列函数(下附五个散列函数)将所有query转换为整数hash。再利用index(文件下标)=hash%1000可将相同的query分到同一个文件。
将两个文件中index相同的小文件进行对比,找出其交集。
将文件的交集汇总起来即为整个文件的交集。
算法复杂度为O(N)。

近似算法:布隆过滤器
首先使用相同的散列函数(下附五个散列函数)将所有query转换为整数hash。
将第一个文件中的整数映射到位图中,再拿第二个文件中的数字与第一个文件映射的位图去对比。存在相同数字即为交集。
算法复杂度为O(N)。

2. 如何扩展Bloom Filter使得它支持删除元素的操作
对每一个位进行引用计数。

散列函数:

template<class T>
size_t BKDRHash(const T* str)
{
	register size_t hash = 0;
	while (size_t ch = (size_t)*str++)
	{
		hash = magic * 131+ ch;
	}
	return hash;
}

template<class T>
size_t SDBMHash(const  char* str)
	{
		register size_t hash = 0;
		while (size_t ch = (size_t)*str++)
		{
			hash = 65599 * hash + ch;
		}
		return hash;
	}

template<class T>
size_t RSHash(const T* str)
{
	register size_t hash = 0;
	size_t magic = 63689;
	while (size_t ch = (size_t)*str++)
	{
		hash = magic * hash + ch;
		magic *= 378551;
	}
	return hash;
}

template<class T>
size_t APHash(const T* str)
{
	register size_t hash = 0;
	size_t ch;
	for (long i = 0; ch = (size_t)*str++; i++)
	{
		if ((i & 1) == 0)
		{
			hash ^= ((hash << 7) ^ ch ^ (hash > >3));
		}
		else
		{
			hash ^=(~ ((hash << 11) ^ ch ^ (hash >> 5)));
		}
	}
	return hash;
}

template<class T>
size_t JSHash(const T* str)
{
	if (!*str)
		return 0;
	register size_t hash = 1315423911;
	while (size_t ch = (size_t)*str++)
	{
		hash ^= ((hash << 5) ^ ch ^ (hash >> 2));
	}
	return hash;
}

template<class T>
size_t DEKHash(const T* str)
{
	if (!*str)
		return 0;
	register size_t hash = 1315423911;
	while (size_t ch = (size_t)*str++)
	{
		hash ^= ((hash << 5) ^ ch ^ (hash >> 27))^ch;
	}
	return hash;
}

template<class T>
size_t FNVHash(const T* str)
{
	if (!*str)
		return 0;
	register size_t hash = 1315423911;
	while (size_t ch = (size_t)*str++)
	{
		hash *= 16777619;
		hash ^= ch
	}
	return hash;
}

倒排索引

概念:

倒排索引不是由记录来确定属性值的,而是由属性值来确定记录的位置。
组成:
单词词典和倒排文件。
倒排索引一般表示为一个关键词,然后是它的频度(出现的次数),位置(出现在哪一篇文章或网页中,及有关的日期,作者等信息),它相当于为互联网上几千亿页网页做了一个索引,好比一本书的目录、标签一般。读者想看哪一个主题相关的章节,直接根据目录即可找到相关的页面。不必再从书的第一页到最后一页,一页一页的查找。

简单来说就是[文章——关键词]颠倒为[关键词——文章]。

在此不具体分析,详细内容参考

题:

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

答:对上千个文件生成1000个布隆过滤器,并将1000个布隆过滤器存入一个文件中,将内存分为两份,一份用来读取布隆过滤器中的词,一份用来读取文件, 直到每个布隆过滤器读完为止。

用一个文件info 准备用来保存n个词和包含其的文件信息。

首先把n个词分成x份。对每一份用生成一个布隆过滤器(因为对n个词只生成一个布隆过滤器,内存可能不够用)。把生成的所有布隆过滤器存入外存 的一个文件Filter中。

将内存分为两块缓冲区,一块用于每次读入一个 布隆过滤器,一个用于读文件(读文件这个缓冲区使用 相当于有界生产者消费者问题模型来实现同步),大文 件可以分为更小的文件,但需要存储大文件的标示信 息(如这个小文件是哪个大文件的)。

对读入的每一个单词用内存中的布隆过滤器来判 断是否包含这个值,如果不包含,从Filter文件中读 取下一个布隆过滤器到内存,直到包含或遍历完所有 布隆过滤器。如果包含,更新info 文件。直到处理完 所有数据。删除Filter文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值