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

MPI并行遗传算法代码np>1运行时出现Segmentation Fault求助

并行遗传算法(N皇后问题)MPI版本段错误排查请求

我正在开发基于MPI的并行遗传算法求解N皇后问题,当进程数np>1时程序出现Segmentation Fault(段错误),未完成执行就退出,附上错误截图,恳请帮忙排查解决。

核心代码

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

#define N 8
#define POPULATION_SIZE 100
#define MUTATION_RATE 0.3
#define MAX_GENERATIONS 50

typedef struct {
    int board[N];
    int fitness;
} Individual;

int calculateFitness(int board[]) {
    int conflicts = 0;
    for (int i = 0; i < N; i++) {
        for (int j = i + 1; j < N; j++) {
            if (board[i] == board[j] || abs(board[i] - board[j]) == j - i) {
                conflicts++;
            }
        }
    }
    return conflicts;
}

void initializePopulation(Individual population[]) {
    for (int i = 0; i < POPULATION_SIZE; i++) {
        for (int j = 0; j < N; j++) {
            population[i].board[j] = rand() % N;
        }
        population[i].fitness = calculateFitness(population[i].board); // Calculate fitness for each individual
    }
}

void selection(Individual population[], Individual matingPool[]) {
    // Tournament selection
    for (int i = 0; i < POPULATION_SIZE; i++) {
        int idx1 = rand() % POPULATION_SIZE;
        int idx2 = rand() % POPULATION_SIZE;
        if (population[idx1].fitness < population[idx2].fitness) {
            matingPool[i] = population[idx1];
        } else {
            matingPool[i] = population[idx2];
        }
    }     
}

void crossover(Individual population[], Individual matingPool[]) {
//  MPI_Barrier(MPI_COMM_WORLD);
    for (int i = 0; i < POPULATION_SIZE; i += 2) {
        int crossoverPoint = rand() % (N - 1) + 1; // Random crossover point
        for (int j = 0; j < crossoverPoint; j++) {
            population[i].board[j] = matingPool[i].board[j];
            population[i + 1].board[j] = matingPool[i + 1].board[j];
        }
        for (int j = crossoverPoint; j < N; j++) {
            population[i].board[j] = matingPool[i + 1].board[j];
            population[i + 1].board[j] = matingPool[i].board[j];
        }
        population[i].fitness = calculateFitness(population[i].board); // Recalculate fitness after crossover
        population[i + 1].fitness = calculateFitness(population[i + 1].board); 
        MPI_Barrier(MPI_COMM_WORLD);
    }
}

void mutate(Individual population[]) {
    for (int i = 0; i < POPULATION_SIZE; i++) {
        if ((double)rand() / RAND_MAX < MUTATION_RATE) {
            int mutationPoint = rand() % N; // Random mutation point
            int newGene = rand() % N; // New random gene
            population[i].board[mutationPoint] = newGene;
            population[i].fitness = calculateFitness(population[i].board); // Recalculate fitness after mutation
        }
    }
}

Individual population[POPULATION_SIZE];

int main(int argc, char *argv[]) {
    MPI_Init(&argc, &argv);

    int rank, size;
//    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
//    MPI_Comm_size(MPI_COMM_WORLD, &size);

//  int totalPopulationSize = 100;  // Replace with the actual total population size
    int numProcesses;
    int remainder;
    
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    
    int chunkSize = POPULATION_SIZE / size;
    remainder = POPULATION_SIZE % size;
    
    if (rank < remainder) {
        chunkSize += 1;
    }
    
    printf("Process %d: Chunk size = %d\n", rank, chunkSize);

    srand(time(NULL) + rank); // Adjust the random seed for each process
    printf("rank : %d\n", rank);

    // Divide the population among processes
//    int chunkSize = POPULATION_SIZE / size;
    Individual population[POPULATION_SIZE];  // Declare the population array
    Individual localPopulation[chunkSize];
    Individual localMatingPool[chunkSize];

    MPI_Datatype individualType;
    int blocklengths[2] = {N, 1};
    MPI_Datatype types[2] = {MPI_INT, MPI_INT};
    MPI_Aint offsets[2];

    offsets[0] = offsetof(Individual, board);
    offsets[1] = offsetof(Individual, fitness);

    MPI_Type_create_struct(2, blocklengths, offsets, types, &individualType);
    MPI_Type_commit(&individualType);
    
    if (rank == 0) {
        initializePopulation(population);
    }

    MPI_Scatter(population, chunkSize, individualType, localPopulation, chunkSize, individualType, 0, MPI_COMM_WORLD);
    
    for (int generation = 1; generation <= MAX_GENERATIONS; generation++) {
        // Perform selection, crossover, and mutation in parallel

        selection(localPopulation, localMatingPool);    
        crossover(localPopulation, localMatingPool);
        mutate(localPopulation);
        
        // Synchronize results within each process
        MPI_Gather(localPopulation, chunkSize, individualType, population, chunkSize, individualType, 0, MPI_COMM_WORLD);

        // Exchange the best individuals among processes
        Individual bestLocalIndividual;
        int bestLocalFitness = INT_MAX;
        for (int i = 0; i < chunkSize; i++) {
            if (localPopulation[i].fitness < bestLocalFitness) {
                bestLocalFitness = localPopulation[i].fitness;
                bestLocalIndividual = localPopulation[i];
            }
        }
        // Reduce the best fitness and individual across all MPI processes
        Individual bestGlobalIndividual;
        int bestGlobalFitness;
        MPI_Reduce(&bestLocalFitness, &bestGlobalFitness, 1, MPI_INT, MPI_MIN, 0, MPI_COMM_WORLD);
        MPI_Gather(&bestLocalIndividual, sizeof(Individual), MPI_BYTE, &bestGlobalIndividual, sizeof(Individual), MPI_BYTE, 0, MPI_COMM_WORLD);

        // Exchange the best individuals between processes
        if (rank == 0) {
        for (int i = 0; i < size; i++) {
            if (i != rank) {
                MPI_Send(&bestGlobalIndividual, 1, individualType, i, 0, MPI_COMM_WORLD);
            }
        }
    } else {
        MPI_Recv(&bestGlobalIndividual, 1, individualType, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    }

    // Synchronize generation number
    MPI_Bcast(&generation, 1, MPI_INT, 0, MPI_COMM_WORLD);

        // Print the current generation and fitness of the best individual
        if (rank == 0) {
            printf("Generation %d, Best Fitness: %d, Solution: ", generation, bestGlobalFitness);
            for (int j = 0; j < N; j++) {
                printf("%d ", bestGlobalIndividual.board[j]);
            }
            printf("\n");

            if (bestGlobalFitness == 0) {
                printf("Solution found in generation %d\n", generation);
                break;
            } else if (generation == MAX_GENERATIONS) {
                printf("Solution exceeds %d generations\n", MAX_GENERATIONS);
            }
        }
         
    }

    MPI_Type_free(&individualType);
    MPI_Finalize();
    return 0;
}

错误截图

MPI段错误截图


问题排查与修复方案

1. 全局变量与局部变量重定义冲突

代码中全局定义了Individual population[POPULATION_SIZE];,main函数内又定义了同名局部变量,导致MPI_Scatter/MPI_Gather时内存寻址混乱,直接触发段错误。
修复:删除全局的population数组定义,仅保留main函数内的局部变量。

2. 种群操作函数的数组越界

selection、crossover、mutate函数硬编码使用POPULATION_SIZE遍历数组,但每个进程实际处理的是chunkSize大小的局部种群(当进程数>1时,chunkSize < POPULATION_SIZE),必然访问超出局部数组的内存。
修复:给这三个函数添加popSize参数,传入实际处理的局部种群大小:

void selection(Individual population[], Individual matingPool[], int popSize) {
    for (int i = 0; i < popSize; i++) {
        int idx1 = rand() % popSize;
        int idx2 = rand() % popSize;
        if (population[idx1].fitness < population[idx2].fitness) {
            matingPool[i] = population[idx1];
        } else {
            matingPool[i] = population[idx2];
        }
    }     
}

void crossover(Individual population[], Individual matingPool[], int popSize) {
    for (int i = 0; i < popSize - (popSize % 2); i += 2) {
        int crossoverPoint = rand() % (N - 1) + 1;
        for (int j = 0; j < crossoverPoint; j++) {
            population[i].board[j] = matingPool[i].board[j];
            population[i + 1].board[j] = matingPool[i + 1].board[j];
        }
        for (int j = crossoverPoint; j < N; j++) {
            population[i].board[j] = matingPool[i + 1].board[j];
            population[i + 1].board[j] = matingPool[i].board[j];
        }
        population[i].fitness = calculateFitness(population[i].board);
        population[i + 1].fitness = calculateFitness(population[i + 1].board);
    }
    // 处理种群数量为奇数的情况,保留最后一个个体不变
    if (popSize % 2 != 0) {
        population[popSize-1] = matingPool[popSize-1];
    }
}

void mutate(Individual population[], int popSize) {
    for (int i = 0; i < popSize; i++) {
        if ((double)rand() / RAND_MAX < MUTATION_RATE) {
            int mutationPoint = rand() % N;
            int newGene = rand() % N;
            population[i].board[mutationPoint] = newGene;
            population[i].fitness = calculateFitness(population[i].board);
        }
    }
}

main函数中调用时传入chunkSize:

selection(localPopulation, localMatingPool, chunkSize);    
crossover(localPopulation, localMatingPool, chunkSize);
mutate(localPopulation, chunkSize);

3. MPI_Gather收集最优个体的内存错误

原代码用MPI_Gather将所有进程的bestLocalIndividual收集到单个bestGlobalIndividual结构体中,内存空间不足导致越界。
修复:定义数组存储所有进程的最优个体,再从中筛选全局最优:

// 收集所有进程的最优个体
Individual bestGlobalIndividuals[size];
MPI_Gather(&bestLocalIndividual, 1, individualType, bestGlobalIndividuals, 1, individualType, 0, MPI_COMM_WORLD);

// 进程0筛选全局最优
Individual bestGlobalIndividual;
int bestGlobalFitness;
if (rank == 0) {
    bestGlobalFitness = INT_MAX;
    for (int i = 0; i < size; i++) {
        if (bestGlobalIndividuals[i].fitness < bestGlobalFitness) {
            bestGlobalFitness = bestGlobalIndividuals[i].fitness;
            bestGlobalIndividual = bestGlobalIndividuals[i];
        }
    }
}
// 广播全局最优个体给所有进程
MPI_Bcast(&bestGlobalFitness, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&bestGlobalIndividual, 1, individualType, 0, MPI_COMM_WORLD);

4. 不必要的MPI_Barrier与冗余MPI_Bcast

  • crossover函数内的MPI_Barrier放在循环中,会导致进程死锁,直接删除。
  • 原代码中MPI_Bcast(&generation, 1, MPI_INT, 0, MPI_COMM_WORLD)属于冗余操作,每个进程的generation是循环变量,无需同步,直接删除。

5. MPI_Scatter/MPI_Gather的参数一致性

当POPULATION_SIZE不能被进程数整除时,不同进程的chunkSize不同,原代码的MPI_Scatter/MPI_Gather使用统一的chunkSize参数会出错。
修复:使用MPI_Scatterv和MPI_Gatherv替代,根据进程rank分配不同的发送/接收计数:

// 准备Scatterv的参数
int sendcounts[size];
int displs[size];
int offset = 0;
for (int i = 0; i < size; i++) {
    sendcounts[i] = (i < remainder) ? (POPULATION_SIZE / size + 1) : (POPULATION_SIZE / size);
    displs[i] = offset;
    offset += sendcounts[i];
}

// 分发种群
MPI_Scatterv(population, sendcounts, displs, individualType, localPopulation, chunkSize, individualType, 0, MPI_COMM_WORLD);

// 收集种群
MPI_Gatherv(localPopulation, chunkSize, individualType, population, sendcounts, displs, individualType, 0, MPI_COMM_WORLD);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:09:51