【问题标题】:Sorting large files in better time than 0(n log n) time以比 0(n log n) 更好的时间对大文件进行排序
【发布时间】:2013-06-10 12:54:53
【问题描述】:

我有两个文件:

一个文件存储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) 更高效的方法。

【问题讨论】:

  • 如果您有答案,请照此发布。关于这个问题是否应该保持开放的元讨论是here

标签: c++ sorting file-handling


【解决方案1】:

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

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

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

因为您的索引是连续的并且按整数索引,所以查找是恒定的时间。无论如何,任何内存中的查找时间都将完全被磁盘寻道时间所掩盖。

【讨论】:

    【解决方案2】:

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

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

    如果可能的话,我建议将映射文件的格式更改为更适合直接查找调用的格式。例如,您可以考虑在一个包含固定长度字符串的文件中,这样您就可以计算每个条目的位置(这在 fseek 调用的数量中是恒定的,但请记住,函数本身不会是恒定的)

    [编辑]:

    您可以采取的其他措施来尽量减少对 mapping.txt 的访问:

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

    【讨论】:

    • @RoseBeck 如果您无法更改映射文件格式,请遵循 Sebastian 的建议,由于该映射文件的格式,我认为您没有太多选择。
    • 我真的不明白。你能详细解释一下第二点吗?
    • @RoseBeck 好的...想象一下,您解决了问题。我们将做相反的事情,而不是访问一次“订单”文件和映射几个。因此,我们将有一个关于映射的元素,该元素将位于新文件中的某个位置。由于您的字符串不是固定长度的,因此将它们放在新的大文件中的新位置将是低效的。为避免这种情况,您将把那个巨大的文件分成几个小文件。例如,第 134 位的字符串将存储在 100-200.txt 文件的第 34 位。
    【解决方案3】:

    按照塞巴斯蒂安的建议,试试

    1. 在映射文件(“mapping.txt”)上创建索引,其中包含文件中每个字符串的偏移量(和可选长度)。
    2. 然后访问排序文件(“file.txt”)中每个条目的索引并查找文本文件中存储的位置。

    这具有取决于两个文件的大小的线性时间复杂度和取决于“mapping.txt”的行数的小因子线性空间复杂度

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

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

    【讨论】:

      【解决方案4】:

      如果你有一个你想要的数据输出的列表,我会尝试一个数组

      【讨论】:

      • 但是因为我有一个 10GB 的大文件。所以我不能将它存储在内存中..所以你能解释一下你的答案..关于它如何适合内存
      • 用于 file.txt。还有一个行号。
      • 你能用代码解释一下吗...我很困惑..因为“我拥有的字符串是任意长度的”
      • 文件1:每一行由一个数字和一个字符串组成。文件 2:要输出的文件 1 中的行列表。因此,您读取文件 2,然后读取文件 1 并输出字符串,其数字与文件 2 中的数组中的条目之一匹配
      【解决方案5】:

      最好把这个问题分解成更小的问题:

      • 将 mapping.txt 和 file.txt 分别拆分为 nm 入口块(nm 可以相同或不同)
      • 使用您的常规地图排序例程并对其进行修改以获取一个块编号(该块是您正在操作的 file.txt 的m-offset)并对来自各种索引的这些索引执行地图排序mapping.txt 块。
      • 完成后,您将拥有m output-X.txt 文件,您可以将其合并到您的实际输出文件中。

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

        • 我在想同样的事情,但有一个挑战,因为必须完成一个映射,并且不能保证两个文件的子集都会包含该匹配项。跨度>
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-19
        • 2017-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-25
        • 2015-07-06
        相关资源
        最近更新 更多