海量数据场景下的热门算法题(算法村第十五关白银挑战)

从40亿中产生一个不存在的整数

给定一个输入文件,包含40亿个非负整数,请设计一个算法,产生一个不存在该文件中的整数,假设你有1GB的内存来完成这项任务。

进阶:如果只有10MB的内存可用,该怎么办?

不用写代码,将思路方法说清楚。

1GB内存

假设用哈希表来保存出现过的数,如果40亿个数都不同,则哈希表的记录数为40亿条,存一个32位整数需要 4B 空间,所以最差情况下需要40亿 * 4B = 160亿字节,大约需要16GB的空间,不符合要求。(10亿字节 = 0.93GB ≈ 1GB,100万字节 = 0.95MB ≈ 1MB)

采用位存储是常用的思路。位存储最大的好处是占用的空间是简单存整数的1/32, 16G / 32 = 0.5G = 500MB ,满足要求。

具体地说,因为500MB = 4,194,304,000 bit < 2 ^ 32 bit = 4,294,967,295 bit,而创建bit数组的大小一般是2的幂次方(且在10MB内存限制的场景下,一般来说,我们以2的整数倍来划分数据块)。所以我们申请一个长度为 4,294,967,295 的 bit 类型的数组 bitArr ( boolean 类型),bitArr 上的每个位置只有0或1状态。遍历这40亿个无符号数,遍历到某个数就在 bitArr 相应位置的值设置为1。例如,遇到1000,就把 bitArr[1000] 设置为1。

遍历完数据后,再遍历 bitArr ,哪个位置上的值没被设置为1,这个位置所表示的数就不在40亿个数中。例如,bitArr[8001] == 0 ,那么8001就是没出现过的数。遍历完 bitArr 之后,所有没出现的数就都找出来了。

位存储的核心是:不存储这40亿个数据本身,而是存储数据在位图中的对应位置。

10MB内存

使用分块思想,用时间换空间,通过两次遍历搞定。

40亿个数需要500MB的空间,如果只有10MB的空间,则至少需要将数据分为50个块。而一般来说,我们以2的整数倍来划分数据块。

10MB = 83,886,080 bit < 2^27 = 134,217,728 bit,而恰好 2^26 bit = 67,108,864 bit = 8MB ,所以每个数据块的大小最大为8MB,即分成 4,294,967,295 bit / 67,108,864 bit = 63.99 ≈ 64 块 ,即每个数据块包含67,108,864个数字。在这里最少要分成64块,分成128块、256块也是可以的(每个数据块所含的数字更少,即存储数据占用的内存更少,当然可以)。

首先,将0~4,294,967,295 这个范围平均分成64个区间(64块),每个区间包含67,108,864个数(67,108,864 bit),例如:

  • 第0区间:0 ~ 67108863
  • 第1区间:67108864 ~ 134217728
  • 第 i 区间:67108864 * i ~ 67108864 *(i+1) - 1
  • 第63区间:4227858432~4294967295

因为一共只有40亿个数,所以,如果统计落在每一个区间上的数分别有多少,则至少有一个区间上的计数少于67108864。利用这一特点,我们可以找出其中一个乃至多个没出现过的数。

具体则是通过两次遍历40亿个数实现:

第一次遍历:

  1. 申请长度为64的整型数组countArr, countArr[i]用统计在这40亿个数中,属于第 i 区间的有多少个。使用的内存是countArr的大小(64 * 4B),非常小,可忽略不计。
  2. 遍历40亿个数,根据当前的数来决定哪一个区间上的计数增加。例如,如果当前数是3422552090, 3422552090 / 67108864 = 51,所以第51区间上的计数增加1,即countArr[51]++

遍历完40亿个数之后,遍历countArr,必然存在一个i,使countArrl[i]小于67108864,表示第i区间上至少有一个数没出现过。

假设第37区间上的计数小于67108864,那么我们对这40亿个数据进行第二次遍历

  1. 申请长度为 67108864 的bit型数组bits,占用8MB的空间
  2. 遍历这40亿个数,即当前数为num,当num满足num / 67108864 == 37,才进行第3步,否则继续遍历。
  3. bits[num - 6710886437 * 37]的值设置为1。
  4. 遍历完40亿个数之后,bits中必然存在没被设置成1的位置,假设bits中第i个位置没被设置成1,那么6710886437 * 37 + i就是一个没出现过的数。

总结一下进阶的解法:

  1. 根据10MB的内存限制,确定统计区间的大小,即第二次遍历时bits的大小。
  2. 利用区间计数的方式,找到计数不足的区间countArrl[i],这个区间上肯定存在没出现的数。
  3. 第二次遍历40亿个数,在计数不足的区间上进行位存储,再遍历bits(这次遍历的耗时可忽略),找出一个没出现的数。

用 2GB 内存在 20 亿个整数中找到出现次数最多的数

有一个包含 20 亿个全是 32 位整数的大文件,你需要在其中找到出现次数最多的数,内存限制为 2GB。

在很多整数中找到出现次数最多的数,通常的做法是使用哈希表对出现的每一个数做词频统计,哈希表的 key 是某一个整数,value 是这个数的出现次数。

就本题来说,一共有 20 亿个数,哪怕只是一个数出现了 20 亿次,用 32 位的整数也可以表示其出现的次数而不会产生溢出(2^32 - 1 > 20亿)。哈希表的 key 占用 4B,value 占用是 4B。那么哈希表的一条记录(key,value)需要占用 8B,当哈希表记录数为 2 亿个时,需要至少 1.6GB 的内存。

如果 20 亿个数中不同的数超过 2 亿种,最极端的情况是 20 亿个数都不同,那么在哈希表中可能需要产生 20 亿条记录,这样内存会不够用,所以一次性用哈希表统计 20 亿个数的办法是有很大风险的。

解决办法是把包含 20 亿个数的大文件用哈希函数分成 16 个小文件。根据哈希函数的性质,同一种数不可能被散列到不同的小文件上,同时每个小文件中不同的数一定不会大于 2 亿种,(假设哈希函数足够优秀)。然后对每一个小文件用哈希表来统计其中每种数出现的次数,这样我们就得到了 16 个小文件中各自出现次数最多的数,还有各自的次数统计。接下来只要选出这16 个小文件各自的第一名,再比较出出现次数最多的即可。

把一个大的集合通过哈希函数分配到多台机器中,或者分配到多个文件里,这种技巧是处理大数据面试题时最常用的技巧之一。但是到底分配到多少台机器、分配到多少个文件,可能是在与面试官沟通的过程中由面试官指定,也可能是根据具体的限制来确定。比如本题确定分成 16 个文件,就是根据内存限制 2GB 的条件来确定的。

从100亿个URL中查找的问题

题目:有一个包含100亿个URL的大文件,假设每个URL占用64B,请找出其中所有重复的URL。

使用解决大数据问题的一种常规方法:把大文件通过哈希函数分配到机器,或者通过哈希函数把大文件拆成小文件,一直进行这种划分,直到划分的结果满足资源限制的要求。

首先,你要向面试官询问在资源上的限制有哪些,包括内存、计算时间等要求。在明确了限制要求之后,可以将每条URL通过哈希函数分配到若干台机器或者拆分成若干个小文件,这里的“若干”由具体的资源限制来计算出精确的数量。

例如,将100亿字节的大文件通过哈希函数分配到100台机器上,然后每一台机器分别统计分给自己的URL中是否有重复的URL(哈希函数的性质决定了同一条URL不可能分给不同的机器);或者在单机上将大文件通过哈希函数拆成1000个小文件,对每一个小文件再利用哈希表遍历,找出重复的URL;还可以在分给机器或拆完文件之后进行排序,排序过后再看是否有重复的URL出现。

总之,牢记一点,很多大数据问题都离不开分流,要么是用哈希函数把大文件的内容分配给不同的机器,要么是用哈希函数把大文件拆成小文件,然后处理每一个小数量的集合。

补充问题:某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天热门Top100词汇的可行办法。

一句话:分流,哈希表统计词频,小根堆找各自的 top100,在汇合比较出最终的 top100。

补充问题最开始还是用哈希分流的思路来处理,把包含百亿数据量的词汇文件分流到不同的机器上,具体多少台机器由面试官规定或者由更多的限制来决定。对每一台机器来说,如果分到的数据量依然很大,比如,内存不够或存在其他问题,可以再用哈希函数把每台机器的分流文件拆成更小的文件处理。处理每一个小文件的时候,通过哈希表统计每种词及其词频,哈希表记录建立完成后,再遍历哈希表,遍历哈希表的过程中使用大小为100的小根堆来选出每一个小文件的Top100(整体未排序的Top100)。每一个小文件都有自己词频的小根堆(整体未排序的Top100),将小根堆里的词按照词频排序,就得到了每个小文件的排序后Top100。然后把各个小文件排序后的Top100进行外排序或者继续利用小根堆,就可以选出每台机器上的Top100。不同机器之间的Top100再进行外排序或者继续利用小根堆,最终求出整个百亿数据量中的Top100。对于TopK的问题,除用哈希函数分流和用哈希表做词频统计之外,还经常用堆结构和外排序的手段进行处理。

40亿个非负整数中找到出现两次的数

32位无符号整数的范围是0~4294967295,现在有40亿个无符号整数,可以使用最多1GB的内存,找出所有出现了两次的数。

本题可以看做是第一题的进阶,这里将出现次数限制在两次。

首先,可以用 bit map(位图,即位存储)的方式来表示数出现的情况。具体地说,是申请一个长度为4294967295x2bit类型的数组bits,用2个位置表示一个数出现的词频,1B占用8个bit,所以长度为4294967295 x 2bits占用1GB空间。

然后,遍历这40亿个无符号数,初次遇到num,就把bits[num*2+1]bits[num*2]设置为01,第二次遇到num,就把bits[num*2+1]bits[num*2]设置为10,第三次遇到num,就把bits[num*2+1]bits[num*2]设置为11。以后再遇到num就不再做任何设置。

40亿个无符号数遍历完成后,再遍历bits,如果bits[i*2+1]bitArr[i*2]10,那么i就是出现了两次的数。

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

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

相关文章

Gradle学习笔记:Gradle的使用方法

文章目录 1.初始化项目2.构建脚本语言选择3.项目命名4.项目构建过程 1.初始化项目 创建一个test空文件夹&#xff0c;在该文件夹下打开终端&#xff0c;并执行命令&#xff1a;gradle init. 会有一个选项让你选择项目的类型。下面是每个选项的含义和用途&#xff1a; basic&am…

Hylicos - MINI2440 - 中断控制

中断 中断源管理 中断是一种异步异常&#xff0c;CPU需要处理很多来自设备的中断请求&#xff0c;而CPU引出的line只有IRQ线和FIQ线&#xff0c;所以就得引入中断控制器帮助CPU搞清楚是中断的来源。 MINI2440的中断控制器&#xff0c;可以接受来自60个中断源的请求。提供这些…

mysql更新charset

因为要从mysql5.x升级到mysql8&#xff0c;原来数据库表里面的一些utf-8字段只支持utf8mb3&#xff0c;更新到utf8mb4以支持更多的unicode字符. 解决办法 改变数据库 ALTER DATABASEdatabase_nameCHARACTER SET utf8mb4COLLATE utf8mb4_general_ci;改变表 ALTER TABLEtab…

k8s从入门到实践

k8s从入门到实践 介绍 Kubernetes&#xff08;简称k8s&#xff09;和Docker Swarm是两个流行的容器编排工具&#xff0c;它们都可以帮助用户管理和部署分布式应用&#xff0c;尤其是基于容器的应用。以下是两者的主要特点和对比&#xff1a; Kubernetes (k8s)&#xff1a; 开…

机器学习笔记 - 基于自定义数据集 + 3D CNN进行视频分类

一、简述 这里主要介绍了基于自定义动作识别数据集训练用于视频分类的 3D 卷积神经网络 (CNN) 。3D CNN 使用三维滤波器来执行卷积。内核能够在三个方向上滑动,而在 2D CNN 中它可以在二维上滑动。 这里的模型主要基于D. Tran 等人2017年的论文“动作识别的时空卷积研究”。 …

【算法】糖果(差分约束)

题目 幼儿园里有 N 个小朋友&#xff0c;老师现在想要给这些小朋友们分配糖果&#xff0c;要求每个小朋友都要分到糖果。 但是小朋友们也有嫉妒心&#xff0c;总是会提出一些要求&#xff0c;比如小明不希望小红分到的糖果比他的多&#xff0c;于是在分配糖果的时候&#xff…

RocketMQ源码阅读-九-自定义过滤规则Flitersrv

RocketMQ源码阅读-九-自定义过滤规则Flitersrv 什么是FiltersrvFiltersrv注册到Broker过滤类Consumer发起订阅设置过滤类代码Consumer上传过滤类代码Flitersrv编译过滤类代码 过滤消息Consumer 从 Filtersrv 拉取消息Flitersrv从Broker拉取消息 Flitersrv的高可用总结 什么是Fi…

vue上传解析excel表格并修改字段名

目录 1.安装 xlsx 2.引入 3.使用 1.安装 xlsx npm install xlsx 2.引入 import * as XLSX from xlsx; 3.使用 <template><div class"UploadCptOutbox"><div class"Tooloutbox"><el-uploadclass"upload"ref"u…

数据库(二)

DDL语句对数据库的操作&#xff1a; 创建&#xff1a;create database 数据库名 &#xff1b; 修改&#xff1a;alter database 数据库名 charset 字符集 &#xff1b; 删除: drop database 数据库名&#xff1b; 查询&#xff1a;show databases; DDL语句对表的操作 …

【C++】内存分区模型

目录 1.程序运行前 2.程序运行后 3. new操作符 3.1 基本语法 3.2 开辟数组 C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的 全局区&#xff1a; 存放全局变量和静态变量以及常量栈区 …

时序分解 | MATLAB实现CEEMDAN+SE自适应经验模态分解+样本熵计算

时序分解 | MATLAB实现CEEMDANSE自适应经验模态分解样本熵计算 目录 时序分解 | MATLAB实现CEEMDANSE自适应经验模态分解样本熵计算效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现CEEMDANSE自适应经验模态分解样本熵计算 包括频谱图 附赠案例数据 可直接运行 …

喜报|「云原生数据库PolarDB」、「阿里云瑶池一站式数据管理平台」揽获“2023技术卓越奖”

日前&#xff0c;国内知名IT垂直媒体&技术社区IT168公布2023年“技术卓越奖”评选结果&#xff0c;经由行业CIO/CTO大咖、技术专家及IT媒体三方的联合严格评审&#xff0c;阿里云瑶池数据库揽获两项大奖&#xff1a;云原生数据库PolarDB荣获“2023年度技术卓越奖”&#xf…

YOLOv5全网独家首发:Powerful-IoU更好、更快的收敛IoU,效果秒杀CIoU、GIoU等 | 2024年最新IoU

💡💡💡本文独家改进:Powerful-IoU更好、更快的收敛IoU,是一种结合了目标尺寸自适应惩罚因子和基于锚框质量的梯度调节函数的损失函数 💡💡💡MS COCO和PASCAL VOC数据集实现涨点 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/category_1251193…

《SPSS统计学基础与实证研究应用精解》视频讲解:数据加权处理

《SPSS统计学基础与实证研究应用精解》4.7 视频讲解 视频为《SPSS统计学基础与实证研究应用精解》张甜 杨维忠著 清华大学出版社 一书的随书赠送视频讲解4.7节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。本书旨在手把手教会使…

Redis 面试题 | 11.精选Redis高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

CNN卷积理解

1 卷积的步骤 1 过滤器&#xff08;卷积核&#xff09;&#xff08;Filter或Kernel&#xff09;&#xff1a; 卷积层使用一组可学习的过滤器来扫描输入数据&#xff08;通常是图像&#xff09;。每个过滤器都是一个小的窗口&#xff0c;包含一些权重&#xff0c;这些权重通过训…

小红书如何做混部?

作者&#xff1a;宋泽辉&#xff08;小红书&#xff09;、张佐玮&#xff08;阿里云&#xff09; 编者按&#xff1a; Koordinator 是一个开源项目&#xff0c;是基于阿里巴巴内部多年容器调度、混部实践经验孵化诞生&#xff0c;是行业首个生产可用、面向大规模场景的开源混…

加速震荡交易策略

实盘开户找我哦~ # 加速震荡指标&#xff08;Accelerator Oscillator&#xff09; import talib import datetime import numpy as np def init(context): context.nday 5 #短期值 context.mday 20 #长期值 # 定义股票池 context.stocks [600519.SH,0003…

在 Linux 上搭建 Java 环境

目录 一、安装jdk 1. 挑选 jdk 版本 2. 安装 3. 验证 jdk 二、安装tomcat 1. 下载压缩包 2. 上传压缩包给 Linux &#xff08;需要用到 rz 命令&#xff09; 3. 解压压缩包&#xff08;需要用到 unzip&#xff09; 4. 进入 bin 目录 5. 给启动脚本增加可执行权限 6. 启…

sqlmap 是一个自动化的 sql 注入渗透工具

一.介绍 sqlmap 是一个自动化的 sql 注入渗透工具&#xff0c;指纹检测、注入方式、注入成功后的取数据等等都是自动化的&#xff0c;sqlmap 还提供了很多脚本.但在实践测试的时候基本用不上.用于检测和利用 Web 应用程序中的 SQL 注入漏洞。它自动化了识别和利用 SQL 注入漏洞…