在比0(n log n)时间更好的时间内对大文件进行排序

时间:2013-06-10 12:54:54

标签: c++ sorting file-handling

我有两个文件:

一个文件存储10GB的名称“mapping.txt”:

1 "First string"
2 "Second string"
3 "Third string"
...
199000000 "199000000th string"

另一个文件以任意顺序存储来自mapping.txt的整数(存储在file.txt中):

88 76 23  1  5 7 9 10 78 12 99  12  15  16 77  89  90  51

现在我想按照上面的整数指定的顺序对“mapping.txt”进行排序,如:

88 "88th string"
76 "76th string"
23 "23rd string"
1  "1st string"
5  "5th string"
7  "7th string"

如何使用C ++完成此操作?

我知道对于文件中的每个整数,可以在“mapping.txt”中执行二进制搜索,但由于其时间复杂度为O(n log n),因此对于大文件来说效率不高。

我想要一种比0(n log n)更高效的方法。

6 个答案:

答案 0 :(得分:4)

这就是我要做的。这可能不是最有效的方式,但我想不出更好的方法。

首先,您传递一次大文件以构建每行开始的偏移量的索引。如果您的行足够长,该索引应该适合内存。

然后你传递小文件,读取每个索引,跳转到大文件中的相应位置,然后将该行复制到目标文件。

因为您的索引是连续的并且由整数索引,所以查找是常量时间。但是,无论如何,任何内存查找时间都会被磁盘搜索时间完全掩盖。

答案 1 :(得分:2)

正如塞巴斯蒂安建议的那样,试试

  1. 使用文件中每个字符串的偏移量(以及可选长度)在映射文件(“mapping.txt”)上创建索引。
  2. 然后访问订购文件(“file.txt”)中每个条目的索引,并查找文本文件中的存储位置。
  3. 这具有线性时间复杂度,具体取决于两个文件的大小和线性空间复杂度,因素很小,具体取决于“mapping.txt”的行数

    对于大型常规文件的快速且内存有效的顺序读取访问,请使用{API}中的mmap(2)madvise(2)或其相应的构造。如果文件大于您的地址空间,请将其尽可能大地映射到块中。不要忘记在第2步(随机与顺序)中对不同访问模式的内核进行恶意软件。

    如果您以后不需要它并且您的系统有内存映射,请不要将那么多内容从文件复制到堆上!

答案 2 :(得分:2)

  

我知道对于file.txt中的每个整数,都可以在“mapping.txt”中执行二进制搜索

正如你所说的,二进制搜索在这里没有用,除了你暴露给你的原因之外还有一个挑战,即mapping.txt不是友好的格式来执行搜索或索引。

如果可能,我建议将映射文件的格式更改为更适合直接搜索呼叫的格式。例如,您可以在包含固定长度字符串的文件中进行思考,这样您就可以计算每个条目的位置(这将在fseek调用的数量中保持不变,但请记住函数本身不会是常量)

[编辑]:

您可以采取以下措施来最小化对mapping.txt的访问:

  • 将“order”文件加载到内存中的数组中,但其位置是mapping.txt上的实际行,并且元素是新文件上的所需位置,例如该数组的第一个元素将是4因为1位于第4位(在你的例子中)。
  • 为方便起见,将新阵列拆分为N个桶文件,这样,如果一个元素将转到第200个位置,那么它将成为第4个桶的第一个位置(例如)。
  • 现在,您可以按顺序方式访问映射文件,您可以对阵列中的每一行检查新文件中的实际位置,然后将其放入相应的存储桶中。
  • 一旦你传递了整个映射文件(你只需要检查一次),你只需要将N个桶附加到你想要的文件中。

答案 3 :(得分:1)

鉴于您有一个确切的数据输出列表,我会尝试一个数组

答案 4 :(得分:1)

最好将此问题分解为更小的问题:

  • 将mapping.txt和file.txt分别分为nm条目块(nm可以是相同的大小或不同)
  • 采取正常的地图排序例程并修改它以获取一个块号(块是m - 您正在操作的file.txt的偏移量)并对来自这些索引的那些索引执行映射排序各种mapping.txt块。
  • 完成后,您将拥有m output-X.txt文件,您可以将这些文件合并到实际的输出文件中。

由于您的数据是ASCII,因此将固定窗口映射到任一文件会很麻烦,因此将两者分成较小的文件会很有帮助。

答案 5 :(得分:1)

这是mergesort的一个非常好的候选者。 这将是O(n log n),但大多数算法都不会超过它。 您只需使用索引文件来更改密钥比较。 你可以在任何体面的算法教科书中找到合并排序,它很适合对磁盘进行外部排序,因为要排序的文件大于内存。

如果你真的必须击败O(n log n),则传递一个文件并构建一个由密钥索引的哈希表,每个行的位置。然后读取索引文件,并使用哈希表来定位每一行。 从理论上讲,这将是O(n +大常数)。 不过我看到了一些问题:什么是n?这将是一个很大的哈希表。由于“大常量”非常大,实现可能比O(n log n)解决方案慢得多。即使您将文件映射为有效访问,也可能会进行大量分页。