MPI并行实现勒让德多项式根求解的问题求助
勒让德多项式根求解MPI并行化问题修复
核心问题拆解
MPI_Gather参数逻辑错误
- 你给
MPI_Gather传递的是数组总分配长度,但每个进程实际找到的根数量远小于该值,未使用的数组位置(初始为内存随机值)被收集到主进程,导致出现多余无效值。 - 主进程在
MPI_Gather后未使用收集到的halfroot数组,反而遍历本地halfrootp,完全忽略其他进程的计算结果。
- 你给
根数量跟踪缺失
- 没有用变量记录每个进程实际找到的根数量,依赖
halfrootp[a] != 0.0筛选不可靠——未赋值的数组位置不是确定的0,而是内存随机值。
- 没有用变量记录每个进程实际找到的根数量,依赖
进程执行逻辑混乱
- 所有进程都执行根整理、打印操作,这些逻辑仅应由主进程(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
相关产品推荐
相关产品推荐

