哈希扩展:位图与布隆过滤器

news2024/9/20 15:01:55

目录

  • 1. 位图
    • 1.1 位图引入
    • 1.2 位图概念
    • 1.3 位图的模拟实现
    • 1.4 位图相关问题
    • 1.5 位图的应用
  • 2. 布隆过滤器
    • 2.1 布隆过滤器概念
    • 2.2 模拟实现
    • 2.3 布隆过滤器相关问题
    • 2.3.1 哈希切分

1. 位图

1.1 位图引入

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

很经典的判断在不在的模型,那么可以使用set吗?答案是不可以的,40亿个整数大概需要16G的内存空间来进行存储,一般而言操作系统是不能也无法开辟这么大的空间,因此这种数据量大且仅需判断在不在的场景可以使用位图来解决。

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
在这里插入图片描述
一个整形能表示数的个数为2的32次方,因此就需要这么多个比特位来表示,换算成字节为2的29次方也就是500MB左右,500MB系统是可以开出来的。

若有负数的情况,需要做一次相对映射,让其加上一个数变成正数,其余所有数字都需要加上这个数,然后判断某个值是否存在则需要再减去这个数得到它的真实值即可

1.2 位图概念

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

1.3 位图的模拟实现

//位图的模板参数为非类型模板参数
//用来表示数的总范围
template<size_t N>
class bitset
{
public:
	bitset()
	{
		// 先开辟空间
		_a.resize(N / 32 + 1);
	}

	//三个主要成员函数:

	// 第x个比特位标记成1
	void set(size_t x)
	{
		//先找到是在第几个整数中
		size_t i = x / 32;
		//再找到是在这个整数的第几个比特位
		size_t j = x % 32;
		_a[i] |= (1 << j);
	}

	// 第x个比特位个标记成0
	void reset(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;
		_a[i] &= (~(1 << j));
	}
	// 检测第x个比特位是0还是1
	bool test(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;
		return _a[i] & (1 << j);
	}
private:
	// 由于要用到移位运算符,因此参数类型必须是整形家族的
	//若使用char则上面所有/或%32的位置都要改成8
	//因为char占8个比特位
	vector<int> _a;
};

1.4 位图相关问题

  1. 给定100亿个整数,设计算法找到只出现一次的整数?

一个比特位只能表示两种状态,即在不在,无法统计对应数字的出现次数,而两个比特位则可以表示出四种状态,00 01 10和11,因此可以使用两个比特位即两个位图来统计次数,规定00代表没有出现,01代表出现一次,10代表两次及以上。

代码实现:

template<size_t N>
class twoBt {
public:
	void set(size_t x) {
		//如果是00则置为01,表示出现一次
		if (!b1.test(x) && !b2.test(x)) {
			b2.set(x);
		}
		//如果是01则置为10,表示出现次数≥2次
		else if (!b1.test(x) && b2.test(x)) {
			b1.set(x);
			b2.reset(x);
		}
		//其它情况均表示大于2次无需出口i
	}

	//判断是否出现一次,即为01
	bool isOnce(size_t x) {
		return !b1.test(x) && b2.test();
	}

private:
	//使用两个位图
	bitset<N> b1;
	bitset<N> b2;
};
  1. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

一种方法是建立一个位图,然后将一个文件中的数据依次set到对应的位置,然后再依次检测另一个文件中的数据,看该数据对应要set的位置是否为1,若为1说明前面一个文件中出现了和当前相同的元素,就是交集,此时需要把该位置的值reset一下即置成0,因为可能会出现重复的值,但是交集没有重复,相当于去重。
另一种方法是建立两个位图,把两个文件中的数据依次set到对应位图中,然后遍历两个位图,若两个位图中的同一个位置都为1说明是交集,使用两个位图天然就完成了去重操作。

代码实现:

//使用第二种方法:
int main() {
	int a1[] = { 1,1,2,2,3,4,5,5,6 };
	int a2[] = { 3,5,7,9 };

	bit::bitset<10> b1;
	bit::bitset<10> b2;

	for (int x : a1) {
		b1.set(x);
	}
	for (int x : a2) {
		b2.set(x);
	}

	for (int i = 0; i < 10; ++i) {
		if (b1.test(i) && b2.test(i)) {
			cout << i << ' ';
		}
	}
	cout << endl;
	return 0;
}
  1. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。

和第一问非常类似,直接上代码:

template<size_t N>
class twoBt {
public:
	void set(size_t x) {
		//如果是00则置为01,表示出现一次
		if (!b1.test(x) && !b2.test(x)) {
			b2.set(x);
		}
		//如果是01则置为10,表示出现两次
		else if (!b1.test(x) && b2.test(x)) {
			b1.set(x);
			b2.reset(x);
		}
		//如果是10则置成11,表示出现三次及以上
		else {
			b1.set(x);
			b2.set(x);
		}
	}
	
	//判断出现0、1和2次
	//00、01和10
	bool isValid(size_t x) {
		return (!b1.test(x) && !b2.test(x)) || (!b1.test(x) && b2.test(x)) || (b1.test(x) && !b2.test(x));
	}
private:
	bitset<N> b1;
	bitset<N> b2;
};

1.5 位图的应用

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

2. 布隆过滤器

位图一般应用在判断整形数据是否存在的场景,得到的结果一定准确,若为字符串(或其它自定义)类型则得到的结果就不一定准确了,会存在误判的可能性,因为不同的字符串通过哈希函数得到的映射位置可能是一样的,这样便导致了冲突造成误判,如何有效缓解误判问题呢?

2.1 布隆过滤器概念

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

将哈希与位图结合,即布隆过滤器

需要注意的是,即使是布隆过滤器也无法完全杜绝误判的存在,只能降低误判率

2.2 模拟实现

#include <iostream>
#include <bitset>
using namespace std;
struct BKDRHash {
    size_t operator()(const string& str) {
        size_t hash = 0;
        for (auto ch : str)
            hash = hash * 131 + ch;
        return hash;
    }
};

struct APHash {
    size_t operator()(const string& str) {
        size_t hash = 0;
        for (size_t i = 0; i < str.size(); i++) {
            size_t ch = str[i];
            if ((i & 1) == 0)
                hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
            else
                hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
        }
        return hash;
    }
};

struct DJBHash {
    size_t operator()(const string& str) {
        size_t hash = 5381;
        for (auto ch : str)
        {
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};

namespace bit {
	template<size_t N,
			 class K = string,
			 class hashFunc1 = BKDRHash,
			 class hashFunc2 = APHash,
		     class hashFunc3 = DJBHash>
        class BloomFilter {
        public:
            void set(const K& key) {
                size_t hashi1 = hashFunc1()(key) % N;
                _bs.set(hashi1);

                size_t hashi2 = hashFunc2()(key) % N;
                _bs.set(hashi2);

                size_t hashi3 = hashFunc3()(key) % N;
                _bs.set(hashi3);
            }

            bool test(const K& key) {
                //计算出对应存储位置
                //只要有一个位置是0就是不存在
                size_t hashi1 = hashFunc1()(key) % N;
                if (!_bs.test(hashi1))
                    return false;

                size_t hashi2 = hashFunc2()(key) % N;
                if (!_bs.test(hashi2))
                    return false;

                size_t hashi3 = hashFunc3()(key) % N;
                if (!_bs.test(hashi3))
                    return false;

                //依旧是存在误判
                //不存在的值可能会返回true
                return true;           
            }

        private:
            bitset<N> _bs;
    };
}

一般而言,布隆过滤器是不支持删除的,因为简单的把某个位置置成0有可能会影响其它值的判定,比如x和y都映射到了1位置,若删除x,把1位置置成0则会影响到y的判定,y存在但是查找y会得到不存在的结果。

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

布隆过滤器的优势之一就是空间损耗小,若像上述支持删除功能的话就相当于把这个优势给抹掉了,需要酌情考虑

2.3 布隆过滤器相关问题

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

设两个大文件的名称分别为A文件和B文件。

近似算法:把A文件中的数据全部放进布隆过滤器,然后逐个检测B文件中的数据是否在过滤器中,如果不在那一定不是交集,如果在,很有可能就是交集,因为还存在小概率误判。

精确算法:可以将其分别平均分割成N份小文件(每个文件大小为几百兆即可),然后把A文件中数据依次放入每个小文件中,然后依次拿A的每个小文件中的数据与B的每个小文件中的数据依次比对,相同就是交集,但是这种比对的效率很低,如果要提高效率则需要用到哈希切分

2.3.1 哈希切分

同样是将其分割成N份小文件(无需平均分割),编号为0~N,把A和B文件中的query依次通过哈希函数计算出一个位置i,然后将该query数据放入编号为Ai(或者Bi)的小文件中,由于使用的是相同的哈希函数,因此A和B文件中相同的query数据必然分别会存放到对应相同编号的小文件中(但也可能包含冲突的query)。

这里与哈希桶非常相似,每个相同的数据必然进入同一个桶,但是桶中不一定都是相同的数据,还可能包含不同但冲突的数据

切分完毕后,找交集,只需要把Ai文件中的数据依次放入哈希表中,然后再用Bi中的数据依次检测是否存在,在就是交集再把其从表中删除(防止重复),这种做法极大的提高了比对效率,那还有什么问题没?

问题是每个小文件并不是平均切分的,因此可能某个小文件相同的或者冲突的query过多导致文件过大,该怎么办?

解决方案如下:先把该文件中的数据读出来放入一个set中,若set报错抛异常(bad_alloc),那么说明该文件数据的冲突太多,set放不下了,这种情况则需要换个哈希函数继续对其进行切分,重新对数据进行映射。
若能够全部放进set中,则说明该文件中有大量相同的数据,这种情况直接进行比对即可。

还有一个与其类似的问题:
给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?

同样使用哈希切分,切分成若干份小文件,把每个IP地址数据通过哈希函数计算出一个编号i,将其存进对应编号的小文件中,然后使用哈希表依次统计每个小文件中每个数据的出现次数,并且每统计一个文件都要更新出现次数最多的那个IP地址数据,全部文件统计完成后即可找到出现次数最多的那个IP地址。

而统计topK则需要再加个小堆结构来处理。

同样会出现某个小文件过大的问题,但是解决方法是一样的

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

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

相关文章

Java 基础学习(十二)文本I/O、日期与时间API

1 文本 I/O 1.1 字符流 1.1.1 什么是字符流 在Java中&#xff0c;字符流是指提供了基于字符的I/O能力的API。 Java 1.0中提供的基于字节的I/O流API只能支持8位字节流&#xff0c;无法妥善地处理16位Unicode字符。由于需要支持Unicode处理国际化字符&#xff0c;因此Java 1.…

网络安全——Iptables防DDoS攻击实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 一、实验目的要求&#xff1a; 1、掌握常见DDoS攻击SYN Flood的攻击…

Toyota Programming Contest 2023#8(AtCoder Beginner Contest 333)

A - Three Threes 题目大意&#xff1a;给你一个整数n&#xff0c;将这个数n输出n次。 呃呃 B - Pentagon 题目大意&#xff1a;给你一个正五边形ABCDE&#xff0c;给你任意两条边&#xff0c;判断是否相等 主要问题要判断一下内边&#xff1a;AD&#xff0c;AC&#xff0c;…

小 cookie,大作用:探索网站中的隐私追踪器(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

用Flask搭建简单的web模型部署服务

目录结构如下&#xff1a; 分类模型web部署 classification.py import os import cv2 import numpy as np import onnxruntime from flask import Flask, render_template, request, jsonifyapp Flask(__name__)onnx_session onnxruntime.InferenceSession("mobilen…

【图的应用一:最小生成树】- 用 C 语言实现普里姆算法

目录 一、最小生成树 二、普里姆算法的构造过程 三、普里姆算法的实现 一、最小生成树 假设要在 n 个城市之间建立通信联络网&#xff0c;则连通 n 个城市只需要 n - 1 条线路。这时&#xff0c;自然会考虑这样一个问题&#xff0c;如何在最节省经费的前提下建立这个通信…

云仓酒庄的品牌雷盛红酒分享红酒里加二氧化硫有害吗?

雷盛葡萄酒是广州万豪酒业有限公司旗下主力葡萄酒品牌&#xff0c;该品牌由云仓酒庄负责全国运营。雷盛&#xff08;LEESON&#xff09;品牌系列葡萄酒有幸邀请著名导演张纪中先生担任品牌代言人。采用多国家采购、多葡萄酒品种、多价位区间的全系列整体品牌形式&#xff0c;让…

谷达冠楠科技:抖音网店到底怎么做靠谱

随着互联网的发展&#xff0c;越来越多的人开始尝试在网上开设自己的店铺。而在众多的电商平台中&#xff0c;抖音网店无疑是近年来最受年轻人欢迎的一种方式。那么&#xff0c;抖音网店到底怎么做才能靠谱呢? 首先&#xff0c;我们需要明确一点&#xff0c;无论是在哪个平台上…

JS对象笔记

对象声明 对象也只是一种数据类型/字面值。写对象这个字面值有两种写法&#xff0c;一种是普通的对象&#xff0c;这种对象用new 构造函数&#xff08;&#xff09;&#xff0c;另一种是JS内特有的json对象。这个对象是直接{}就代表对象。且也是在堆内。 对象的构成 无论是上…

Pytorch当中的.detach()操作是什么意思

.detach() 是 PyTorch 中用于从计算图中分离张量的方法。当我们在PyTorch中进行张量运算时&#xff0c;操作会构建一个计算图来跟踪计算历史&#xff0c;这个计算图用于自动求导和反向传播来计算梯度。 使用.detach()方法可以将一个张量从当前的计算图中分离出来&#xff0c;使…

System作为系统进程陔如何关闭?

一、简介 system进程是不可以关闭的&#xff0c;它是用来运行一些系统命令的&#xff0c;比如reboot、shutdown等&#xff0c;以及用来运行一些后台程序&#xff0c;比如ntfs-3g、v4l2loopback等。system进程也被用于运行一些内核模块&#xff0c;比如nvidia、atd等。system进程…

太阳能电池特性测试用太阳光模拟器24H光源

概述 太阳能模拟器是一种在室内模拟太阳光的设备&#xff0c;能够较为准确地模拟太阳辐射的准直性、均匀性和光谱特性。它的基本原理是利用人工光源模拟太阳光辐射&#xff0c;以克服太阳光辐射受环境、时间和气候等因素影响&#xff0c;并且总辐照度不能调节等缺点&#xff0c…

c++ websocket 协议分析与实现

前言 网上有很多第三方库&#xff0c;nopoll,uwebsockets,libwebsockets,都喜欢回调或太复杂&#xff0c;个人只需要在后端用&#xff0c;所以手动写个&#xff1b; 1:环境 ubuntu18 g(支持c11即可) 第三方库:jsoncpp,openssl 2:安装 jsoncpp 读取json 配置文件 用 自动安装 网…

【Redis】远程访问配置教程与远程客户端连接测试

前言 Redis 是一种基于内存的高性能键值存储数据库&#xff0c;常用于缓存、会话管理和实时数据分析等场景。在默认情况下&#xff0c;Redis 不允许远程连接&#xff0c;为了进行远程连接&#xff0c;需要进行一些配置和操作。接下来将介绍如何修改配置文件以允许远程连接&…

毅速:3D打印随形水路 提高良品率和生产效率的新利器

随着科技的不断发展&#xff0c;3D打印技术已经成为模具制造领域的一种重要技术。其中&#xff0c;模具随形水路的设计和制造是提高注塑产品良品率和生产效率的关键环节。 模具随形水路是一种根据产品形状设计的水路&#xff0c;可以更靠近产品&#xff0c;并在模具内热点集中区…

SpringBoot 源码解析2:启动流程1

SpringBoot 源码解析2&#xff1a;启动流程1 1.启动方式2.SpringBootApplication3.SpringApplication3.1 构造器SpringApplication3.2 SpringApplication#run 3.3 SpringApplication#run 中关键方法3.1 SpringApplication#prepareEnvironment3.2 SpringApplication#prepareCont…

前端登录界面网站设计模板--HTML+CSS

🎀登录表单 💖效果展示 💖HTML代码展示 <!DOCTYPE html> <html lang="en" > <head></

【机器学习】044_Kaggle房价预测(机器学习模型实战)

参考自《动手学深度学习》中“Kaggle比赛实战&#xff1a;预测房价”一节 一、数据准备 首先从网站上下载要获取的房价数据。 DATA_HUB是一个字典&#xff0c;用来将数据集名称的字符串和数据集相关的二元组一一对应。 二元组包含两个值&#xff1a;数据集的URL和用来验证文…

网站转换APP源代码 WebAPP源代码 网站生成APP源代码 Flutter项目 带控制端

源码介绍 一款网站转换成APP的源代码,开发语言使用Flutter,开发工具使用的是AndroidStudio,你只需要在APP源代码里面填写你的域名,即可生成即可生成APP,包括安卓或者苹果,与此同时我们提供了APP的控制端.你可以通过控制端设置APP的颜色、添加APP的图标、添加APP的菜单栏目。 …

2020 ICPC·小米邀请赛 决赛 J. Rikka with Book(状压dp)

题目 登录—专业IT笔试面试备考平台_牛客网 n(n<20)本书&#xff0c;放在桌子上&#xff0c; 第i本书的可以看成是li(li<1e3)*1*1的物体&#xff0c;其中长为li&#xff0c;宽为1&#xff0c;高为1&#xff0c; 质量均匀分布&#xff0c;且为wi(wi<1e3) 求n本书摞…