我正在处理一个问题,即我需要对80GB
数据进行排序。我只有1GB
主内存来排序数据。显然我们将在这里应用外部排序方法。但我的问题是哪种k-merge排序会更有效率?
K-merge排序的复杂性为O(nk^2)
,其中n是元素的数量。假设我使用这种方法来计算复杂性:
8路合并,然后是10路合并
8 way merge - O(n*8^2) => O(64n)
10 way merge - O(8n*10^2) => O(800n)
Total time complexity => O(64n) + O(800n)
5路合并后跟16路合并
5 way merge - O(n*5^2) => O(25n)
16 way merge - O(5n*16^2) => O(1280n)
Total time complexity => O(25n) + O(1280n)
考虑时间复杂度5 way merge followed by 16 way merge
似乎需要更多时间。你认为我的过程是对的吗?我对此并不十分自信。
更新:@rcgldr因为你说更大的块大小会花更少的时间来读/写所以你怎么看待这个公式:
Time to read/write 1 block = Average Seek time +
Average rotational latency + blocksize/Maximum Transfer Rate
根据这个公式,如果块大小很小,那么整个读/写时间也会更短。你认为这里有什么问题吗?或者我们需要将总块数乘以此值,以获得所需总时间的准确图像。
答案 0 :(得分:2)
这是一种外部排序,因此正常的时间复杂度并不适用,至少在现实世界中并非适用。
为了澄清第一个语句,内存(非外部)中的k-way合并(不是合并排序)合并大小为n的k个数组,因此移动kn数据,并且在简单版本中,k-1比较是为每次移动做出,因此(kn)(k-1)= nk ^ 2 - nk => O(n k ^ 2)。如果堆/优先级队列/ ...用于跟踪当前的最小元素集,则比较的数量减少到log2(k),因此O(n k log2(k))。
内存中的n个元素的k-way合并排序采用⌈logk(n)⌉== ceil(logk(n))传递,在每次传递时移动n个元素,因此n⌈logk(n)⌉。使用堆/优先级队列/ ...来实现k个元素的比较需要⌊log2(k)⌋== floor(log2(k)),所以(n⌈logk(n)⌉)(⌊log2(k)⌋ )。如果n是k的幂,并且k是2的幂,则复杂度是n logk(n)log2(k)= n log2(n)。 k对于复杂性并不重要,但实际运行时间可能更好或更差,k> 1。 2意味着更多的比较,但移动更少,因此问题是比较开销与运行开销,例如将指针数组排序到对象。
回到外部排序,假设进程受I / O限制,那么复杂性与I / O有关,而不是cpu操作。
这两种方法都需要3次读取/写入80GB的数据。第一遍创建了80个1GB集群的实例。下一个通道一次合并5个或8个簇,最后一个通道一次合并16或10个簇。主要问题是一次读取或写入大块数据以减少随机访问开销。 16路合并将1GB内存分解为比10路合并更小的块,这可能会使随机访问开销略有不同,因此8/10方法可能会更快一些。如果使用固态驱动器进行外部排序,那么随机访问/块大小就不那么重要了。
另一个问题是如果10路或16路合并在内存中运行得足够快,以便合并过程受I / O限制。