MPI 实现并行快排

主从模式,0号进程是主人,1-4号进程是工人

// 4 个进程的快速排序
// 运行时输入 mpiexec -n 5 name
#include 
#include 
#include 
#include 

using namespace std;

const int TOTAL_SIZE = 1000;
int original[TOTAL_SIZE], sorted[TOTAL_SIZE];

void quickSort(int* arr, int e, int base, int& i)
{	//对数组arr在区间[b,e)上进行快排
	int l = -1, r = e;
	i = 0;
	while (i < r)
	{
		if (arr[i] < base)
			swap(arr[i++], arr[++l]);
		else if (arr[i] > base)
			swap(arr[i], arr[--r]);
		else i++;
	}
	// 结束时 [0,i) 的 <= base, [i,e) 的 > base
}

void bubblingSort(int arr[], int n) 
{
	int i, j, temp;
	// 每次将一个元素送到末尾,n个元素,执行n次
	for (i = 0; i < n; ++i) {
		// 之前的循环已经将i个元素送到末尾,不需要再次比较,故减去,因为跟后一个元素比较,为了避免溢出,故减一
		for (j = 0; j < n - i - 1; ++j) {
			// 如果当前的元素比后一个元素小,就交换
			if (arr[j] > arr[j + 1]) {
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

int main(int argc, char* argv[])
{
	srand(NULL);

	int process_num, my_ID;
	MPI_Init(&argc, &argv);
	MPI_Comm_size(MPI_COMM_WORLD, &process_num);
	MPI_Comm_rank(MPI_COMM_WORLD, &my_ID);

	int base;
	int* arr = new int[TOTAL_SIZE];
	int subSize[5], border[5];
	for (int i = 0; i < 5; i++) subSize[i] = TOTAL_SIZE / 4;
		
	if (my_ID == 0)				// 零号进程获得初始化数组,并发给1-4号进程
	{
		printf("我是主进程,初始序列是: ");
		for (int i = 0; i < TOTAL_SIZE; i++)
		{
			original[i] = rand() % TOTAL_SIZE;
			printf("%d ", original[i]);
		}
		printf("\n");
		for (int i = 1; i < 5; i++)
			MPI_Send(original + (i - 1) * (TOTAL_SIZE / 4), TOTAL_SIZE / 4, MPI_INT, i, i, MPI_COMM_WORLD);
	}
	else 
	{
		MPI_Recv(arr, subSize[my_ID], MPI_INT, 0, my_ID, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
	}
	
	int cnt = 0;
	while (++cnt <= 2)
	{
		if (cnt == 1)			// 第一次快排,1号进程取基数,广播出去
		{
			if (my_ID == 1)
			{
				base = arr[subSize[my_ID] / 2];
			}
			MPI_Bcast(&base, 1, MPI_INT, 1, MPI_COMM_WORLD);
		}
		else if (cnt == 2)		// 第二次快排,1、3号进程取基数,发给2、4号进程
		{
			if (my_ID == 1 || my_ID == 3)
			{
				base = arr[subSize[my_ID] / 2];
				MPI_Send(&base, 1, MPI_INT, my_ID + 1, my_ID, MPI_COMM_WORLD);
			}
			else if (my_ID == 2 || my_ID == 4)
			{
				MPI_Recv(&base, 1, MPI_INT, my_ID - 1, my_ID - 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
			}
		}

		quickSort(arr, subSize[my_ID], base, border[my_ID]);	// 各进程快排

		// 交换数据
		if (cnt == 1)	// 第一次快排后,1和3、2和4交换数据,各进程先通讯要发送的数组大小,再发送数据
		{
			if (my_ID == 1 || my_ID == 2)	// 1、2 给 3、4发数组大小
			{
				MPI_Send(&border[my_ID], 1, MPI_INT, my_ID + 2, my_ID, MPI_COMM_WORLD);
				MPI_Recv(&border[my_ID + 2], 1, MPI_INT, my_ID + 2, my_ID + 2, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(arr, border[my_ID], MPI_INT, my_ID + 2, my_ID, MPI_COMM_WORLD);
				MPI_Recv(arr + subSize[my_ID], subSize[my_ID + 2] - border[my_ID + 2], MPI_INT, my_ID + 2, my_ID + 2, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				subSize[my_ID] = subSize[my_ID] - border[my_ID] + subSize[my_ID + 2] - border[my_ID + 2];
				for (int i = 0; i < subSize[my_ID]; i++)
					arr[i] = arr[i + border[my_ID]];

			}
			if (my_ID == 3 || my_ID == 4)	// 3、4 接收大小并给 1、2 发送大小
			{
				MPI_Recv(&border[my_ID - 2], 1, MPI_INT, my_ID - 2, my_ID - 2, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(&border[my_ID], 1, MPI_INT, my_ID - 2, my_ID, MPI_COMM_WORLD);
				MPI_Recv(arr + subSize[my_ID], border[my_ID - 2], MPI_INT, my_ID - 2, my_ID - 2, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(arr + border[my_ID], subSize[my_ID] - border[my_ID], MPI_INT, my_ID - 2, my_ID, MPI_COMM_WORLD);
				for (int i = 0; i < border[my_ID - 2]; i++)
					arr[i + border[my_ID]] = arr[i + subSize[my_ID]];
				subSize[my_ID] = border[my_ID] + border[my_ID - 2];
			}
		}
		else if (cnt == 2)	// 第二次快排后,1和2、3和4交换数据,各进程先通讯要发送的数组大小,再发送数据
		{
			if (my_ID == 1 || my_ID == 3)
			{
				MPI_Send(&subSize[my_ID], 1, MPI_INT, my_ID + 1, my_ID, MPI_COMM_WORLD);
				MPI_Recv(&subSize[my_ID + 1], 1, MPI_INT, my_ID + 1, my_ID + 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(&border[my_ID], 1, MPI_INT, my_ID + 1, my_ID, MPI_COMM_WORLD);
				MPI_Recv(&border[my_ID + 1], 1, MPI_INT, my_ID + 1, my_ID + 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(arr, border[my_ID], MPI_INT, my_ID + 1, my_ID, MPI_COMM_WORLD);
				MPI_Recv(arr + subSize[my_ID], subSize[my_ID + 1] - border[my_ID + 1], MPI_INT, my_ID + 1, my_ID + 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				subSize[my_ID] = subSize[my_ID] - border[my_ID] + subSize[my_ID + 1] - border[my_ID + 1];
				for (int i = 0; i < subSize[my_ID]; i++)
					arr[i] = arr[i + border[my_ID]];
			}
			if (my_ID == 2 || my_ID == 4)
			{
				MPI_Recv(&subSize[my_ID - 1], 1, MPI_INT, my_ID - 1, my_ID - 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(&subSize[my_ID], 1, MPI_INT, my_ID - 1, my_ID, MPI_COMM_WORLD);
				MPI_Recv(&border[my_ID - 1], 1, MPI_INT, my_ID - 1, my_ID - 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(&border[my_ID], 1, MPI_INT, my_ID - 1, my_ID, MPI_COMM_WORLD);
				MPI_Recv(arr + subSize[my_ID], border[my_ID - 1], MPI_INT, my_ID - 1, my_ID - 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
				MPI_Send(arr + border[my_ID], subSize[my_ID] - border[my_ID], MPI_INT, my_ID - 1, my_ID, MPI_COMM_WORLD);
				for (int i = 0; i < border[my_ID - 1]; i++)
					arr[i + border[my_ID]] = arr[i + subSize[my_ID]];
				subSize[my_ID] = border[my_ID] + border[my_ID - 1];
			}
		}
	}


	if (my_ID != 0)		// 各个进程将自己的数组大小发送给 0 号进程
	{
		MPI_Send(&subSize[my_ID], 1, MPI_INT, 0, my_ID, MPI_COMM_WORLD);
	}
	else
	{
		MPI_Recv(&subSize[1], 1, MPI_INT, 1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&subSize[2], 1, MPI_INT, 2, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&subSize[3], 1, MPI_INT, 3, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(&subSize[4], 1, MPI_INT, 4, 4, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
	}

	if (my_ID != 0)			// 快排后,每个子进程冒泡排序并把数据发给 0 进程
	{
		bubblingSort(arr, subSize[my_ID]);
		MPI_Send(arr, subSize[my_ID], MPI_INT, 0, my_ID, MPI_COMM_WORLD);
	}
	else 
	{
		MPI_Recv(sorted, subSize[4], MPI_INT, 4, 4, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(sorted + subSize[4], subSize[3], MPI_INT, 3, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(sorted + subSize[4] + subSize[3], subSize[2], MPI_INT, 2, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		MPI_Recv(sorted + subSize[4] + subSize[3] + subSize[2], subSize[1], MPI_INT, 1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

		printf("我是主进程,新序列是:\n");
		for (auto p : sorted)
			printf("%d ", p);
		puts("");
	}

	delete[] arr;
	MPI_Finalize();
	return 0;
}

你可能感兴趣的:(C++,排序算法,算法,c++)