整数型数据如何判重我已经给出了答案,当位数组过于庞大的时候怎么利用位数组的映射范围分割文件我也给出了答案。现在考虑这么一个事:
有这么一天,全中国孩子的高考成绩都汇报给你了(海量数据),现在,我让你统计出来考试总分对应的人数。
例如 总分610的有那么1万个人,510的有那么20万个人,310的有那么10万个人。
说句实话,这样的统计很有意义,因为根据这样的统计,出题者能够看出来今年题目的平均水平如何,从而调整明年的出题策略,使得人数的分布呈现出正态。当然了,咱们国家出题的人考不考虑这个事是another story.
现在我们抽象抽象问题:数据很多,数据都是整数,需要对这些整数进行统计以获得每一个数据出现的频度。
问题抽象好后,你静心下来想了想,分数吗,无非就是0-750(你不要不相信有奇葩的人可以得0分),那么我申请一个751个元素的unsigned int型数组,每一个分数就是存储下标,相应下标自增1,一个O(n)的时间复杂度全给他搞定啦!!何等优雅! 怀揣着这样的想法,你高高兴兴的去教育部准备提交方案。
到了那里之后,摆在你面前的是一台功能无比强大,内存无比广阔的——————小霸王学习机。
他们说:751个4字节的数组,也就是3K的内存消耗太多了!!!!我们只有512字节的内存能供你使用。 瀑布般的汗水从你的头上滚落下来。你想起了貌似可以用位映射的方法来缩小内存的使用(缩小8倍),你哈哈大笑一声,说没关系,我用位映射来搞,只需要300个字节!!!!!!
四周掌声一片,你擦了擦头上的汗水,坐下来刚要写代码,突然瀑布般的汗水又开始汩汩涌出。这是怎么了呢?????
因为你想起来了!位映射能够判重!!!但是位映射怎么计数???一位只有两种状态,0,1,代表不存在/存在。现在你要计数了怎么办???
情急之下,你回忆起来好在看过Chris这个坑爹货写的博文,要将数据打散,将相同特征的文件归纳到一起,这样问题的规模就降低了。
现在的问题就是:怎么分割数据? 由于全国考生太多,显然你需要用4字节的无符号整数来表示得这个分数的数量。那512个字节能放多少个元素呢?128个。也就是说,你最多建立一个128个元素的unsigned int数组。 在这里,chris表示,128个够了!你不嫌分割出来的文件多的话,18个就够了。
现在:把分数在[0,127]这个区间的数据都扔一个文件里,[128,255]的扔另一个里,后面的就不需要我教你了,这整个分割是O(n)的时间。
取出0-127分的童鞋们,按照下标分别统计人数,然后数据归档。再取出下一组,分别统计人数,归档。依然是O(n)的时间。方法类似于前一篇的位映射分割。
每一组都显然有一个分数对应着下标零(0分,128分,256分,512分),然后下面的元素的存储位置就是它与本归档文件最低分的差额(偏移量)就是了。
总结一下:对由最小值X和最大值Y表示起来的数据。可以按照[X,X1],(X1,X2],(X2,X3]....(Xn,Y]将其分割成区间,将符合这个区间的数据放置到相应的分割文件中。然后分别在各个文件中进行频率的统计即可。
来个练习题:
在百度服务器的海量访问日志中找到访问服务器频率最高的IP地址,有兴趣的可以自己做做看。
最后自爆一个缺点吧:如果相应分割段内的数据特别少,那么很多空间就被浪费了。
所以推荐采取非均匀分割的办法,比如0-127的分数肯定会很少,于是你不如第一段分成0-255,但是这样就得处理一些地址冲突的问题(因为HASH表本身只有128的空间么,这是鸽洞原理)。
均等分割办法就是直观,适合我这种不会设计HASH函数的人,而如果你在HASH地址冲突处理上有很多的经验,不均匀分割能够很好的解决空间浪费的问题