MPI实现求解10的八次方内素数的个数(版本二)

本文介绍了一种使用MPI并行计算素数个数的方法,通过优化任务分配和计算流程,跳过偶数以提高效率。实验结果显示,相较于原始版本,新版本在计算10的八次方和九次方范围内的素数时,执行时间显著缩短。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用MPI计算素数个数(只计算奇数部分)

在上一版本中实现了基本的功能,但是还有不是很完美。
这一次要再上一版的基础中,不计算偶数,这样在理论上可以提高一倍的效率。
在串行的程序中实现这样的想法是一个很简单的功能,但是现在是一个并行计算的程序,在任务的分配和计算上会有一些小麻烦。

任务的分配:

	low_index = id * ((n - 1) / 2) / p;
    high_index = (id+1) * ((n-1) / 2) / p; 
    low_value = low_index * 2 + 3;
    high_value = high_index * 2 + 1;
    size = (high_value - low_value) / 2 + 1;

采用的方法是先进行分配索引,然后再让每一进程计算自己所需要计算的最小值和最大值。

first的计算:

		if (prime * prime > low_value)
            first = (prime * prime - low_value)/2;
        else {
            if (!(low_value % prime)) first = 0;
            else if(low_value % prime % 2 ==0)
                first = prime - ((low_value % prime)/2);       // 此处在求局部first(数组中第一个可以被prime整除的数)的时候非常巧妙
            else
                first = (prime - (low_value % prime))/2;

关于改造first的计算用了比较长的时间,这里涉及到了一点的小技巧。

  • 当四个进程来分配计算100内的素数时:
0:3-25
0:count 9
1:27-49
1:count 6
2:51-73
2:count 6
3:75-99
3:count 4
The total number of prime: 25, total time:   0.000061, total node 4

first求法描述
如上图,在寻找第一行能被5整除的元素时,距离27最近的是25,在上面由黑色笔写。
这个时候通过下面语句可以计算出结果:

first = prime - ((low_value % prime)/2);

但是对于第二行在寻找可以被7整除的元素是,发现计算出来的索引值是错误的。通过分析发现,距离75最近可以被7整除的数数时70(但是70并没有在计算遍历范围内),通过上式计算出来的结果也不是正确的。
这时使用:

first = (prime - (low_value % prime))/2;

可以计算出正确的索引值first。
(为了找到合适的方法计算出first的值,在这里折腾了好长时间。。。)

版本二(跳过偶数)

#include "mpi.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define MIN(a, b)  ((a)<(b)?(a):(b))

int main(int argc, char *argv[]) {
    unsigned long int count;        /* Local prime count */
    double elapsed_time; /* Parallel execution time */
    unsigned long int first;        /* Index of first multiple */
    unsigned long int global_count = 0; /* Global prime count */
    unsigned long long int high_value;   /* Highest value on this proc */
    unsigned long int i;
    int id;           /* Process ID number */
    unsigned long int index;        /* Index of current prime */
    unsigned long long int low_value;    /* Lowest value on this proc */
    char *marked;       /* Portion of 2,...,'n' */
    unsigned long long int n;            /* Sieving from 2, ..., 'n' */
    int p;            /* Number of processes */
    unsigned long int proc0_size;   /* Size of proc 0's subarray */
    unsigned long int prime;        /* Current prime */
    unsigned long int size;         /* Elements in 'marked' */
    unsigned long int low_index;    // 低位对应的全局索引值
    unsigned long int high_index;   // 高位对应的全局索引值
    MPI_Init(&argc, &argv);

    /* Start the timer */

    MPI_Comm_rank(MPI_COMM_WORLD, &id); //获取进程id
    MPI_Comm_size(MPI_COMM_WORLD, &p);  //获取进程数量
    MPI_Barrier(MPI_COMM_WORLD);        //进行同步
    elapsed_time = -MPI_Wtime();

    // if (argc != 2) {
    //     if (!id) printf("Command line: %s <m>\n", argv[0]);
    //     MPI_Finalize();
    //     exit(1);
    // }

    // n = atoll(argv[1]);      //获取要计算的数
    n = 1000;      //获取要计算的数

    /* Figure out this process's share of the array, as
       well as the integers represented by the first and
       last array elements 计算这个进程在数组中的份额,以及第一个和最后一个数组元素表示的整数*/
    /*********originalSoution*******/
    // low_value = 2 + id * (n - 1) / p;
    // high_value = 1 + (id + 1) * (n - 1) / p;
    // size = high_value - low_value + 1;
    /********solution1**********/
    if (n % 2 == 0)      // 如果给出的是一个偶数,将给出的数减一变为奇数。
        n = n -1;
    // low_value = 3 + id * (n - 1) / p;
    // high_value = 1 + (id + 1) * (n - 1) / p;
    low_index = id * ((n - 1) / 2) / p;
    high_index = (id+1) * ((n-1) / 2) / p; 
    low_value = low_index * 2 + 3;
    high_value = high_index * 2 + 1;
    
       
    
    printf("%d:%d-%d\n",id,low_value,high_value);
    size = (high_value - low_value) / 2 + 1;

    /* Bail out if all the primes used for sieving are
       not all held by process 0 如果用于筛选的所有质数不都由进程0持有,则退出*/

    proc0_size = (n - 1) / p;

    if ((2 + proc0_size) < (int) sqrt((double) n)) {
        if (!id) printf("Too many processes\n");
        MPI_Finalize();
        exit(1);
    }

    /* Allocate this process's share of the array.分配此进程在数组中的份额 */

    marked = (char *) malloc(size);

    if (marked == NULL) {
        printf("Cannot allocate enough memory\n");
        MPI_Finalize();
        exit(1);
    }

    for (i = 0; i < size; i++) marked[i] = 0;
    if (!id) index = 0;         // 不明白为什么要一直判断!id----->只有0号进程才会执行。
    prime = 3;
    do {
        if (prime * prime > low_value)
            first = (prime * prime - low_value)/2;
        else {
            if (!(low_value % prime)) first = 0;
            else if(low_value % prime % 2 ==0)
                first = prime - ((low_value % prime)/2);       // 此处在求局部first(数组中第一个可以被prime整除的数)的时候非常巧妙
            else
                first = (prime - (low_value % prime))/2;
        }
        // if (!id)
        //     printf("---%d",first);
        for (i = first; i < size; i += prime) 
            marked[i] = 1;
        if (!id) {
            while (marked[++index]);
            // prime = index + 2;
            prime = index * 2 + 3;	// 要重新写出一个prime的计算表达式
        }
        if (p > 1) MPI_Bcast(&prime, 1, MPI_INT, 0, MPI_COMM_WORLD);    //广播,将一个进程中的数据发送到所有进程
    } while (prime * prime <= n);
    count = 0;
    for (i = 0; i < size; i++)
        if (!marked[i]) 
        {
        count++;    // 统计单个进程中素数的个数,看有多少个0
        }
    if(!id) count++;
    printf("%d:count %d\n",id,count);
    if (p > 1)
        MPI_Reduce(&count, &global_count, 1, MPI_INT, MPI_SUM,
                   0, MPI_COMM_WORLD);  // 规约,集合通信,由进程0来计算全局的count

    /* Stop the timer */

    elapsed_time += MPI_Wtime();


    /* Print the results */

    if (!id) {
        printf("The total number of prime: %ld, total time: %10.6f, total node %d\n", global_count, elapsed_time, p);
    }
    MPI_Finalize();
    return 0;

}

输出:(四个核心计算10的八次方)

>>>
0:3-24999999
1:25000001-49999999
2:50000001-74999999
3:75000001-99999999
1:count 1435207
0:count 1565926
2:count 1393170
3:count 1367151
The total number of prime: 5761455, total time:   0.881289, total node 4

版本一的计算要花费2.1秒,而版本二只要0.8812秒,效果显著。

再看看计算10的九次方(16个核心):

>>>
0:3-62499999
1:62500001-124999999
2:125000001-187499999
3:187500001-249999999
4:250000001-312499999
5:312500001-374999999
6:375000001-437499999
8:500000001-562499999
9:562500001-624999999
10:625000001-687499999
12:750000001-812499999
13:812500001-874999999
7:437500001-499999999
11:687500001-749999999
14:875000001-937499999
15:937500001-999999999
2:count 3314265
0:count 3701487
3:count 3254911
1:count 3408655
5:count 3179482
4:count 3212954
7:count 3130885
6:count 3153228
8:count 3110499
10:count 3078479
11:count 3064367
9:count 3093969
15:count 3020834
12:count 3052785
14:count 3029831
13:count 3040903
The total number of prime: 50847534, total time:   4.620501, total node 16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值