大数据算法面试:1亿数据在有限内存上如何排序

我的机器学习教程「美团」算法工程师带你入门机器学习   已经开始更新了,欢迎大家订阅~

任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”学习小组“,沙雕博主在线答疑~此外,公众号内还有更多AI、算法、编程和大数据知识分享,以及免费的SSR节点和学习资料。其他平台(知乎/B站)也是同名「图灵的猫」,不要迷路哦~

 

 

 

 

 

相信大家或多或少都看过一些算法类的面试题,其中比较常出现的就有大数据排序问题。因为目前的内存仍无法处理TB级的数据,只能通过不同的算法优化以及I/O来进行尽可能快速的排序。

对于这类题目,我总结了以下几种排序方法,同时也提出了自己的一些疑问,希望大家可以一起讨论。这里只讨论nlogn级别的算法,其他的不列入讨论范围。

 

题型:亿级别数据(同型且有重复),统计其中出现次数最多的前N个数据

两种情况:可一次读入内存,不可一次读入

解法:

一、区间快速排序(当某个区间的长度=N则输出排序区间)

二、堆排序(维护N个结点的堆结构)

三、哈希映射(用hash将大文件映射为小文件,依次进内存排序后输出)

四、trie树

五、位图 (Bit Map)

 

 

  所谓的是否能一次读入内存,实际上应该指去除重复后的数据量。如果去重后数据可以放入内存,我们可以为数据建立字典,比如通过map,hashmap,trie,然后直接进行统计即可。当然在更新每条数据的出现次数的时候,我们可以利用一个堆来维护出现次数最多的前N个数据,当然这样导致维护次数增加,不如完全统计后在求前N大效率高。

  如果数据无法放入内存。一方面我们可以考虑上面的字典方法能否被改进以适应这种情形,可以做的改变就是将字典存放到硬盘上,而不是内存,这可以参考数据库的存储方法。而我主要讲的分治策略是对常见复杂问题的一种万能的解决方法,虽然很多情况下,分治策略的解法都不是最优解,但是其通用性很强。 此处只讲解一个方法,即最常用的堆+hash:

 

从1亿个整数里找出100个最大的数 
  1. 先对这批海量数据预处理,在O(N)的时间内用Hash表进行拆分映射
  2. 将需要排序的数据切分为多个样本数大致相等的区间,例如:1-100,101-300… 这里要考虑IO次数和硬件资源问题,例如可将小数据文件数设定为1G(要预留内存给执行时的程序使用) 
  3. 读取每个小文件的前100个数字,建立最大值堆。(这里采用堆排序将空间复杂度讲得很低,要排序1亿个数,但一次性只需读取100个数字,或者设置其他基数,不需要1次性读完所有数据,降低对内存要求) 
  4. 依次读取余下的数,与最大值堆作比较,维持最大值堆。可以每次读取的数量为一个磁盘页面,将每个页面的数据依次进堆比较,这样节省IO时间。 
  5. 将堆进行排序,即可得到100个有序最大值。
  6. 最后对各个数据区间内的排序结果文件进行处理,最终每个区间得到一个排序结果的文件,将各个区间的排序结果合并。 

       可以很明显的看出,这个方法时间复杂度为NlogK。 借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是100)大小的小根堆,然后遍历100万的Query,分别和根元素进行对比所以,我们最终的时间复杂度是:O(N) + N*O(logK)

  当然还有更好的方法,就是可以采用分布式计算,基本上就是map-reduce过程,首先可以根据数据值或者把数据hash(md5)后的值,将数据按照范围划分到不同的机子,最好可以让数据划分后可以一次读入内存,这样不同的机子负责处理各种的数值范围,实际上就是map。得到结果后,各个机子只需拿出各自的出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是reduce过程。这里拿上面的方法和trie树做一个比较:

大数据算法面试:1亿数据在有限内存上如何排序_第1张图片

  实际上可能想直接将数据均分到不同的机子上进行处理,这样是无法得到正确的解的。因为一个数据可能被均分到不同的机子上,而另一个则可能完全聚集到一个机子上,同时还可能存在具有相同数目的数据。比如我们要找出现次数最多的前100个,我们将1000万的数据分布到10台机器上,找到每台出现次数最多的前100个,归并之后这样不能保证找到真正的第100个,因为比如出现次数最多的第100个可能有1万个,但是它被分到了10台机子,这样在每台上只有1千个,假设这些机子排名在1000个之前的那些都是单独分布在一台机子上的,比如有1001个,这样本来具有1万个的这个就会被淘汰,即使我们让每台机子选出出现次数最多的1000个再归并,仍然会出错,因为可能存在大量个数为1001个的发生聚集。因此不能将数据随便均分到不同机子上,而是要根据hash后的值将它们映射到不同的机子上处理,让不同的机器处理一个数值范围。

  而先映射后排序的方法会消耗大量的IO,效率不会很高。而上面的分布式方法,也可以用于单机版本,也就是将总的数据根据值的范围,划分成多个不同的子文件,然后逐个处理。处理完毕之后再对这些单词的及其出现频率进行一个归并。实际上就可以利用一个外排序的归并过程。

  另外还可以考虑近似计算,也就是我们可以通过结合自然语言属性,只将那些真正实际中出现最多的那些词作为一个字典,使得这个规模可以放入内存。

   

你可能感兴趣的:(算法之离散数学,算法之数据结构,***Data,Science***)