面试大数据与空间限制问题总结
大数据与空间限制问题
statement:本篇文字是看书(最后有介绍)的总结,题目同样来自书上,仅限于想法,没有代码实现。
1. 布隆过滤器:100亿个黑名单网页,使用额外空间不超过30GB,允许万分之一失误率。
常见于建立黑名单时过滤使用。使用多个(就k吧)相互独立的优秀的hash函数,创建一个m个bit的数组,先将数据进行k次hash,对结果%m,那么每个数据就可以在bit数组中置多个1(可能是k个也可能少于k个),这样在验证的时候,就可以在进行k次hash计算,然后判断bit数组中的对应位是否被置1,如果全部为1,可以认为是命中,在名单中,进行相应的处理。
2. 使用2GB内存找到20亿个整数中出现最多的数
20亿个整数,假定32位=4B,也要8GB内存才能一次性处理(还只是读入内存),现在个人机内存也才差不多8GB,当然服务器肯定是可以处理的,采用哈希表,key=4B,value=4B(大概需要16GB内存,然后读入数字,查找统计数和词频,最后遍历找到最大的)。然,现在要求2GB内存找出出现最多的数,只能先利用hash函数进行分块,具体分块的数目由数据量和内存大小来定。比如目前2GB内存,按照极端情况来算,所有数据都不同,或者只有一个出现两次,需要8B*20亿=16GB=2GB*8,可以分为8块,或者16块。按照16快的分法,每个小文件大小会来到1GB,2GB的内存当然可以处理,每个小文件使用哈希表来统计词频,最后将16个文件中词频最大的16个数字再外排序一次,找到出现次数最多的那一个。
3. 40亿个非负整数中找到未出现的数,内存限制1GB
一拍脑袋,我们可以想到,使用哈希表来存储出现过的数(即,全部存入哈希表),然后再按照递增的顺序遍历42亿的数字,同时在哈希表中O(1)查找,找不到的就是未出现过的数。但是40亿个数(32位)一次性占用内存要40亿*4B=16GB,显然1G不够,内存溢出。那么另寻他法,我们知道INT_MAX(32位可表示的最大数字)是42亿多的一个数,我们可以使用bitmap来操作。用每一位来表示一个数存在与否,这样一来,使用内存可以到42亿*1bit/8 = 0.5GB = 500MB,大概500MB就可以解决。先创建INT_MAX个位的bitmap,然后遍历数据集,将bitmap[i]置1,最后再次遍历bitmap,将值为1的下标输出即为未出现过的数。
4. 100亿个URL中找到重复的URL(假设最长64B)
100亿*64B = 640GB,可怕的内存使用量,快快hash分治,分多少呢?如果可以多机来处理,那么就按照每台机器可提供的最大内存来分,比如每台可提供20GB的内存,那么就分成32块,交由32台机子分别使用哈希表去统计词频,最终将每台机子上词频为1的URL汇总起来(hash函数可以确保将相同的数字/串散列到同一桶中,“单向性”)。如果无法提供多台机器,那么就在一台机器上进行分块,假定分配4GB内存来做处理,那么就散列成为160块/个小文件,分别采用同样的哈希表的方法统计词频,最后汇总。
5. 40亿个非负整数中找到出现两次的数和所有数的中位数,内存限制1GB
出现两次?还记得我们在第3个问题的解答吗。只要出现两次的,那么意味着,我们可以用2bit来存储数字出现的次数,嗯~?出现超过3次怎么办呢?11最大为3呀!好好想想,需要统计更大的次数吗。没有必要,我们只要将出现两次的区别开来即可。说干就干,创建一个2*INT_MAX的bitmap,使用每两位存储一个数字的出现次数。那么就遍历数据集(每个数字为i),更新bitmsp[i*2]和[i*2+1]两位即可,每出现一次加1,即00-01-10-11,大于三次,就不做处理了。最后,我们以步长为2遍历bitmap,找出两位为10的数字。这样的内存使用是40亿*2bit/8 = 1GB。
那么如何找出中位数呢?
按照上面的做法,遍历bitmap,找出出现次数大于0的所有数的中位数即可。
要是内存限制更小比如10MB,怎么办?
这样的话,我们一次性可以处理的数就大打折扣了。10MB = 80Mb = 2*40*10^6,即一次能处理大概4000w个数,那么我们采用分治策略,每次处理一个区间,0-4000w,4000w-8000w,这样一来就需要每次都遍历一次数组,然后将该数据投射到他所属的区间中去。最后将每个区间的出现两次的数汇总。至于如何找到中位数,那么我们在处理每个区间的数字时,顺便统计该区间的数据个数,最终将所有区间统计完后,再/2找到是第几个数,从而能确定是在第一个区间中的第几个数,那么对该区间重新处理一次,找到该序号的数,即为所求。
6. 一致性哈希的基本原理
在增加和删减机器是,会导致机器间的负载不均衡,需要重新hash计算,在进行大规模的数据迁移,代价很大。采用虚拟节点的技术,用较多的虚拟节点去抢占哈希的桶(将负载分配到尽可能多的虚拟节点上),最终将虚拟节点再分配给具体的机器,从而达到负载均衡的目的,这样一来在增删机器的时候,只要将虚拟节点重新分配即可,无需重新hash,进行数据迁移。
book:《程序员代码面试指南》(第二版 左程云著)