實驗環境 linux 這里我使用了超算習堂的雲主機https://easyhpc.net/personal-computer
0. N皇后問題並行算法說明:
在N×N格的國際象棋上擺放N個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法?
要求:要求N>8,進(線)程數P取等於N,小於N,大於N等不同情況。所有進(線)程依次將滿足條件的排法個數輸出。進(線)程0先輸出,進(線)程1再輸出,進(線)程P-1最后輸出。
- 更新軟件包列表
sudo apt-get update - 配置基礎編譯環境
sudo apt-get install -y build-essential
下載與安裝完成后,可鍵入下面的命令觀察gcc版本信息。
gcc -v

- MPICH的安裝
sudo apt-get install -y mpich
mpicc -v

- 環境搭建完成,編寫代碼如下:
N皇后問題的並行算法求解,算法思路參考了> 八皇后問題mpi求解方案> 建議對Nqueen不了解的同學去看看這篇。
,為了滿足多個進程順序輸出其搜索到的方案的要求,其前面的連接基礎上,我的代碼加入了阻塞通訊,構造了一個ostringstram oss保存單個進程搜索到的方案數, 可以使得多個進程之間進行通訊。進程1將oss輸出后,向下一進程send消息,下一進程會堵塞,知道接收到上一進程的消息后(也就是上一進程完成了輸出),輸出自己的方案並且自己發送消息到自己的下一個進程。
- 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;
}
- 編譯
mpixx ./Nqueen.cpp -o ./Nqueen - 運行
mpirun -np 8 ./Nqueen - 運行結果

