查找文件中出现的每个整数值的绝对计数

Posted

技术标签:

【中文标题】查找文件中出现的每个整数值的绝对计数【英文标题】:find absolute counts for each integer value that occurs in a file 【发布时间】:2011-08-05 02:46:30 【问题描述】:

在最近的一次采访中被问到

有一个包含一百万个整数的日志文件。每个整数的长度为 32 位。日志文件中的特定整数值可能会重复。您可以按顺序读取日志文件。您还可以顺序读取和写入临时文件;可以随时打开的文件数量没有限制。但是,在任何给定时间,您在内存中最多可以保留 2000 个整数。

我被要求生成一个直方图,显示日志文件中出现的每个整数值的绝对计数,并说明每个整数必须加载到内存中的次数的顺序复杂度上限

【问题讨论】:

我希望这份工作做得很好,我会告诉他们坚持下去 【参考方案1】:

打开 232 个临时文件,每个整数一个。按顺序读取日志文件一次。每当读取整数 n 时,将“1”写入临时文件编号 n。然后通过所有临时文件生成直方图。每个整数只被读入一次内存,所以它是 O(n) 算法。

【讨论】:

是的,O(n) 有一个 非常大 常数! 是的,哈哈,我想看看可以打开所有文件的操作系统 :) 答案是个玩笑,我认为问题不是很好 :) 这是个玩笑,但它回答了所提出的问题,时间为 O(n)。这是我给出的“答案”,希望他们随后将“无限制”改写为某个合理的数字。【参考方案2】:

这是一个令人困惑的问题。您不能只读取 2,000 个数字,对它们进行排序,然后写入临时文件吗?这样做 500 次,然后进行 N 路合并。每个数字将被加载到内存中两次。

如果您必须在只有 2 GB RAM 的计算机上对 TB 大小的文件进行排序,您会做同样的事情。

【讨论】:

@gradbot:确实如此,尽管人们会期望操作系统会缓冲很多。 4K 缓冲区将保存其中一个临时文件的一半内容。如果您不能指望缓冲,那么进行简单的合并可能会更好:写入第一个缓冲区,读取下一个并将其与第一个合并以创建一个新的临时文件。读取下一个缓冲区并合并等。基本上,您正在执行 499 次 2 路合并,而不是单个 N 路合并。有许多不同的方法可以进行合并。 确实,对于现代硬盘缓存大小(多 MB),顺序访问时间与随机访问时间并没有多大关系。 如果您知道数据包含相对较高的重复次数,您可以使用合并排序存储键值对,合并相等的键(键将是整数值之一)并将它们的值相加(计数整数外观)。 @gradbot:如果您不关心每次顺序搜索数组的成本,这是一个值得的优化。如果没有读取缓冲,那么这绝对是一个值得优化的优化,因为每次点击都会节省一次搜索,而一次搜索将花费与多次搜索相同的时间。【参考方案3】:
    在内存中保留 2000 int = 大小 缓冲区

    对文件的读/写没有限制 = 每个数字计数将存储在一个文件中。

    32 位长度的数量 = 每个 文件是一个数字和文件名 是代表的 32 位 整数(可以使用整数值 也一样)

    显示直方图(表示没有顺序 需要)

伪代码:

count = 2000
HashMap<number, number> = new 

code:readbuffer
while count != 0 
read NextNumber
if HashMap.HasKey NextNumber then HashMap[NextNumber]++
else HashMap[NextNumber]=1
count --;
end while 

code:flushbuffer
foreach Key in HashMap 
 if exists FileName Key.ToBinnary 
  FileValue += HashMap[HashKey]
 else WriteNewFile FileName=Key.toBinnay; SetValue = 1
end foreach


code: histogram
each file name is the number;
each file value is the count. 

成本:读取缓冲区

读取的序列数 = 2M(M = 百万)

Map.HasKey = (在2000年的记录中搜索key,最坏的情况是数字不存在,SUMMARY(∑2000)x2M)

SetValue on Map 与上面的成本相同

总计:(2M)+(2Mx∑2000)x2

成本:flushbuffer File.exists 2M

成本:直方图 2M

总计 = 6M + 4Mx∑2000

【讨论】:

如果文件包含重复的 3,000 个值会怎样?也就是说,文件的内容是1,2,3,...3000,1,2,3,3000,etc.你最终会读取每个临时文件超过300次。 天哪,您要创建最多 2^32 个文件吗?我不认为这是一个成功的答案。 @Jim Mischel 是的,但没什么大不了的,因为您正在读取 2^32 个数字,缓冲区只有 2000 个值。这还不是最坏的情况。最糟糕的是每个数字出现一次。 @Nick Johnson,这就是这里每个人都提出的建议,实际上也是唯一可能的回应。只有另一种方法可以使它更快。在二叉树中组织文件名(如下所示)。这样可以更快地生成直方图。 这远非唯一可能的答案 - 请参阅 @Jim 的更明智的外部排序以获得更好的解决方案。【参考方案4】:

实现基于文件的 B-tree。

文件名是 GUID。

文件内容为: 数见 数数 左节点文件 右节点文件

经过一次后,复杂度可以从 B-tree 推导出来。

您的直方图隐含在结构中。

【讨论】:

以上是关于查找文件中出现的每个整数值的绝对计数的主要内容,如果未能解决你的问题,请参考以下文章

在无法容纳内存的大文件中查找出现的字符串

查找熊猫中每个唯一 ID 的先前计数出现的总和

使用一个表中的整数值在另一个 SQL 表中查找值

批处理文件一次检索一行,然后循环遍历文件目录以查找每行的匹配项

查找分组的最大计数

2.23-2.26 查找文件和文件名后缀