1. foreword
做腾讯TSA比赛时,遇到海量数据的问题,工作站54G内存,把原始数据读进去各种操作后生成特征,再训练导致崩掉。后来只能把特征做好后存成文件,需要用的时候再读进去,省去了生成的中间环节文件。为了确保内存受得住,把特征按照天来存,最终总算是可以顺利训练和预测了。
内存总是不够的。对于海量数据处理的策略无非是:分解,大化小。除此没啥好办法,除非加内存。但总归需要在软件上下功夫,资源总是有限的。避免浪费。
《九章算法》提供有数十道关于大数据的题目。特此记录。
2. 题目解析
(1)给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
答:前两问其实是一问,top K 分别为1和k。该文件大小为100G,放入内存压力太大,分解为若干小文件,每个小文件则可以在内存中处理。这叫做hash分桶法。
- 将该文件按照 IP 的最后三位分为256个小文件。若均匀则每个小文件大小约为400M,可在内存中直接操作。用公式表述为:file_id = hash(ip) % 1000。
- 在每个小文件中统计各 IP 的出现次数。可以用IP为key,其出现次数为value统计。取出每个小文件中的top k 的记录。要求次数最多时k=1,前5则k=5。
- 合并这些小文件,取出前top k的记录。
注意:相同的IP会映射到同一个小文件/桶中。换句话说不同桶中的IP不重复。
第3问:
// sort 对文件升序排列;去掉重复行并在输出中加上计数;根据第一列计数值对文件按照数值大小逆序排列;取出前10行显示
sort log_file | uniq -c | sort -n -r -k 1 | head -10
(2)给定100亿个整数,设计算法找到只出现一次的整数。
答:100亿个整数的占用空间约为40GB。可参考(1)解法,hash分桶法。在每个文件中计算时,可使用bitmap方法,可用2个bit位表示一个数的情况。
(3)给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
答:设两个文件分别为A和B。分别将文件映射到1000个桶中。得到A_1,A_2…和B_1,B_2…。若两者有交集,则交集的数字必然存在于每对A_i,B_i。例如A中的512映射到A_5中,则B中若有512也会映射到B_5中。对每对小文件找到交集,两个表对应位相与。最后合并。
还可以直接bitmap:
整数是32bit的话,直接使用bitmap的方法就能实现了。所有整数共2^32种可能,每个数用2bit来表示,“00”表示两个文件均没出现,“10”表示文件1出现过,“01”表示文件2出现过,“11”表示两个文件均出现过,共需(2^32)*2/8=1GB内存,遍历两个文件中的所有整数,然后寻找bitmap中“11”对应的整数即是两个文件的交集,这样即可线性时间复杂度完成。
(4)1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数?
答:采用hash分桶+组内bitmap。直接bitmap内存不够。先分桶,然后每个组内采用扩展的bitmap,每个数字采用2个bit表示。0未出现过,1出现过1次,2(二进制为11)出现了2次或以上。然后将各组结果合并即可。
(5)给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法?
答:精确算法就是(3)中所说。近似算法?
允许有一定的错误率,可以使用Bloom filter(即布隆过滤器),1G内存大概可以表示80亿bit。将其中一个文件中的query使用Bloom filter映射为这80亿bit。然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
需要的为n*13(错误率为0.01时)。将其分为10个桶,每个大概为10亿个,需要130亿,1G内存大致可表示80亿多,基本满足。
(6)如何扩展BloomFilter使得它支持删除元素的操作?
答:布隆过滤器,对每个数映射时,采用k个hash依次处理置位。第一次的1生效,其余不生效。若要删除,则需置对应的hash位为1,如此一来牵扯到其他。
布隆过滤器存在假正例:即某个数不在,但布隆认为它在。原因是其他数可能影响了对应位。即说是在,算个正例,但其实是假的不在。
但不存在假反例:若某个数在,则布隆一定可算出它在。在必有痕迹。假反例是指,说是不在,算个反例,其实是在的真的。
基本的布隆过滤器不支持删除(Deletion)操作,但是 Counting filters 提供了一种可以不用重新构建布隆过滤器但却支持元素删除操作的方法。在Counting filters中原来的位数组中的每一位由 bit 扩展为 n-bit 计数器。
实际上,基本的布隆过滤器可以看作是只有一位的计数器的Counting filters。原来的插入操作也被扩展为把 n-bit 的位计数器加1,查找操作即检查位数组非零即可,而删除操作定义为把位数组的相应位减1。
但是该方法也有位的算术溢出问题,即某一位在多次删除操作后可能变成负值,所以位数组大小 m 需要充分大。另外一个问题是Counting filters不具备伸缩性,由于Counting filters不能扩展,所以需要保存的最大的元素个数需要提前知道。否则一旦插入的元素个数超过了位数组的容量,false positive的发生概率将会急剧增加。类似于引用计数。
(7)如何扩展BloomFilter使得它支持计数操作?
答案见(6)讨论的。
(8)给上千个文件,每个文件大小为1K—100M。给n个词,设计算法对每个词找到所有包含它的文件,你只有100K内存。
答:
(9)有一个词典,包含N个英文单词,现在任意给一个字符串,设计算法找出包含这个字符串的所有英文单词?
答:
参考资料
【1】july海量数据处理
【2】布隆过滤器
【3】布隆过滤器总结
来源:CSDN
作者:鸟恋旧林XD
链接:https://blog.csdn.net/niaolianjiulin/article/details/77883379