Skip to content

Lec15 External Sorting

External Sorting

ref

减少Pass数量

pass数量分析

\[ \text{\#pass}=1+\lceil\log_k\lceil\frac{N}{M} \rceil\rceil \]

Replacement Selection

https://web.eecs.utk.edu/~bvanderz/cs302/notes/replacement_selection.html

过程:

需要一个长度为m的优先队列。开始时先把前m个数读入优先队列。之后对于每个输入的元素x:

  1. 把队头元素t出队。如果t没有标记,则t放到当前run. 如果t有标记,则新开一个run, 把新的run作为当前run
  2. 比较x和t
    1. 如果x<t, 说明x是下一个run的, 把x打一个标记,放到优先队列末尾。
    2. 否则说明x是当前run的,把x插入优先队列前面。

利用替换选择策略以后,每一次生成的 run 一定不小于M(末端余数除外),于是相对来说#pass就会减少

Code
#include<bits/stdc++.h>
#define MAXN 100000
using namespace std;
int n,m;
int numRun=1;
bool curMark=0;
struct node{
    int val;
    bool isNextRun;
    node(int val,bool isNextRun):val(val),isNextRun(isNextRun){}
    friend bool operator<(node a,node b){
        if(a.isNextRun!=b.isNextRun)
            return a.isNextRun!=curMark;
        return a.val>b.val;//std::priority_queue默认是大根堆,所以这里要取反
    }
};
priority_queue<node>q;
vector<int>run[MAXN+5];
int main(){
    int x;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        if(i<=m) q.push(node(x,curMark));
        else{
            node top=q.top();q.pop();//取出队头最小值
            if(top.isNextRun!=curMark){
                //如果队头标记是下一个run的,说明现在队列里所有元素都是下一个run的,那么要新开一个run
                numRun++;//新开一个run
                run[numRun].push_back(top.val);
                curMark=!curMark;//标记取反,因为新run现在成为了当前run
            }else{
                run[numRun].push_back(top.val);//加入到当前run
            }
            //如果当前值小于最小值,那就是下一个run,放到队尾
            if(x<top.val) q.push(node(x,!curMark));
            else q.push(node(x,curMark));//否则就是当前run
        }
    }
    while(!q.empty()){
        node top=q.top();q.pop();
        if(top.isNextRun!=curMark){
            numRun++;
            run[numRun].push_back(top.val);
            curMark=!curMark;
        }else{
            run[numRun].push_back(top.val);
        }
    }
    for(int i=1;i<=numRun;i++){
        for(int j=0;j<run[i].size();j++){
            if(j!=0) printf(" ");
            printf("%d",run[i][j]);
        }
        printf("\n");
    }
}

减少tape数量(不均匀切分)

朴素做法: k路归并需要2k个taoe

利用斐波那契数列来切分: k路归并需要k+1个tape

并行化

k路归并, 2k个input buffer, 2个output buffer

需要两倍buffer的原因: 对于每一路, 用两个buffer (记为A,B. A用于写从硬盘中读进来的数据,B用于排序。读入和排序可以同时进行。当排序 bufferB 空了的时候,就和读满了的A交换,对A进行排序,同时空的buffer B则可用于继续读入。

buffer也是内存的一部分

Comments