高性能计算:使用MPI多进程并行求解N皇后,并按进程顺序输出摆放方案

实验环境 linux 这里我使用了超算习堂的云主机https://easyhpc.net/personal-computer
0. N皇后问题并行算法说明:
在N×N格的国际象棋上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法?
要求:要求N>8,进(线)程数P取等于N,小于N,大于N等不同情况。所有进(线)程依次将满足条件的排法个数输出。进(线)程0先输出,进(线)程1再输出,进(线)程P-1最后输出。

  1. 更新软件包列表
    sudo apt-get update
  2. 配置基础编译环境
    sudo apt-get install -y build-essential
    下载与安装完成后,可键入下面的命令观察gcc版本信息。
    gcc -v
  3. MPICH的安装
    sudo apt-get install -y mpich
    mpicc -v
    安装完毕验证
  4. 环境搭建完成,编写代码如下:
    N皇后问题的并行算法求解,算法思路参考了> 八皇后问题mpi求解方案> 建议对Nqueen不了解的同学去看看这篇。

,为了满足多个进程顺序输出其搜索到的方案的要求,其前面的连接基础上,我的代码加入了阻塞通讯,构造了一个ostringstram oss保存单个进程搜索到的方案数, 可以使得多个进程之间进行通讯。进程1将oss输出后,向下一进程send消息,下一进程会堵塞,知道接收到上一进程的消息后(也就是上一进程完成了输出),输出自己的方案并且自己发送消息到自己的下一个进程。

  1. C++实现
点击查看代码
#include <chrono>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#ifndef MPICH_SKIP_MPICXX
#define MPICH_SKIP_MPICXX
#endif
#include <mpi.h>

using std::cout;

using std::endl;

using std::clog;

using std::ofstream;

using std::ifstream;
using std::ostringstream;
using std::string;

int N = 8;
double start, end;

void outPut(const int &size, int *array, ostringstream &file) //输出到文件
{
     int myid;
     MPI_Comm_rank(MPI_COMM_WORLD, &myid);
     file << "myId: " << myid << ":";
     for (int i = 0; i < size; i++)
     {
          file << array[i];
     }
     file << "\n";
}

bool isContact(const int &deep, const int *const &array) //判断对角线上是否有冲突
{
     int temp;
     for (int i = 1; i < deep + 1; i++)
     {
          temp = array[deep - i];
          if (array[deep] - i == temp || array[deep] + i == temp) //这条语句完成判断
               return true;
     }
     return false;
}

void range(const int &size, const int &deep, int *const &flags, int *&array, int &count, ostringstream &file) //进行递归推导
{
     for (int i = 0; i < size; i++) //从第到第个,判断是否还有没用过并且没有冲突的数据
     {
          if (!flags[i]) //判断是否被用过,这里使用的是按内容寻址的方式
          {
               array[deep] = i; //如果i没有被使用过,那么现在使用i
               if (deep != 0)   //不是第一行的元素要判断对角线上是否有冲突
               {
                    if (isContact(deep, array)) //判断对角线是否有冲突,主要deep是层次
                         continue;              //如果有冲突,继续循环,寻找下一个试探点
               }
               flags[i] = 1;         //目前第i个点可用,这里进行标记,第i个点已经被占用了
               if (deep == size - 1) //当深度为,就是找到了一个序列,满足八皇后要求了
               {
                    outPut(size, array, file); //将结果输出到文件
                    count++;                   //次数加一
               }
               else //没有找全所有的棋子
               {
                    range(size, deep + 1, flags, array, count, file); //进一步递归调用,完成没完成的棋子
                    array[deep] = -1;                                 //递归回来,要恢复原状,以备下次继续递归到新的序列
               }
               flags[i] = 0; //恢复标志
          }
     }
}

void mpi_range(const int &size, int *&flags, int *&array, const int &myId) //mpi开启递归调用
{
     flags = new int[N];                //两个数据结构,flags用来存储位置是否被占用信息
     array = new int[N];                //存储第i行的棋子放的位置
     memset(flags, 0, sizeof(int) * N); //赋初值
     memset(array, -1, sizeof(int) * N);
     flags[myId] = 1; //第i个进程执行第一行为i的计算
     array[0] = myId;
     int count = 0;      //计数为
     int totalCount = 0; //总计数为
     ostringstream file;
     range(N, 1, flags, array, count, file);                                  //开始递归
     MPI_Reduce(&count, &totalCount, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); //规约所有递归结构

     if (myId == 0) //主进程输出计算结果
     {
          MPI_Recv(&deley, 1, MPI_INT, size - 1, 0, MPI_COMM_WORLD, &status2);
          end = MPI_Wtime();
          cout << "totalCount: " << totalCount << endl;
          cout << "Runtime =:" << end - start << endl;
     }
     MPI_Status status;
     int deley = 0;
     if (myId == 0)
     {
          cout << file.str();
          // cout<<myId;
          MPI_Send(&deley, 1, MPI_INT, myId + 1, 0, MPI_COMM_WORLD);
     }
     if (myId >= 1)
     {
          MPI_Recv(&deley, 1, MPI_INT, myId - 1, 0, MPI_COMM_WORLD, &status);
          cout << file.str();
          //  cout<<myId;
          if (myId + 1 <= size)
          {
               MPI_Send(&deley, 1, MPI_INT, (myId + 1) % size, 0, MPI_COMM_WORLD);
          }
     }
}

int main(int argc, char *argv[])
{
     int size;
     int myId;
     MPI_Init(&argc, &argv); //初始化mpi
     start = MPI_Wtime();
     MPI_Comm_size(MPI_COMM_WORLD, &size); //获取开启的进程数量
     N = size;
     MPI_Comm_rank(MPI_COMM_WORLD, &myId); //获取当前进程的id号
     int *flags;                           //标记
     int *array;                           //存放棋盘次序
     mpi_range(N, flags, array, myId);     //开始递归计算
     MPI_Finalize();                       //计算终止
     return 0;
}

  1. 编译mpixx ./Nqueen.cpp -o ./Nqueen
  2. 运行mpirun -np 8 ./Nqueen
  3. 运行结果 运行结果
posted @ 2022-02-12 21:03  Llon_Cheng  阅读(1106)  评论(4编辑  收藏  举报