快速排序并行实现MPI

简介

常规的快速排序,先在数组中选取一个基准点,将数组分区为小于基准点和大于基准点(相同的数可以到任一边),对分区的子数组递归的执行分区的操作,当子数组长度为1时退出递归。此时数组就完成了排序过程。
对快排的过程分析可以发现,分区以及对子数组排序的过程均可以并发执行,这里首先对数组进行分区,生成分区数组,为了保证不同分区不受到影响需要先完成分区再进行排序。
处理器个数:4
数组长度:1000
具体过程为:先在0号处理器创建一个1000维的数组,将数组广播道所有处理器,在0处理器对数组进行partition,将根据pivot值划分为两个子数组,左边的数组仍然留在0号进程,右边子数组分配给1号处理器,然后对0号和1号处理器中的数组分别进行递归操作,将0处理器中数组分给2号处理器,将1号处理器数据分给3号处理器,当没有可用的处理器时,数据划分完毕,开始各自进行快速排序,排序结束后,逐级向上返回,最后数据全部返回0处理器,排序结束。

代码

	#include "StdAfx.h"
  	#include "stdafx.h"
  	#include"mpi.h"
	#include<time.h>
  	#include<iostream>
  	#include <stdlib.h>
  	#include <stdio.h>
  	using namespace std;
  	#define TRUE 1
    #define SIZE 10000
  	
	
double wht_start_time,wht_end_time, end_time, start_time;  	
int Partition(int *wht_data,int wht_start,int wht_end)
  {
  	int wht_pivo;
  	int wht_i, wht_j;
  	int wht_tmp;
  	wht_pivo=wht_data[wht_end];
  	wht_i=wht_start-1; /*wht_i(活动指针)*/
  	for(wht_j=wht_start;wht_j<wht_end;wht_j++)
  		if(wht_data[wht_j]<=wht_pivo)
  		{
  			wht_i++; /*wht_i表示比wht_pivo小的元素的个数*/
  			wht_tmp=wht_data[wht_i];
  			wht_data[wht_i]=wht_data[wht_j];
  			wht_data[wht_j]=wht_tmp;
  		}
  	wht_tmp=wht_data[wht_i+1];
  	wht_data[wht_i+1]=wht_data[wht_end];
  	wht_data[wht_end]=wht_tmp; /*以wht_pivo为分界,wht_data[wht_i+1]=wht_pivo*/
  	return wht_i+1;
  }

void QuickSort(int *wht_data,int wht_start,int wht_end)
{
  	int wht_r;
  	int wht_i;
  	if(wht_start<wht_end)
  	{
  		wht_r=Partition(wht_data,wht_start,wht_end);
  		QuickSort(wht_data,wht_start,wht_r-1);
  		QuickSort(wht_data,wht_r+1,wht_end);
  	}
}
  	

	/*功能:求2的wht_num次幂*/
int exp2(int wht_num)
{
  	int wht_i;
  	wht_i=1;
  	while(wht_num>0)
  	{
  		wht_num--;
  		wht_i=wht_i*2;
  	}
  	return wht_i;
}


	/*功能:求以2为底的wht_num的对数*/
int log2(int wht_num)
{
  	int wht_i, wht_j;
  	wht_i=1;
  	wht_j=2;
  	while(wht_j<wht_num)
  	{
  		wht_j=wht_j*2;
  		wht_i++;
  	}
  	if(wht_j>wht_num)
  		wht_i--;
  	return wht_i;
}
  	 
  	 
  	/*
  	* 函数名: para_QuickSort
  	* 功能:并行快速排序,对起止位置为wht_start和wht_end的序列,使用2的wht_m次幂个处理器进行排序
  	* 输入:无序数组wht_data[1,n],使用的处理器个数2^wht_m
  	* 输出:有序数组wht_data[1,n]
  	*/


void para_QuickSort(int *wht_data,int wht_start,int wht_end,int wht_m,int id,int MyID)
{
	int wht_i, wht_j;
  	int wht_r;
  	int MyLength;
  	int *wht_tmp;
  	MPI_Status status;
  	MyLength=-1;
  	/*如果可供选择的处理器只有一个,那么由处理器id调用串行排序*/
	if(wht_m==0)
  	{
  		wht_start_time=MPI_Wtime();
  		if(MyID==id)
  		QuickSort(wht_data,wht_start,wht_end);
  		return;
  	}
  	wht_start_time=MPI_Wtime();
  	/*由第id号处理器划分数据,并将后一部分数据发送到处理器id+exp2(wht_m-1),对应于算法步骤(1.2,1.3)*/
  	if(MyID==id)
  	{
  		/*将当前的无序区R[1,n]划分成左右两个无序的子区R[1,wht_i-1]和R[wht_i,n](1≤wht_i≤n)*/
		/*(1.2) Pid: wht_r=patrition(wht_data,wht_i,wht_j)*/
  		wht_r=Partition(wht_data,wht_start,wht_end);
  		MyLength=wht_end-wht_r;
  	/*(1.3) Pid swht_end wht_data[wht_r+1,wht_m-1] to P(id+2m-1) */
  	/* {MyLength表示发送缓冲区地址;*/
  	/* 发送元素数目为1; */
  	/* MyID是消息标签 } */
  		MPI_Send(&MyLength,1,MPI_INT,id+exp2(wht_m-1),MyID,MPI_COMM_WORLD);
  		/*若缓冲区不空,则第id+2m-1号处理器取数据的首址是wht_data[wht_r+1]*/
  		if(MyLength!=0)
  			MPI_Send(wht_data+wht_r+1,MyLength,MPI_INT,id+exp2(wht_m-1),MyID,MPI_COMM_WORLD);
  	}
  	/*处理器idex+p2(wht_m-1)接受处理器id发送的消息*/
  	if(MyID==id+exp2(wht_m-1))
  	{
  		MPI_Recv(&MyLength,1,MPI_INT,id,id,MPI_COMM_WORLD,&status);
  		if(MyLength!=0)
  		{
  			wht_tmp=(int *)malloc(MyLength*sizeof(int));
  			if(wht_tmp==0) printf("Malloc memory error!");
  				MPI_Recv(wht_tmp,MyLength,MPI_INT,id,id,MPI_COMM_WORLD,&status);
  		}
  	}
  	/*递归调用并行排序,对应于算法(1.4,1.5)*/
  	/*用2^wht_m-1个处理器对wht_start--(wht_r-1)的数据进行递归排序*/
  	wht_j=wht_r-1-wht_start;
  	MPI_Bcast(&wht_j,1,MPI_INT,id,MPI_COMM_WORLD);
  	/*(1.4) para_quicksort(wht_data,wht_i,wht_r-1,wht_m-1,id)*/
  	if(wht_j>0)
  		para_QuickSort(wht_data,wht_start,wht_r-1,wht_m-1,id,MyID);
  	/*用2^wht_m-1个处理器对(wht_r+1)--wht_end的数据进行递归排序*/
  	wht_j=MyLength;
  	MPI_Bcast(&wht_j,1,MPI_INT,id,MPI_COMM_WORLD);
  	/*(1.5) para_quicksort(wht_data,wht_r+1,wht_j,wht_m-1,id+2m-1)*/
  	if(wht_j>0)
  		para_QuickSort(wht_tmp,0,MyLength-1,wht_m-1,id+exp2(wht_m-1),MyID);
  	/*将排序好的数据由处理器id+exp2(wht_m-1)发回id号处理器,对应于算法/
  	/*(1.6) P(id+2m-1) swht_end wht_data[wht_r+1,wht_m-1] back to Pid */
  	if((MyID==id+exp2(wht_m-1)) && (MyLength!=0))
  		MPI_Send(wht_tmp,MyLength,MPI_INT,id,id+exp2(wht_m-1),MPI_COMM_WORLD);
  	if((MyID==id) && (MyLength!=0))
  		MPI_Recv(wht_data+wht_r+1,MyLength,MPI_INT,id+exp2(wht_m-1),id+exp2(wht_m-1),MPI_COMM_WORLD,&status);
  }

/*串行快速排序*/
int partition(int *array,int p,int r){
    int x=array[r];     //以最后一个作为主元(pivot element)
    int i=p-1;
    for(int j=p;j<r;++j){
        if(array[j]<x){
            i++;
            swap(array[i],array[j]);
        }
    }
    swap(array[r],array[i+1]);
    return i+1;
}
void quicksort(int *array,int p,int r){
    if(p<r){
        int q=partition(array,p,r);
        quicksort(array,p,q-1);
        quicksort(array,q+1,r);     
    }
}

	/*
  	* 函数名: main
  	* 功能:实现快速排序的主程序
  	* 输入:argc为命令行参数个数;
  	* argv为每个命令行参数组成的字符串数组。
  	* 输出:返回0代表程序正常结束
  	*/
int main(int argc,char *argv[])
{

	double wht_dataSize;
  	int *wht_data;
	int *wht_data1;
  	/*MyID表示进程标志符;SumID表示组内进程数*/
  	int MyID, SumID;
  	int wht_i, wht_j;
  	int wht_m, wht_r;
  	MPI_Status status;
  	/*启动MPI计算*/
  	MPI_Init(&argc,&argv);
  	/*MPI_COMM_WORLD是通信子*/
  	/*确定自己的进程标志符MyID*/
  	MPI_Comm_rank(MPI_COMM_WORLD,&MyID);
  	/*组内进程数是SumID*/
  	MPI_Comm_size(MPI_COMM_WORLD,&SumID);
  	/*根处理机(MyID=0)获取必要信息,并分配各处理机进行工作*/
	if(MyID==0)
  	{
  		/*获取待排序数组的长度*/
  		wht_dataSize=SIZE;
  		wht_data=(int *)malloc(wht_dataSize*sizeof(int));
  		wht_data1=(int *)malloc(wht_dataSize*sizeof(int)); 
  	 
  		/*动态生成待排序序列*/
  		srand(396);
  		printf("排序前的随机数组为 :\n");
  		for(wht_i=0;wht_i<wht_dataSize;wht_i++)
  		{
  			wht_data[wht_i]=(int)rand();
			wht_data1[wht_i]=wht_data[wht_i];
  			if(wht_i<100)
				printf("%d ",wht_data[wht_i]);
  		}
		printf("... ...\n");
  	}
  	wht_m=log2(SumID);
  	/* 从根处理器将数据序列广播到其他处理器*/
  	/*{"1"表示传送的输入缓冲中的元素的个数, */
  	/* "MPI_INT"表示输入元素的类型, */
  	/* "0"表示root processor的ID } */
  	MPI_Bcast(&wht_dataSize,1,MPI_INT,0,MPI_COMM_WORLD);
  	/*ID号为0的处理器调度执行排序*/
  	para_QuickSort(wht_data,0,wht_dataSize-1,wht_m,0,MyID);
  	wht_end_time=MPI_Wtime();
  	/*ID号为0的处理器打印排序完的有序序列*/
	
  	if(MyID==0)
  	{
  		printf("排序后的有序数组为 :\n");
  		for(wht_i=0;wht_i<wht_dataSize;wht_i++)
  			if(wht_i<100)
				printf("%d ",wht_data[wht_i]);
  		printf("... ...\n");
		
		/*计算串行时间*/
		start_time=MPI_Wtime();
		quicksort(wht_data1,0,SIZE-1);
		end_time=MPI_Wtime();
  	
		printf("并行时间 = %fs\n",wht_end_time-wht_start_time);
		printf("串行时间 = %fs\n",end_time-start_time);
		printf("加速比为:%f \n", (end_time-start_time)/(wht_end_time-wht_start_time));
  	}
  	 
  	MPI_Finalize(); //结束计算
}

执行结果

在这里插入图片描述

  • 14
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 使用 MPI 实现快速排序的一种方法是使用分治策略。首先,将数组划分成若干个子数组,然后使用 MPI 进程分别对各自的子数组进行快速排序。然后,在所有进程完成排序之后,使用 MPI 函数进行数据交换,将所有子数组中的元素进行归并。最后,对归并后的数组进行快速排序即可完成整个数组的排序。 下面是一个使用 MPI 实现快速排序的简单示例: ``` #include <stdio.h> #include <stdlib.h> #include <mpi.h> #define N 1000000 int cmp(const void *a, const void *b) { return (*(int *)a - *(int *)b); } int main(int argc, char *argv[]) { int rank, size; int i, pivot, temp; int *data, *sub_data; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); data = (int *)malloc(N * sizeof(int)); sub_data = (int *)malloc((N / size) * sizeof(int)); if (rank == 0) { for (i = 0; i < N; i++) data[i] = rand(); } MPI_Scatter(data, N / size, MPI_INT, sub_data, N / size, MPI_INT, 0, MPI_COMM_WORLD); qsort(sub_data, N / size, sizeof(int), cmp); if (rank != 0) { MPI_Send(sub_data, N / size, MPI_INT, 0, 0, MPI_COMM_WORLD); } else { for (i = 1; i < size; i++) { MPI_Recv(data, N / size, MPI_INT, i, 0, MPI_COMM_WORLD, &status); qsort(data, N / size, sizeof(int), cmp); } qsort(sub ### 回答2: 快速排序是一种常用的排序算法,在并行计算中可以利用MPI实现更高效的排序。 快速排序的基本思想是通过选定一个基准元素,将待排序序列切分成两个子序列,其中一个序列所有元素都小于基准元素,另一个序列所有元素都大于基准元素。然后对两个子序列分别进行快速排序,最终将两个有序序列合并为一。 在MPI中,我们可以利用分布式内存模型,将待排序序列分布到不同的进程上,每个进程负责排序一部分数据。首先,我们选择一个进程作为主进程,负责生成随机序列,并将序列拆分并发送给其他进程。然后,每个进程对接收到的数据进行快速排序。排序结束后,他们将排好序的数据发送给主进程。 在主进程接收到所有排序结果后,可以利用归并操作将多个有序序列合并为一个有序序列,最终得到完整的有序序列。 在实际实现中,需要注意处理边界情况,比如仅有一个进程的情况,此时直接对整个序列进行排序即可。另外,在切分序列并选择基准元素时,需要使用一些算法来提高快速排序的效率,比如选择中位数作为基准元素。 总而言之,通过MPI实现快速排序可以利用多个进程同时处理不同部分的数据,并利用分布式计算资源加速排序过程,从而实现更快速的排序。 ### 回答3: 使用MPI(Message Passing Interface)实现快速排序需要将排序任务划分为多个子任务,并通过消息传递机制协同各个进程进行排序。 首先,选择一个排序算法作为快速排序的基本算法。快速排序的基本思想是通过选择一个基准元素,将待排序序列划分为两个子序列,左侧序列小于等于基准元素,右侧序列大于基准元素,然后分别对左右子序列进行递归排序。 在MPI实现中,可以采用Master-Worker模型。首先,Master进程负责读入待排序序列,并将序列拆分成若干个均匀的子序列,将每个子序列分发给Worker进程。Master进程通过发送消息给Worker进程来分配子序列和指示排序操作。 每个Worker进程接收子序列后,使用快速排序对其进行排序。排序过程中,Worker进程将子序列根据基准元素的大小分为两个子序列,并将左右子序列分别发送给其他Worker进程进行排序。这样不断地划分和排序,直到子序列长度为1或为空,则排序完成。 排序完成后,每个Worker进程将自己的排序好的子序列发送回Master进程。Master进程接收到Worker进程返回的消息后,按照顺序将这些子序列合并起来,得到最终的有序序列。 最后,Master进程将有序序列输出,即得到了使用MPI实现快速排序结果。 需要注意的是,MPI实现快速排序的过程中需要合理地划分子序列,并进行消息传递和接收。同时,应确保MPI进程之间的通信是同步的,以保证排序的正确性和一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值