【并行计算】使用MPI实现简单的并行素数筛法

并行素数筛法的几种思路

1.每个进程负责剔除一个数的倍数:

我们注意到,2的倍数比3多,3的倍数比5多,以此类推,即前几个进程几乎决定了总的执行时间,所以本方法不可能是高效的。

2.每个进程负责剔除一定范围内的数的倍数:

下面代码所实现的是这一种。

/* *Author:张华键 * *Date:2015/10/30 * *本程序用于并行求素数,给定一个整数n(本程序n <= 2^9*10^3),使用ceil(sqrt(n)/2)个进程进行筛选素数 * *数据区根据进程个数均等划分 * *主程序(0号进程)每找到一个素数k,就将其广播,其他从进程收到k,将本区间内k的倍数筛除 * */
#include <stdio.h>
#define MPICH_SKIP_MPICXX
#include <mpi.h>
#include <stdlib.h>
#include <math.h>
#pragma comment(lib,"mpi.lib")

bool is_prime[512100];
int data[1500]; //起步素数

void Help(char* prog_name); //usage of the program

/* ------------------------------------------------------------------- * 主程序 * ------------------------------------------------------------------- */ 
int main(int argc, char* argv[])

{

    int n,          //问题规模
        mycount,    //单个进程中素数个数
        allcount,   //总的素数个数
        myrank,     //子任务标识
        task_num,   //子任务个数
        partion_size,       //子问题规模
        begin,      //数据区间左端点
        end;        //数据区间右端点

    double  start_time,     //开始时间
            end_time,       //结束时间
            total_time,     //总时间(取所有进程时间中的最大值)
            my_time;        //单个进程的时间

    mycount = 0;

    allcount = 0;

    MPI_Init(&argc,&argv);  //MPI程序初始化

    MPI_Comm_size(MPI_COMM_WORLD,&task_num);    //取子任务个数

    MPI_Comm_rank(MPI_COMM_WORLD,&myrank);      //取子任务标识

    if (argc != 2)      //参数个数为2:文件名以及问题规模n

    {

        Help(argv[0]);

        exit(0);

    }

    if (myrank == 0)

    {

        n=atoi(argv[1]);

    }

    MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);   //主程序将获取的参数n广播

    start_time= MPI_Wtime();    //开始时间

    partion_size = (int)ceil(n*1.0/task_num);   //每个进程划分得到的数据规模

    //左闭右开
    begin = myrank * partion_size;

    end = (myrank+1) * partion_size;

    //保证第一组数据从2开始
    if (begin < 2)

        begin = 2;

    //保证最后一组数据不越界
    if (end > n+1)

        end = n+1;

    int i, k;

    k = 0;

    for (i = begin; i < end; i++)

        is_prime[i] = true;

    if (myrank == 0)    //主程序

    {
        int j;

        fprintf (stdout, "\n正在使用%d个子任务,在查找%d之内的素数.\n",task_num,n);

        for (i = 2; i < partion_size && i*i < n; i++)

        {

            if(is_prime[i])

            {

                for (j = i*i; j < partion_size; j = j+i)

                    is_prime[j] = false;

                data[k] = i;

                //每找到一个素数就广播
                //注意,广播的第3个参数指定了广播发送方,第4个参数指定了群组,群组中其他进程为接收方。
                MPI_Bcast(data+k, 1, MPI_INT, 0, MPI_COMM_WORLD);

                k++;

            }

        }

        data[k] = 0;

        //广播0表示结束
        MPI_Bcast(data+k, 1, MPI_INT, 0, MPI_COMM_WORLD);

    }

    else    //从程序

    {

        int new_begin;

        while (1)

        {

            //接收来自主程序的广播
            MPI_Bcast(data+k, 1, MPI_INT, 0, MPI_COMM_WORLD);

            //接收到0表示结束
            if (data[k] == 0)   break;  

            //本区间中第一个能被data[k]整除的数
            new_begin = begin+(data[k]-begin%data[k])%data[k];

            if (new_begin>end)  break;

            //筛掉data[k]的倍数
            for (i = new_begin; i < end; i += data[k])

                    is_prime[i] = false;

            k++;

        }

    }

    fprintf (stdout, "\nI'm process %d,the primes I found are:\n",myrank);

    for (i = begin; i < end; i++)

        if(is_prime[i])

        {
            mycount++;

            fprintf(stdout, "%d ", i);

        }

    fprintf(stdout, "\n");

    //素数个数归约
    MPI_Reduce(&mycount,&allcount,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD); 

    end_time = MPI_Wtime();

    my_time = end_time - start_time;

    //printf("\nI'm process %d,my running time is %f seconds\n",myrank,my_time);

    //执行时间归约
    MPI_Reduce(&my_time,&total_time,1,MPI_DOUBLE,MPI_MAX,0,MPI_COMM_WORLD); 

    if(myrank==0)

    {

        printf("\nTotal count of prime number:%d\n",allcount);              

        printf("Elapsed time = %f seconds\n",total_time);

        fflush(stdout);

    }

    MPI_Finalize();

    return 0;

}

void Help(char* prog_name) 

{

    fprintf(stderr, "Please input the command as follow: %s <n> \n", prog_name);

    fprintf(stderr, " (n: number of elements)\n");

}

3.流水线实现:

首先,第一个流水线级输入一系列连续的数,然后剔除所有2的倍数,并把余下的数传递给第二级。第二级剔除所有3的倍数,并把余下的数传递给第三级,以此类推。这里,流水线级的个数必须和质数的个数相等(除非用了“块”划分,即每个流水线级处理一组数)。注意,流水线的实现并没有顺序版本中会重复考察已标为质数的数的缺点。

每个进程的形式可如下:

recv(&x, P[i-1]);
for (i = 0; i < n; i++)
{
    recv(&number, P[i-1]);
    if (number == terminator) break;
    if ((number % x) != 0) send(&number, P[i+1]);
}

流水线的实现是相当有效的。

你可能感兴趣的:(素数,并行计算,mpi)