输入: n n n个待排序的数据, P P P个处理器。
输出:从小到大排序的数据。
算法思想: P P P个处理器对自己的 n / P n/P n/P个数据进行局部排序。每个处理器从局部有序的序列中选出 P P P个数据作为自己的代表元素并将它们送到 P 0 P_0 P0中, P 0 P_0 P0对这 P 2 P^2 P2个元素进行排序,然后从中选取 P − 1 P-1 P−1个主元并将它们广播到所有的处理器中。然后每个处理器根据这 P − 1 P-1 P−1个主元把自己的局部有序序列划分成 P P P个部分,把每个部分送给对应的处理器,然后再对自己收到的部分做 P P P路归并排序,最后把它们放到数组的对应位置上即可。
环境: V S 2019 VS2019 VS2019
c o d e : code: code:
#include
#include
#include
#include
#include
#include
#include
#include
#define pr pair
using namespace std;
inline void quicksort(int* vec, int beg, int end, int* pivots, int numprocs, int w) //对给定区间的元素进行排序 并选取代表元素
{
sort(vec+beg, vec + end); //排序
for (int i = 0; i < numprocs; i++) //选取代表元素
{
pivots[i] = vec[beg + i * w];
}
}
inline void AnoSort(int* src, int* dst, int* recvcount, int* rdispls, int numprocs,int totalnum)
{
int cur = 0, MIN, idx;
vector<int> cnt(numprocs);
while (cur < totalnum)
{
idx = -1;
for (int i = 0; i < numprocs; i++)
{
if (cnt[i] != recvcount[i])
{
if (idx == -1 || src[cnt[i] + rdispls[i]] < MIN)
MIN = src[cnt[i] + rdispls[i]], idx = i;
}
}
dst[cur++] = MIN;
++cnt[idx];
}
}
inline void MergeSort(int* src, int* dst, int* recvcount, int* rdispls, int numprocs) //另外一种多路归并排序
{
priority_queue<pr, vector<pr>, greater<pr>> q;//first 为具体的值 second为row的下标
vector<int> idx(numprocs);
for (int i = 0; i < numprocs; i++)
{
if (idx[i] != recvcount[i])
{
q.push(pr(src[idx[i] + rdispls[i]], i));
++idx[i];
}
}
int cur = 0;
pr tmp;
while (!q.empty())
{
tmp = q.top();
q.pop();
dst[cur++] = tmp.first;
if (idx[tmp.second] != recvcount[tmp.second])
{
tmp.first = src[idx[tmp.second] + rdispls[tmp.second]];
++idx[tmp.second];
q.push(tmp);
}
}
}
inline void choosepivots(int* pivots, int numprocs, int myid,int *final_pivots) //选取主元并广播
{
int* root_pivots = new int[numprocs * numprocs];; //收集pivots数组
MPI_Gather(pivots, numprocs, MPI_INT, root_pivots, numprocs, MPI_INT, 0, MPI_COMM_WORLD);
if (myid == 0) //0号进程
{
sort(root_pivots, root_pivots + numprocs * numprocs);
for (int i = 1; i < numprocs; i++) //选择排序后的第P-1,2(P-1),…,(P-1)(P-1)个共P-1个主元;
final_pivots[i - 1] = root_pivots[i * numprocs];
}
MPI_Bcast(final_pivots, numprocs - 1, MPI_INT, 0, MPI_COMM_WORLD); //将最终选取的主元广播出去
delete[] root_pivots;
}
inline void PSRS(int* vec, int n)
{
int myid, numprocs;
MPI_Comm_rank(MPI_COMM_WORLD, &myid); //得到进程id
MPI_Comm_size(MPI_COMM_WORLD, &numprocs); //进程总数
//if (!myid)
//{
// cout << "初始数组:\n";
// for (int i = 0; i < n; i++)
// cout << vec[i] << ' ';
// cout << "\n";
//}
clock_t start = clock();
int num = n / numprocs; //每个处理器要处理的数据
int beg = myid * num; //区间起始位置
int end = (myid + 1) * num; //区间结束位置 左闭右开
int w = n / (numprocs * numprocs); //选取代表元素
end = min(end, n); //限制区间右端点的最大值
int* pivots = new int[numprocs];
quicksort(vec, beg, end, pivots, numprocs, w); //进行局部排序 并选取代表元素
if (numprocs <= 1) //仅有1个进程
{
clock_t end = clock();
//cout << "排完序后:\n";
//for (int i = 0; i < n; i++)
// cout << myarray[i] << ' ';
cout << end - start << endl;
delete[] pivots;
return;
}
int* final_pivots = new int[numprocs - 1];
choosepivots(pivots, numprocs, myid, final_pivots); //选取主元并广播
delete[] pivots;
int* sendcount = new int[numprocs], * sdispls = new int[numprocs]; //记录分割后 每一段的个数 全局交换需要用到
int* recvcount = new int[numprocs], * rdispls = new int[numprocs];
sdispls[0] = beg;
sendcount[0] = upper_bound(vec + beg, vec + end, final_pivots[0]) - vec - sdispls[0];
for (int i = 1; i < numprocs - 1; i++)
{
sdispls[i] = sdispls[i - 1] + sendcount[i - 1]; //记录每一段发送的起始位置
sendcount[i] = upper_bound(vec + beg, vec + end, final_pivots[i]) - vec - sdispls[i];//计算每一段发送的个数
}
sdispls[numprocs - 1] = sdispls[numprocs - 2] + sendcount[numprocs - 2];
sendcount[numprocs - 1] = end - sdispls[numprocs - 1];
delete[] final_pivots;
MPI_Alltoall(sendcount, 1, MPI_INT, recvcount, 1, MPI_INT, MPI_COMM_WORLD); //记录每一段要接受的个数
int totalnum = recvcount[0];
rdispls[0] = 0;
for (int i = 1; i < numprocs; i++)
{
totalnum += recvcount[i];
rdispls[i] = rdispls[i - 1] + recvcount[i - 1];
}
int* result = new int[totalnum];
MPI_Alltoallv(vec, sendcount, sdispls, MPI_INT, result, recvcount, rdispls, MPI_INT, MPI_COMM_WORLD); //全局交换
int* sort_result = new int[totalnum];
//MergeSort(result, sort_result, recvcount, rdispls, numprocs); //多路归并
AnoSort(result, sort_result, recvcount, rdispls, numprocs, totalnum);
int* num_idx = new int[numprocs]; //存储每个进程控制的元素数
MPI_Gather(&totalnum, 1, MPI_INT, num_idx, 1, MPI_INT, 0, MPI_COMM_WORLD); //汇集信息到根进程中
int* finalpos = new int[numprocs];
if (myid == 0)
{
finalpos[0] = 0;
for (int i = 1; i < numprocs; i++)
{
finalpos[i] = finalpos[i - 1] + num_idx[i - 1]; //最终放置的位置
}
}
MPI_Gatherv(sort_result, totalnum, MPI_INT, vec, num_idx, finalpos, MPI_INT, 0, MPI_COMM_WORLD);
if (!myid)
{
clock_t end = clock();
//cout << "排完序后:\n";
//for (int i = 0; i < n; i++)
// cout << vec[i] << ' ';
//cout << endl;
cout << end - start << endl;
}
delete[] num_idx;
delete[] finalpos;
delete[] result;
delete[] sort_result;
delete[] sendcount;
delete[] sdispls;
delete[] recvcount;
delete[] rdispls;
}
int main(int argc, char* argv[])
{
int n = 1e7; //元素个数
int* vec = new int[n];
srand(time(0));
for (int i = 0; i < n; i++)
{
//vec[i] = i + 1;
vec[i] = rand(); //数组
}
MPI_Init(&argc, &argv);
PSRS(vec, n); //排序
MPI_Finalize();
return 0;
}