使用mpi并行技术实现快排Qsort()

这篇具有很好参考价值的文章主要介绍了使用mpi并行技术实现快排Qsort()。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

快排基本原理:

快速排序可以说是最为常见的排序算法,冒泡排序时间复杂度达到了O(N2),而桶排序容易造成浪费空间。快排(Quicksort)就成为了不错的选择。

1、原理:快排需要找一个数作为基准数,用来参照。(可取第一个数为参照)

        基准数在中间某位置,两端有指针,找到相应数后,交换。

注意:若令第一个数为基准数,先从右往左找,再从左往右找。

2、优点:平均时间复杂度O(NlogN),相比冒泡排序每次交换可以是跳跃式的

排序过程:

使用mpi并行技术实现快排Qsort()

代码实现:

#include<mpi.h>
#include<time.h>
#include <stdlib.h>
#include <stdio.h>

int Partition(int* data, int start, int end)   //划分数据
{
    int temp = data[start];   //以第一个元素为基准
    while (start < end) {
        while (start < end && data[end] >= temp)end--;   //找到第一个比基准小的数
        data[start] = data[end];
        while (start < end && data[start] <= temp)start++;    //找到第一个比基准大的数
        data[end] = data[start];
    }
    data[start] = temp;   //以基准作为分界线
    return start;
}

void QuickSort(int* data, int start, int end)  //串行快排
{
    if (start < end) {    //未划分完
        int r = Partition(data, start, end);   //继续划分,进行递归排序
        QuickSort(data, start, r - 1);
        QuickSort(data, r + 1, end);
    }
}

//求2的n次方
int exp2(int n)
{
    int i = 1;
    while (n-- > 0) i *= 2;
    return i;
}

//求以2为底n的对数,向下取整
int log2(int n)
{
    int i = 1, j = 2;
    while (j < n) {
        j *= 2;
        i++;
    }
    return i;
}

void paraQuickSort(int* data, int start, int end, int m, int id, int nowID, int N)
{
    int i, j, r = end, length = -1;  //r表示划分后数据前部分的末元素下标,length表示后部分数据的长度
    int* t;
    MPI_Status status;
    if (m == 0) {   //无进程可以调用
        if (nowID == id) QuickSort(data, start, end);
        return;
    }
    if (nowID == id) {    //当前进程是负责分发的
        while (id + exp2(m - 1) > N && m > 0) m--;   //寻找未分配数据的可用进程
        if (id + exp2(m - 1) < N) {  //还有未接收数据的进程,则划分数据
            r = Partition(data, start, end);
            length = end - r;
            MPI_Send(&length, 1, MPI_INT, id + exp2(m - 1), nowID, MPI_COMM_WORLD);
            if (length > 0)   //id进程将后部分数据发送给id+2^(m-1)进程
                MPI_Send(data + r + 1, length, MPI_INT, id + exp2(m - 1), nowID, MPI_COMM_WORLD);
        }
    }
    if (nowID == id + exp2(m - 1)) {    //当前进程是负责接收的
        MPI_Recv(&length, 1, MPI_INT, id, id, MPI_COMM_WORLD, &status);
        if (length > 0) {   //id+2^(m-1)进程从id进程接收后部分数据
            t = (int*)malloc(length * sizeof(int));
            if (t == 0) printf("Malloc memory error!");
            MPI_Recv(t, length, MPI_INT, id, id, MPI_COMM_WORLD, &status);
        }
    }
    j = r - 1 - start;
    MPI_Bcast(&j, 1, MPI_INT, id, MPI_COMM_WORLD);
    if (j > 0)     //负责分发的进程的数据不为空
        paraQuickSort(data, start, r - 1, m - 1, id, nowID, N);   //递归调用快排函数,对前部分数据进行排序
    j = length;
    MPI_Bcast(&j, 1, MPI_INT, id, MPI_COMM_WORLD);
    if (j > 0)     //负责接收的进程的数据不为空
        paraQuickSort(t, 0, length - 1, m - 1, id + exp2(m - 1), nowID, N);   //递归调用快排函数,对前部分数据进行排序
    if ((nowID == id + exp2(m - 1)) && (length > 0))     //id+2^(m-1)进程发送结果给id进程
        MPI_Send(t, length, MPI_INT, id, id + exp2(m - 1), MPI_COMM_WORLD);
    if ((nowID == id) && id + exp2(m - 1) < N && (length > 0))     //id进程接收id+2^(m-1)进程发送的结果
        MPI_Recv(data + r + 1, length, MPI_INT, id + exp2(m - 1), id + exp2(m - 1), MPI_COMM_WORLD, &status);
}

int main(int argc, char* argv[])
{
    int* data;
    int rank, size;
    int i, j, m, r, n = atoi(argv[1]);   //随机数组的长度
    double start_time, end_time;
    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);  //当前进程的进程号
    MPI_Comm_size(MPI_COMM_WORLD, &size);  //总进程数
    if (rank == 0) {   //根进程生成随机数组
        start_time = MPI_Wtime();
        data = (int*)malloc(n * sizeof(int));
        srand(time(NULL) + rand());   //随机数种子
        for (i = 0; i < n; i++)
            data[i] = (int)rand();   //获取n个随机整数
    }
    m = log2(size);  //第一次分发需要给第2^(m-1)个进程
    
    MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);  //广播n
    paraQuickSort(data, 0, n - 1, m, 0, rank, size);  //执行快排
    

    if (rank == 0) {   //根进程输出并行时间
        end_time = MPI_Wtime();
        for (i = 0; i < 10 && i<n; i++)//输出前十个元素
           printf("%d ", data[i]);
        printf("\n并行时间:%lfs\n", end_time - start_time);
    }
    
    MPI_Finalize();
}

运行结果:

Mpi快排结果:

使用mpi并行技术实现快排Qsort()

注1000后面的参数为生成随机数的个数

编译:mpicxx ./filename.cpp -o ./filename

Mpi基本原理:

  1.什么是MPI

Massage Passing Interface:是消息传递函数库的标准规范,由MPI论坛开发。

一种新的库描述,不是一种语言。共有上百个函数调用接口,提供与C和Fortran语言的绑定

MPI是一种标准或规范的代表,而不是特指某一个对它的具体实现

MPI是一种消息传递编程模型,并成为这种编程模型的代表和事实上的标准

2.MPI的特点

MPI有以下的特点:

消息传递式并行程序设计

指用户必须通过显式地发送和接收消息来实现处理机间的数据交换。

在这种并行编程中,每个并行进程均有自己独立的地址空间,相互之间访问不能直接进行,必须通过显式的消息传递来实现。

这种编程方式是大规模并行处理机(MPP)和机群(Cluster)采用的主要编程方式。

并行计算粒度大,特别适合于大规模可扩展并行算法

用户决定问题分解策略、进程间的数据交换策略,在挖掘潜在并行性方面更主动,并行计算粒度大,特别适合于大规模可扩展并行算法

消息传递是当前并行计算领域的一个非常重要的并行程序设计方式

二、MPI的基本函数

MPI调用借口的总数虽然庞大,但根据实际编写MPI的经验,常用的MPI函数是以下6个:

MPI_Init(…);

MPI_Comm_size(…);

MPI_Comm_rank(…);

MPI_Send(…);

MPI_Recv(…);

MPI_Finalize();

三、MPI的通信机制

MPI是一种基于消息传递的编程模型,不同进程间通过消息交换数据。

1.MPI点对点通信类型

所谓点对点的通信就是一个进程跟另一个进程的通信,而下面的聚合通信就是一个进程和多个进程的通信。

使用mpi并行技术实现快排Qsort()

  1. 标准模式:

使用mpi并行技术实现快排Qsort()

该模式下MPI有可能先缓冲该消息,也可能直接发送,可理解为直接送信或通过邮局送信。是最常用的发送方式。

由MPI决定是否缓冲消息

没有足够的系统缓冲区时或出于性能的考虑,MPI可能进行直接拷贝:仅当相应的接收完成后,发送语句才能返回。

这里的系统缓冲区是指由MPI系统管理的缓冲区。而非进程管理的缓冲区。

MPI环境定义有三种缓冲区:应用缓冲区、系统缓冲区、用户向系统注册的通信用缓冲区

MPI缓冲消息:发送语句在相应的接收语句完成前返回。

这时后发送的结束或称发送的完成== 消息已从发送方发出,而不是滞留在发送方的系统缓冲区中。

该模式发送操作的成功与否依赖于接收操作,我们称之为非本地的,即发送操作的成功与否跟本地没关系。文章来源地址https://www.toymoban.com/news/detail-497530.html

到了这里,关于使用mpi并行技术实现快排Qsort()的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CUDA 以及MPI并行矩阵乘连接服务器运算vscode配置

    本地安装 服务器端安装 c_cpp_properties.json launch.json tasks.json     本地安装和服务器端安装的扩展和CUDA一样 c_cpp_properties.json launch.json settings.json tasks.json

    2024年04月27日
    浏览(30)
  • C语言库函数之 qsort 讲解、使用及模拟实现

    我们在学习排序的时候,第一个接触到的应该都是冒泡排序,我们先来复习一下冒泡排序的代码,来作为一个铺垫和引入。 代码如下: 很简单的一种排序方法,但我们可以发现一个问题,那就是冒泡排序不够通用,它只能用于整型数组的排序,如果我要排序float类型,或者排

    2024年02月12日
    浏览(29)
  • 【C语言】回调函数,qsort排序函数的使用和自己实现,超详解

    先记录一下访问量突破2000啦,谢谢大家支持!!! 这里是上期指针进阶链接,方便大家查看:添加链接描述 大家好呀,今天分享一下上期指针进阶中剩余的内容——回调函数,这个很重要滴,让我们一起来学会学懂他吧!!! 标准概念: 回调函数就是一个通过函数指针调

    2024年02月12日
    浏览(40)
  • 使用omp并行技术加速最短路径算法-迪杰斯特拉(Dijkstra)算法(记录最短路径和距离)

    原理: Dijkstra算法是解决**单源最短路径**问题的**贪心算法** 它先求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径     直到求出从源点到其他各个顶点的最短路径。 首先假定源点为u,顶点集合V被划分为两部分:集合 S 和 V-S。 初始时S中仅含有源点u,

    2024年02月10日
    浏览(35)
  • 谷歌seo快排技术怎么做?Google排名霸屏推广原理

    本文主要分享关于谷歌快速排名的方法和所需要的条件。 本文由光算创作,有可能会被剽窃和修改,我们佛系对待这种行为吧。 首先提出一个问题:谷歌seo快排技术怎么做?如何达到谷歌霸屏的效果? 答案是: 利用谷歌蜘蛛池+谷歌搜索留痕即可实现。 我们知道,谷歌seo是

    2024年02月06日
    浏览(50)
  • 什么是谷歌快排技术,谷歌排名推广霸屏的原理

    谷歌快排是怎么做的? 答案是: 利用GLB外推快速上词达到谷歌霸屏的效果,俗称谷歌快排,也叫谷歌快速排名技术。 想做到谷歌快速排名,需要具备谷歌对页面排名的机制,并且要具备更底层的技术操控才能实现。 你可以阅读:什么叫GLB外推? 谷歌快排到底能快到什么程

    2023年04月22日
    浏览(31)
  • MPI实现矩阵向量乘法

    (1)问题 MPI实现矩阵向量:Ab的乘积。其中A:100行100列,b为列向量。 (2)思路 将所有进程分为两部分,rank=0的进程为master节点,其余进程为worker节点。 master节点: (1)对A,b赋值,同时将b广播出去(这里涉及一个对广播这个函数不太熟悉的点) (2)对A进行划分,使其

    2024年02月11日
    浏览(26)
  • MPI和OpenMP实现蒙特卡罗算法

    基本思想 当所求解问题是某种随机事件出现的概率,或者是某个随机变量的期望值时,通过某种“实验”的方法,以这种事件出现的频率估计这一随机事件的概率,或者得到这个随机变量的某些数字特征,并将其作为问题的解。 数学应用: 通常蒙特·卡罗方法通过构造符合

    2024年02月05日
    浏览(30)
  • 妙用指针实现qsort

    qsort函数是C语言标准库中的一个快速排序函数,位于stdlib.h头文件中。(可以对任意类型进行排序的函数) 函数为: 参数解释 参数base - base指向数组的起始地址,通常该位置传入的是一个数组名 参数nmemb - nmemb表示该数组的元素个数 参数size - size表示该数组中每个元素的大小

    2024年02月14日
    浏览(25)
  • 冒泡排序模拟实现qsort()函数

    要模拟qsort()函数,我们首先要知道qsort()函数的特点: 使用快速排序的方法。 适用于任何数据类型的排序。 但由于部分学者还没有学习快速排序算法,所以本篇博客采用冒泡排序来模拟功能类似于qsort()的函数bubble_sort。 C库对qsort()函数解释: 我们得到的关于qsort()函数参

    2024年02月16日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包