Lec15 External Sorting
External Sorting
减少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:
- 把队头元素t出队。如果t没有标记,则t放到当前run. 如果t有标记,则新开一个run, 把新的run作为当前run
- 比较x和t
- 如果x<t, 说明x是下一个run的, 把x打一个标记,放到优先队列末尾。
- 否则说明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也是内存的一部分