You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MPI并行实现勒让德多项式根求解的问题求助

勒让德多项式根求解MPI并行化问题修复

核心问题拆解

  1. MPI_Gather参数逻辑错误

    • 你给MPI_Gather传递的是数组总分配长度,但每个进程实际找到的根数量远小于该值,未使用的数组位置(初始为内存随机值)被收集到主进程,导致出现多余无效值。
    • 主进程在MPI_Gather后未使用收集到的halfroot数组,反而遍历本地halfrootp,完全忽略其他进程的计算结果。
  2. 根数量跟踪缺失

    • 没有用变量记录每个进程实际找到的根数量,依赖halfrootp[a] != 0.0筛选不可靠——未赋值的数组位置不是确定的0,而是内存随机值。
  3. 进程执行逻辑混乱

    • 所有进程都执行根整理、打印操作,这些逻辑仅应由主进程(rank=0)完成,其他进程完成计算后即可结束,否则会出现重复输出或内存错误。

修复后的完整代码

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

// 勒让德多项式递归实现
double p(int n, double x){
    if(n == 0){
        return 1.;
    } else if(n == 1){
        return x;
    }else{
        return ((2.*(n-1.)+1)*x*p(n-1.,x)-(n-1.)*p(n-2.,x))/(n);
    }
}

int main(int argc, char **argv)
{
    int n = atoi(argv[1]);

    if(n>50 || n<=0){
        printf("Degree must be between 1 and 50\n");
        return 1;
    }

    MPI_Init(&argc, &argv);
    int size, rank;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    // 划分遍历区间:每个进程处理的v范围
    int total_points = 1000000;
    int local_start = rank * (total_points / size);
    int local_end = (rank + 1) * (total_points / size);
    // 处理total_points不能被size整除的情况,避免遗漏点
    if(rank == size-1){
        local_end = total_points;
    }

    // 每个进程最多可能找到的正根数量:奇数n时为(n+1)/2,偶数为n/2
    int max_local_roots = (n % 2 == 1) ? ((n+1)/2) : (n/2);
    double *local_roots = (double*)malloc(max_local_roots * sizeof(double));
    int local_count = 0;

    // 暴力查找正根
    for(int v = local_start; v < local_end; v++) {
        double x = v / 1000000.0;
        // 检查区间[x, x+0.000001]是否有根(符号变化)
        if(p(n, x) * p(n, x + 0.000001) < 0) {
            if(local_count < max_local_roots){ // 防止数组越界
                local_roots[local_count] = x;
                local_count++;
            }
        }
    }

    // 主进程收集所有进程的根数量,再收集实际根数据
    int *global_counts = NULL;
    double *global_half_roots = NULL;
    int *displs = NULL;

    if(rank == 0){
        global_counts = (int*)malloc(size * sizeof(int));
        int total_half_roots = (n % 2 ==1) ? ((n+1)/2) : (n/2);
        global_half_roots = (double*)malloc(total_half_roots * sizeof(double));
        displs = (int*)malloc(size * sizeof(int));
    }

    // 第一步:收集每个进程找到的根数量
    MPI_Gather(&local_count, 1, MPI_INT, global_counts, 1, MPI_INT, 0, MPI_COMM_WORLD);

    // 第二步:计算每个进程数据在全局数组中的偏移量(仅主进程)
    if(rank ==0){
        displs[0] =0;
        for(int i=1; i<size; i++){
            displs[i] = displs[i-1] + global_counts[i-1];
        }
    }

    // 第三步:用MPI_Gatherv收集可变长度的根数据
    MPI_Gatherv(local_roots, local_count, MPI_DOUBLE,
                global_half_roots, global_counts, displs, MPI_DOUBLE,
                0, MPI_COMM_WORLD);

    // 主进程整理并输出所有根
    if(rank ==0){
        double *roots = (double*)malloc(n * sizeof(double));
        int idx =0;

        // 填充正根和负根
        for(int i=0; i<(n%2==1 ? (n+1)/2 : n/2); i++){
            roots[idx++] = global_half_roots[i];
            roots[idx++] = -global_half_roots[i];
        }

        // 奇数n时添加0根
        if(n%2 ==1){
            roots[idx] =0.0;
        }

        // 打印结果
        printf("All roots of Legendre polynomial degree %d:\n",n);
        for(int i=0; i<n; i++){
            printf("root[%d] = %.6lf\n",i, roots[i]);
        }

        // 释放主进程分配的内存
        free(global_counts);
        free(global_half_roots);
        free(displs);
        free(roots);
    }

    // 释放本地内存
    free(local_roots);

    MPI_Finalize();
    return 0;
}

关键修改说明

  • 用MPI_Gatherv替代MPI_Gather:支持可变长度数据收集,避免传递多余无效值,适配不同进程找到的根数量差异。
  • 跟踪实际根数量:每个进程用local_count记录找到的根数量,主进程先收集所有local_count,再计算偏移量完成准确收集。
  • 主进程专属逻辑:仅rank=0的进程处理结果整理和打印,其他进程完成计算后直接释放内存并结束。
  • 数组越界防护:添加根到local_roots时检查local_count是否小于最大容量,避免内存越界。

内容的提问来源于stack exchange,提问作者user21702987

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:23:09