外部排序剖析-以磁带为例

本文总结自《数据结构与算法分析(C++语言描述)》第四版第7章外部排序的内容。
错误和不足之处,敬请指出,谢谢!

一、基础部分

1. 为什么要区分外部排序与内部排序

所谓外部,是指磁盘、磁带、光盘等外部存储介质;那么内部相应指的是内存。
显然,他们之间的重要区别就是速度和容量,外存慢容量超大,内存超快容量小;
因此,当需要排序的数据量超过内存所能容纳的大小时,普通的排序算法比如快速排序,便无用武之地。本质的原因就是内存可以常数时间访问一个元素,但是外存比如硬盘,会有转动磁盘和移动磁头等不容忽视的时间开销。

2. 外部排序的维基解释

外排序(External sorting)是指能够处理极大量数据的排序算法。通常来说,外排序处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。外排序通常采用的是一种“排序-归并”的策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。尔后在归并阶段将这些临时文件组合为一个大的有序文件,也即排序结果。

3. 简单排序-归并策略

3.1 概念先知

1) 顺串:一组排过序的记录,称之为顺串,顺串长度在每趟外部排序过程中是不固定的;谨记此定义,否则阅读多相合并的内容会比较困难
2) 排序:普通的内部排序算法,比如快速排序;
3) 归并:指的是将两个已经排序的序列合并成一个序列的操作,即归并操作;

3.2 以磁带的 2 路合合并外排为例

3.2.1 假设
内存大小受限,因此假设它的大小只能容纳 M=3 个记录;
有4盘磁带,分别为 Ta1, Ta2, Tb1, Tb2;
待排数据预先存储在 Ta1;
3.2.2 外部排序过程详解

(1)数据原始状态
这里写图片描述
(2)每次从 Ta1 读取 M 个记录到内存中,排序完成,分别按顺串输出到 Tbn;得到如下结果,总共 5 个顺串, Tb1 三个, Tb2 两个;
这里写图片描述
(3)依次从 Tb1 和 Tb2 读取一个顺串进行合并,并输出到 Tan,得到如下结果,总共三个顺串,Ta1 两个,Ta2 一个;
这里写图片描述
(4)重复(3)的做法,即第二趟合并,得到如下结果,两个顺串,Tb1 和 Tb2 各一个;
这里写图片描述
(5)继续第三趟合并,得到最终结果,如下,
这里写图片描述


二、高阶部分

1. 多路合并与多相合并

多路合并的作用在于减少外部排序所需的趟数,从 ⌈log(N/M)⌉ 降至 ⌈log k(N/M)⌉ (即以 k 为底,N/M 为真数),k>=3,N 指总记录数;多相合并则解决多路合并中对磁盘需求量大的问题,从 2k 数量的磁带降至 k+1。

2. 多路合并

多路合并与 2 路合并的方式基本相似,相信大家注意到了,两者唯一的区别是在合并上,2 路合并只需比较两个数从而可以得到最小值,但是多路合并有 k 个数,即需从 k 个数取出最小值,因此采取的合理方式是用最小堆来实现,这样每次通过对最小堆执行 deleteMin 操作,来逐一获取最小值。

2.1 3路合并过程详解

(1)数据初始状态
这里写图片描述
(2)依次从 Ta1 中读取 M 个记录,排序后按顺串依次输出到 Tbn,得到如下结果,Tb1 两个顺串,Tb2两个顺串,Tb3 一个顺串;
这里写图片描述
(3)第一趟合并结果,此时 3 个输出磁带未用完,只有两个顺串,Ta1 一个顺串, Ta2 一个顺串, 如下;
这里写图片描述
(4)第二趟合并,即最终外部排序结果,如下;
这里写图片描述

3. 多相合并

现在讨论如何将多路排序的 2k 盘磁带转换为 k+1 盘;这里以 2 路合并为例,通过 3 盘磁带实现。本节关心每趟合并中顺串的数量,而不关心具体的记录

3.1 假设

3 盘磁带分别为,T1,T2,T3,
输入数据在 T1 上,它将输出共 34 个顺串, 
以两种方式对 T2 和 T3 分配顺串数量:(1)17 和 17;(2) 21 和 13;

3.2 分析

节省磁盘需要的牺牲就是增加合并总的趟数,多相合并中总趟数会因初始顺串数量分配方式不同而产生较大差异,这里初始顺串分配方式指将原始记录在内存排序完成后输出到空磁带上的过程。下面是图解,注意,每一趟顺串的记录数量不一样,
(1)17 和 17 的方式,总共 12 趟,拆开的原因在于,把结果合并到一个磁带上了,此时无法做合并操作,只能先拆分再合并。
这里写图片描述
(2)21 和 13 的方式,总共 8 趟,
这里写图片描述

3.3 总结

大家有兴趣可以自己在纸上画出初始分配方式为 22 和 12 的过程图,从上面图解过程基本可以知道,初始的顺串数量分配方式对最终外部排序需要的趟数有较大的影响。
但是有一种最优的分配方式,那就是斐波拉契数列,2 路合并就分配为 Fn-1 和 Fn-2, 如果无法分成两个斐波拉契数,则通过补充虚顺串(长度为0的顺串)来实现。拓展到 k 路合并,则需要 k 阶斐波那契数列来分配顺串。
k 阶斐波那契数列: 这里写图片描述
其初始条件:这里写图片描述

三、讨论

1) 从上面的总结分析可以看出,外部排序比内部排序对设备的依赖性更强。比如性能,以及数据读取方式(e.g. 硬盘和磁带)等。
2) 高阶部分还有替换选择

猜你喜欢

转载自blog.csdn.net/chuanglan/article/details/79028613