排序几乎排序的巨大文件

时间:2015-03-26 20:23:30

标签: c++ algorithm sorting optimization

我面临以下问题:

  • 我有一个巨大的文件(假设30 GB),它使用特定的API在内存中流式传输。

    • 此API仅允许我阅读前进(而非后退)。但是可以根据需要多次读取文件。

    • 该文件包含几乎所有已排序的数据,因为99%的数据已经排序,但是如果记录未处于正确位置并且应该在事先排序之前已经插入很多内容(如果所有内容都已排序)

我正在尝试创建此文件的副本,但需要对其进行排序。

有没有优雅的方法来做到这一点?

我能想到的唯一方法是最通用的方式:

  • 阅读文件
  • 创建几GB内存的批量,对它们进行排序,将它们写入HDD上的文件
  • 使用外部合并将所有这些临时文件合并到最终输出

然而,这并没有使用数据“几乎”排序的特殊性。有没有更好的方法来做到这一点?例如,不使用硬盘上的外部文件?

2 个答案:

答案 0 :(得分:1)

你可以这样做(Python中的例子)

last = None
special = []
for r in records:
    if last is None or r > last:
        last = r
    else:
        special.append(r)
        if len(special) > max_memory:
            break
if len(special) > max_memory:
    # too many out of sequence records, use a regular sort
    ...
else:
    sort(special)
    i = 0
    for r in records:
        while i < len(special) and special[i] < r:
            write(special[i])
            i += 1
        write(r)
    while i < len(special):
        write(special[i])
        i += 1

答案 1 :(得分:1)

使用自下而上合并排序的变体称为自然合并排序。这里的想法是查找有序数据的运行,然后在两个文件(所有顺序I / O)之间来回重复合并这些运行,直到只剩下一次运行。如果排序不必保持稳定(保持相等元素的顺序),那么只要一对顺序元素出现故障,就可以考虑运行边界。这消除了一些家务。如果排序需要稳定,那么您需要在查找运行的初始传递上跟踪运行边界,这可能是计数数组(每次运行的大小)。希望这个数组适合内存。每次合并传递后,数组中的计数数量减少一半,一旦只有一个计数,排序就完成了。

Wiki文章(尽管没有提供示例代码):natural bottom up merge sort

如果所有乱序元素都包含有些孤立的记录,您可以将乱序元素分成第三个文件,只将第一个文件中的顺序记录复制到第二个文件。然后使用您想要的任何方法对第三个文件进行排序(如果第三个文件很大,则自下而上合并排序可能仍然是最好的),然后合并第二个和第三个文件以创建排序文件。

如果您有多个硬盘驱动器,请将文件保存在不同的驱动器上。如果在SSD驱动器上执行此操作,则无关紧要。如果使用单个硬盘驱动器,一次读取或写入大量记录,例如每次读取或写入10MB到100MB,将大大减少排序过程中的搜索开销。