挑战程序员同学,如何只用2GB内存从20/40/80亿个整数中找到出现次数最多的数?


沙茶敏碎碎念


64mb内存就够。假设你的数据都存在文件中。

1,分治法,空间换时间,分片读取hash到n个文件中

2,统计每个文件中出现次数最多的数字

3,堆排序,对比每个文件中出现次数最多的数字。

4,结束


wuyukun91


需求:

使用2G的内存,找出80亿个数字中出现最多的数字。

假设

  1. 整数为4字节(2^32=4G),即最大40多亿。
  2. 所有的数字有80亿个。
  3. 所有数字在硬盘中,本身不会占用内存。
  4. 所用内存为2G多一些,例如有限的变量。但多出的内存和2G相比可以忽略不计。

设计:

  1. 80亿的计数可以用4字节保存(2^32=4G)。因为如果计数超过一半,则表明该数字一定是出现最多的。
  2. 2G的内存约可以保存5亿多数字的计数(2G/4=512M)。

也就是说,将2G的内存分成单位为4字节的数组,可以一次获得0∼5亿多之间出现最多的数字。

步骤:

  1. 顺序扫描80亿个数字,忽略0∼512M之外的数字,每个数字N的出现个数累加存放在第N个数组元素中。最后将最出现最多的数字及其次数保存起来,出现并列第一时,只保存第一个数字。如果过程中某数字出现个数超过40亿,则直接结束。
  2. 再次扫描所有数字,此次忽略512M∼1G之外的数字。每个数字N的出现个数累加存放在第N-512M个数组元素中。本轮所获得的数字的出现个数和第一轮结果比较,保存较大的那个。

  3. 由于整数取值范围为4G,所以最多扫描8次后即可获得最终结果。

问题:

如果整数长度为8字节,则需要扫描约300多亿次(2^64/512M=2^40)。所以此算法并不适用于8字节的整数。

讨论:

当数字足够多,且数字取值范围足够大时,以有限内存获取出现次数最多的数字几乎是不可能的。因为数字的取值范围极大,且数字极多,任何哈希或其它分片的算法都有可能出现极端情况,导致分片数据过多而无法一次性导入内存计算。除非我们预先知道部分数字规律,否则考虑到效率,应该只会要求得到近似结果。


tutu_cloud


只有不是程序员才会出这样的题,你要知道,3、8、55246546是整数,但12345的阶乘,葛立恒数等也是整数,葛立恒数的葛立恒数次方也是整数,你没有限定整数范围,所以我觉得真正的程序员会先和你谈需求。另,我就是程序员。


伴你去看海850


low一点的不考虑时间成本,直接按2G分批MapReduce。


Deathef


小问题。

有限集。

坑不多,一个是复杂度,一个是准确度。

将整个集划分为000-XXX的子集,每个子集内统计000-YYY的数,形成列表,合并各表,

然后前ZZZ个数再全集统计一遍。

搞定。


巫师威武


一、用4字节表示的整数个数为2^32≈40亿,而用2字节表示的无符号整数个数为2^16≈6万。

二、2G=2^31B≈20亿字节。

三、要找出出现次数最多的数,则应记录每个数出现的次数,最快的方法是在内存中将每个数出现的次数记录下来,记录的方法则是内存地址对应数,相应地址的内存单元记录次数,但2G内存以字节为单位仅能记录20亿个数,且每个数出现的次数大于255将会出现溢出风险。因此,这一方案不可取。

四、这样只能将每个次出现的次数记录在磁盘上。这样在磁盘上建一个16G的文件,每4字节对应一个整数,可对应40亿个整数,并用于记录相应整数的出现的次数。

1、将文件初始化。

2、依次读取数据,并用无符号整数记录在磁盘文件中,如出现溢出,则该数为次数最多的数。

3、从文件中读取各数出现的次数,用一个变量A记录最高次数,再用一个变量B记录最高次数出现的数据个数,要用个文件依次记录最高次数出现的数。当最高次数增加时,A+1,B置1,文件中写入该数,同次数的数出现时,B+1,文件相应位置写入该数,直到全部读完。

这样根本不需2G内存。


新热机发明者曾祥云


2g内存不是重点,80亿数字和取值范围才是重要的:

1. 80亿的数字至少需要加载一遍,才知道有哪些数据

2. 如果是取mapsize = 2ˇ16 或者 80亿开方,一个map

大mapsize的空间不到1m

3. 顺序读80亿数据,除以mapsize取余,同一余数放追加同一文件,余数作文件名

4. 顺序读取步骤3产生的所有文件,读取的每个文件时新建mapsize大小的hashmap,统计每个数的次数,再取该hashmap中出现最多次数的整数放到新的map中

5. 依次读完步骤3 产生的文件,就能得到每个文件最多次数的整数map

所有步骤需要80亿数据的两次读盘,一次写盘,mapsize次取最大值,80亿数据取余数


结翼唯乃


用类似Spark的AppendOnlyMap(本质是个用数组和开放定址法做成的Map)的思想做略小于2G内存的聚合,快满的时候把数组元素前挪然后排序再append到单独一个文件并清空map。遍历完20亿行数据的时候,也就生成了n个部分聚合的有序kv文件(key是数值,value是key出现的次数)。

然后每个文件生成一个Iterator,记录当前行的值。再搞个长度为n的优先队列,每个队列元素是文件Iterator,排序比较的是每个文件Iterator的当前值。这样每从优先队列中取出一个文件Iterator,对这个Iterator取下一个kv,再把这个Iterator塞回到优先队列。相同的key必相邻,把连续相同的key的value相加即可得到这个key的出现次数,遍历一次优先队列就能知道哪个key出现次数最多


924060929


以最高位或最低位为依据分组,写入各个文件

再从条数最多的文件开始,去掉分组标志位,再以最高位或最低位分组,重复以上,得到一组相同数字个数,把所有低于这个条数的文件丢弃,重复过程,可以几十k内存搞定


分享到:


相關文章: