哈希的应用:海量数据处理

news2024/11/24 18:52:38

文章目录

  • 前言
  • 什么是海量数据处理
  • 位图的应用
    • 题目1
    • 题目2
    • 题目3
  • 布隆过滤器的应用
    • 问题1
    • 问题2
  • 哈希切割的应用
    • 题目1
    • 问题2

前言

如果只需要知道某些元素是否存在于集合中,当数据量达到一定程度时(以亿级起步),搜索树、哈希表等数据结构会因为其内存占用过大而降低效率,哈希思想将映射的位置缩小到极致:将元素的存在与否以1或0映射到若干个比特位上。位图、布隆过滤器都是空间利用率和查找效率很高的数据结构。

由于实际上海量数据都是储存在数据库中的,而本文要谈论的是它本质:哈希思想。也就是位图、布隆过滤器的应用,而这也是面试中常见的类型。

注:
海量数据处理的方式不止哈希一种,作者仍在学习中,只对哈希处理海量数据作出阐述。

什么是海量数据处理

处理数据无非就是对数据进行存储和增删查改操作,而数据量一旦很大,操作起来的时间就会变长。“海量”二字可见数据量之多,要知道,我们处理数据大多数情况都是在内存中进行的,而少得可怜的内存无法储存海量数据。可见,海量数据处理主要有两个方面:

  • 内存足够,时间太长:使用位图、布隆过滤器等数据结构;
  • 内存不够:使用哈希切割思想,将大问题转化为小问题,分而治之。

位图的应用

题目1

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

每个整数出现的次数可能是0,1,2,…100亿次,而题目只要求找到只出现1次的整数,所以可以将两个比特位作为一个整数是否存在的映射位置,用这两个比特位的二进制序列表示:

  • 00:出现0次;
  • 01:出现1次;
  • 10:出现2次及以上。

而STL中的bitset默认以1个比特位为元素的映射位置,有两种方法:

  1. 人为地控制遍历时每步的跨度为2个比特位;
  2. 用两个位图同时记录。

下面使用第二种方法,原因是好理解,操作比较简单:

  • 遍历元素,默认位置是00,第一次设置为01,第二次设置为10。
#include <iostream>
#include <vector>
#include <bitset>
#include <assert.h>
using namespace std;

int main()
{
    vector<int> v{1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 1, 3, 5, 7, 9};
    bitset<4294967295>* bs1 = new bitset<4294967295>;
    bitset<4294967295>* bs2 = new bitset<4294967295>;
    for (auto e : v)
    {
        if (!bs1->test(e) && !bs2->test(e))     // 00->01
        {
            bs2->set(e);
        }
        else if (!bs1->test(e) && bs2->test(e)) // 01->10
        {
            bs1->set(e);
            bs2->reset(e);
        }
        else if (bs1->test(e) && !bs2->test(e)) // 10->10
        {
            continue;
        }
        else
        {
            assert(false);
        }
    }
    for (size_t i = 0; i < 4294967295; i++)
    {
        if (!bs1->test(i) && bs2->test(i)) 		// 01
            cout << i << endl;
    }
    return 0;
}

输出

2
4
6
8
10

注意:

  1. 上面的代码只用vector里的几个样例测试,实际上是要从文件中获取数据的。
  2. 如果直接bitset<4294967295>实例化位图,也就是 2 32 2^{32} 232个比特位,合计512MB,两个bitset就是1GB,这样会撑爆栈区所以使用new,让编译器在堆区申请内存。

因为一个整型变量(不论是有符号还是无符号)在32位机器下是4个字节,以unsigned类型为例,它取值范围是[0,4294967295],即[0, 2 32 2^{32} 232]。

题目2

给两个文件,分别有 100 亿个整数,只有 1G 内存,如何找到两个文件的交集?

思路1:

  1. 创建1个位图;
  2. 将第一个文件中的整数映射到位图中;
  3. 遍历第二个文件中的所有整数,如果整数已经被映射到位图,那么它就是交集中的集合。

思路2:

  1. 创建2个大小相同的位图;
  2. 将第一个文件中的整数映射到位图1中;
  3. 将第二个文件中的整数映射到位图2中;
  4. 将位图 1 和位图 2 进行与操作,结果就是交集。

题目3

一个文件有 100 亿个整数,1G 内存,设计算法找到出现次数不超过 2 次的所有整数。

思路和题目1非常类似。无非就是将二进制序列增加到3:

  • 00:出现0次;
  • 01:出现1次;
  • 10:出现2次;
  • 11:出现3次及以上。
#include <iostream>
#include <vector>
#include <bitset>
using namespace std;
int main()
{
    vector<int> v{1, 3, 5, 7, 9, 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 2};
    bitset<4294967295>* bs1 = new bitset<4294967295>;
    bitset<4294967295>* bs2 = new bitset<4294967295>;
    for (auto e : v)
    {
        if (!bs1->test(e) && !bs2->test(e))     // 00->01
        {
            bs2->set(e);
        }
        else if (!bs1->test(e) && bs2->test(e)) // 01->10
        {
            bs1->set(e);
            bs2->reset(e);
        }
        else if (bs1->test(e) && !bs2->test(e)) // 10->11
        {
            bs2->set(e);
        }
        else                                              //
        {
            continue;
        }
    }
    for (size_t i = 0; i < 4294967295; i++)
    {
        if (!bs1->test(i) && bs2->test(i))      // 01 or 10
            cout << i << endl;
    }
    return 0;
}

输出

4
6
8
10

布隆过滤器的应用

布隆过滤器是位图的优化,它可以利用多个哈希函数字符串类型的元素处理。

问题1

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

近似算法要求没那么严格,允许误判的情况存在,所以可以考虑布隆过滤器。

  • 先读取其中一个文件当中的query,将其全部映射到一个布隆过滤器。
  • 然后读取另一个文件当中的query,依次判断每个 query 是否在布隆过滤器中,是则为交集,反之则否。

问题2

如何让布隆过滤器实现删除功能?

布隆过滤器一般不支持删除操作:

首先从原理上删除操作会直接影响哈希函数的结果,那么每一次删除都要重新把这个容器中的所有元素重新再映射一遍,影响了其他元素,降低效率。
其次布隆过滤器要删除一个元素,首先要保证它是真正存在这个集合中的,但是误判是无法避免的,所以删除有一定的风险。
要让布隆过滤器支持删除,必须要满足:

为每一个比特位增加一个引用计数,当在一个位置上增加一个元素,引用计数+1,反之-1,这样删除就不会改变布隆过滤器的长度,也就不会影响其他元素。但是这违背了布隆过滤器(位图)本身的应用场景:省空间+快速查询。

当使用Test接口得知元素可能存在于映射以后的布隆过滤器中,再进一步去原始文件验证这个元素是否存在于集合中。但这就像从内存中突然跳到磁盘文件中查找,文件IO和磁盘IO相对于内存而言是很慢的,所以还是降低了效率。

结合上面两点,再加上布隆过滤器本身的应用就是为了查询,而删除对它而言不痛不痒,因为位图这个容器是直接在内存中操作比特位的,即使有很多剩下的没用的元素,对计算机而言它也只是几个比特位,这是无关痛痒的。

哈希切割的应用

题目1

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

精确算法不允许误判,考虑使用哈希切割,其实就是分治思想:将大问题转化为小问题。

假设每个 query 为 20 字节,100 亿个query就是200GB,由于只有 1GB 内存,考虑将一个文件切分成 400 个小文件。
在这里插入图片描述

值得注意的是,必须使用同一个哈希函数对文件A和文件B的元素操作,才能实现“分而治之”的设想。原因是必须保证两个文件中每个小集合中每个元素的映射是正确的,才能保证两个文件的每个对应小集合是对应的。

  • 切割以后的小文件大小是512MB,所以可以将一个个小文件加载到内存,用set容器存放,再遍历另一个小文件中的每个元素,判断这个文件中的元素是否存在于set容器中。在则是交集元素,反之则否。

  • 哈希切割并不是平均切割,有可能切出来的小文件中有一些小文件的大小仍然大于 1GB,此时如果与之对应的另一个小文件可以加载到内存,则可以选择将另一个小文件中的元素加载到内存,因为目的是比较两文件的公有部分,所以只需要把其中一个小文件加载到内存。

  • 但如果两个小文件的大小都大于 1GB,可以将这两个小文件再进行一次切割。

哈希切割的本质是:将小文件当做哈希桶,将大文件中的query通过哈希函数映射到这些哈希桶中,如果是相同的query,则会产生哈希冲突进入到同一个小文件中。

问题2

给一个超过 100G 大小的 log file,log 中存着 IP 地址,设计算法找到出现次数最多的 IP 地址?如何找到 top K 的 IP?如何直接用 Linux 系统命令实现?

哈希切割:

  • 由于文件log file的大小超过100GB,考虑将文件log file切分成200个小文件。
  • 使用相同的哈希函数切分文件,使得每个小文件能够以合适的大小(不影响效率)加载到内存中。同样的
  • 遍历每个IP,然后将IP对应的哈希值写入它所在的小文件中。

查找出现次数最多的IP:

  • 要找到出现次数最多的IP,依次将每个小文件加载到内存中, 然后用map容器统计出每个小文件中各个 IP 地址出现的次数,然后比对各个小文件中出现次数最多的 IP 地址,最后能得到出现次数最多的IP。

查找出现次数最多的前K个IP:

  • TOP-K问题,用数据结构堆解决。用每个小文件中出现次数最多的IP建一个小堆,这个小堆的前K个IP就是出现次数最多的前K个IP。

在Linux中的操作:

sort file_name | uniq -c | sort -nrk1,1 | head -K命令选取出现次数 top K 的 IP 地址。

  • sort:对文件file_name排序;
  • uniq:统计每个 IP 地址出现的次数;
  • nrk1,1:再次使用sort命令按照每个 IP 底层出现的次数进行反向排序;
  • head:选出出现次数前K个元素。

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

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

相关文章

你评论,我赠书~【TFS-CLUB社区 第10期赠书活动】〖uni-app跨平台开发与应用从入门到实践〗等你来拿,参与评论,即可有机获得

文章目录❤️‍&#x1f525; 赠书活动 - 《uni-app跨平台开发与应用从入门到实践》❤️‍&#x1f525; 编辑推荐❤️‍&#x1f525; 抽奖方式与截止时间❤️‍&#x1f525; 赠书活动 → 获奖名单❤️‍&#x1f525; 赠书活动 - 《uni-app跨平台开发与应用从入门到实践》 内…

三子棋超详细解说,人机大战,PVP玩家对战

&#x1f992;这个游戏相信大家都听过&#xff0c;三子棋&#xff0c;不就是井字棋吗&#xff1f;那么今天我们就来亲手制作一个三子棋游戏&#xff0c;实现人机“智能”大战以及玩家PVP对战。 &#x1f412;三子棋的基本功能构思 &#x1f992;面对一个较大的游戏程序我们要做…

全套Python学习路线,快速上手!

最近几年随着互联网的发展学习Python人越来越多&#xff0c;Python的初学者总希望能够得到一份Python学习路线图&#xff0c;小编经过多方面汇总&#xff0c;总结出比较全套Python学习路线&#xff0c;快速上手。对于一个零基础的想学习python的朋友来说&#xff0c;学习方法很…

JavaWeb框架(二):Servlet组件入门

Servlet入门 MVC实战项目 仓储管理系统Servlet 入门DemoServlet 执行流程、生命周期执行流程生命周期Servlet API介绍Servlet体系结构Servlet urlPattern配置Servlet&#xff1a;请求与响应Request&#xff1a;请求请求的构成请求API方法来获取对应的值:请求参数的获取方式请求…

月入5000+|技术博客长期搬砖项目

大家好&#xff0c;我是钱der。 这篇文章介绍一个我之前研究过一段时间的小众技术人员的副业项目&#xff0c;做的好一天有几百收入&#xff0c;做的差一天也能有几十收入&#xff0c;这个项目只需要前期的积累&#xff0c;后期坐等收钱就可以。这个项目有一定的门槛&#xff…

动态规划DP

动态规划 DP3. 动态规划 DP什么是动态规划动态规划和其他算法的区别解题方法解题步骤[509. 斐波那契数](https://leetcode.cn/problems/fibonacci-number/) (easy)暴力递归递归 记忆体动态规划滚动数组优化动态规划 降维[62. 不同路径](https://leetcode.cn/problems/unique-…

[vue应用实践]:vue3使用自定义指令定义拖拽方法

总结一些日常需要用到的一些api&#xff0c;也是在一些面试中会经常出现的题目&#xff0c;今天分享的是vue3中使用自定义指令封装拖拽方法&#xff0c; 同时文章也被收录到我的《JS基础》专栏中&#xff0c;欢迎大家点击收藏加关注。 vue指令 vue中有内置的一些指令供我们使用…

信贷产品年终总结之贷中行为分析

番茄知识星球平台上周开始推出信贷业务年终总结的系列文章&#xff0c;首篇主题为客户特征画像&#xff0c;并已在平台发布&#xff08;12月13日&#xff09;&#xff0c;感兴趣童鞋可前翻查阅。作为系列专题的续集&#xff0c;本篇将围绕信贷存量数据为大家带来第二个主题“贷…

多维数据库概念与理解

如今多维数据库已经越来越普及&#xff0c;不少公司开始研发属于自己公司的基于多维开发的作业平台。利用多维的数据直观化、效率高等优势&#xff0c;直接打开数据分析的大门。 有人好奇什么是多维数据库&#xff1f;下面我和大家一一探讨 其实多维数据库是指将数据存放在一…

C语言数组

1、数组 数组使用之前必须声明&#xff1a; 类型定义符 数组名[常量表达式] 在声明数组时必须说明数组长度。 较好的方法是用宏来定义数组的长度。 #include <stdio.h> #define N 10 int main() { int arr[N];for(int i0;i<10;i){arr[i]i1;printf("%d &q…

科技云报道:疫情三年,数字会展成色几何?

科技云报道原创。 三年疫情&#xff0c;会展行业并未消极等待&#xff0c;线上线下融合趋势越来越明显&#xff0c;“数字展会”模式已成为常态化。 据《中国会展主办机构数字化调研报告&#xff08;2022&#xff09;》显示&#xff0c;超七成会展主办方采取数字化手段提升展…

OpenCV 之 图像平滑

1 图像平滑 图像平滑&#xff0c;可用来对图像进行去噪 (noise reduction) 或 模糊化处理 (blurring)&#xff0c;实际上图像平滑仍然属于图像空间滤波的一种 (低通滤波) 既然是滤波&#xff0c;则图像中任一点 (x, y)&#xff0c;经过平滑滤波后的输出 g(x, y) 如下&#xff…

从编程小白到年薪40万,为什么首选Python?

前言 在众多的计算及语言中&#xff0c;呼声很高、位列编程语言榜前面的无疑是生命力顽强的java、最近热度猛增的Python、被称为万物之源的C语言、争议很大的PHP等等。但是对于初学者来说&#xff0c;计算机语言就像天书&#xff0c;不知道到底该学习哪个&#xff0c;从哪一门…

Karl Guttag:Niantic户外AR参考设计或采用Lumus光波导

前不久&#xff0c;Niantic在高通骁龙峰会上公布了一款用于户外场景的AR眼镜参考设计&#xff0c;其特点是采用无线一体化设计&#xff0c;配备了柔性头带&#xff0c;可用来玩LBS AR游戏。目前关于该AR眼镜方案的信息不多&#xff0c;它的光学方案是大家非常关注的一点&#x…

游戏服务端 - AOI九宫格算法

游戏服务端 - AOI九宫格算法 下面简述内容&#xff0c;只针对平面上的简易场景。我们将平面上的场景分为一个个格子&#xff08;Grid&#xff09;&#xff0c;场景管理所有的Grid。如下&#xff08;假设场景的长宽均为20&#xff0c;每个格子宽高定义为1&#xff09;&#xff1…

电脑怎么查看是固态硬盘还是机械硬盘

前言 前两天有粉丝问我&#xff0c;买电脑的时候有的参数看不懂&#xff0c;比如固态硬盘和机械硬盘区分&#xff0c;他听商家说给他配置的电脑是512G固态硬盘&#xff0c;但是又不知道从哪里看到底是不是固态硬盘&#xff0c;怕以次充好。 今天我就跟大家详细介绍一下硬盘到…

五、path路径模块和url模块

上一篇内容讲到的fs文件系统模块是官方提供的内置模块&#xff0c;本篇path路径模块也是Node.js官方提供的内置模块&#xff0c;也是核心模块&#xff0c;用来处理路径&#xff0c;path模块用来满足用户对路径的处理需求。在上一篇内容就涉及到路径拼接的问题&#xff0c;来一个…

1 数据结构 绪论(时间空间复杂度)

文章链接是我的掘金博客&#xff0c;大家有兴趣可以去我的博客上看 博客地址&#xff1a;数据结构专栏 1 数据结构 绪论&#xff08;时间空间复杂度&#xff09;考纲要求 &#x1f495;1 术语&#xff08;逻辑结构&存储结构&#xff09;1.1 数据结构的形式定义&#xff08;…

【图像分割】遗传算法优化K聚类图像分割【含Matlab源码 1605期】

⛄一、遗传算法优化K聚类简介 文中提出基于优化遗传算法的模糊聚类图像分割算法, 是在上述对遗传算法进行了优化的基础上形成的。不仅根据个体适应度大小和变化快慢自适应调节变异率和交叉率, 提高计算准确性和效率, 另外, 在遗传算法迭代计算中加入基于曲线二阶导数的约束条件…

JavaWeb框架(三):JavaWeb项目实战 基于Servlet 实现系统登录注册功能

MVC实战项目 仓储管理系统需求&#xff1a;实现基本的登录和注册功能MVC实战项目&#xff1a;登录和注册登录功能实现注册功能实现总结Redis章节复习已经过去&#xff0c;新的章节JavaWeb开始了&#xff0c;这个章节中将会回顾JavaWeb实战项目 公司管理系统部分功能 代码会同步…