使用MPI计算N皇后问题。
要求:
N>8,进(线)程数P取等于N,小于N,大于N等不同情况。
现有的一些实现中存在部分问题;
八皇后问题mpi求解方案 该文章中无法实现进程数和N不相等情况,而且在输出文件过程中没有进行进程同步,导致输出结果不全或者乱序。
高性能计算:使用MPI多进程并行求解N皇后,并按进程顺序输出摆放方案该文章进行了部分改进,可以按照顺序输出N皇后的排列方案,但是仍然无法实现N不等于P的情况。
本文所做的工作
对上述两个blog的代码进行改进,实现了计算过程中支持进(线)程数P取等于N,小于N,大于N等不同情况。并且可以按照进程号的顺序输出排列结果。
主要是为每个进程划分任务,当进程数大于N时,有部分进程不参与N皇后排列。当进程数小于N时,每个进程对应着在第一行棋盘放多个皇后的情况(原来是每个进程只管在第一行棋盘放一个的情况)。
代码实现
n_queue.cpp
//#include "stdafx.h"
#include <chrono>
#include <cstring>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <cmath>
#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;
#define N 8
double start_time,end_time;
void outPut(const int & size,int * array,ostringstream & file)//输出到文件
{
//MPI_File_write_at()
//MPI_File_write_ordered(mpi_file,&endl, 1, MPI_CHAR, MPI_STATUS_IGNORE);
for(int i = 0 ; i < size ; i++)
{
file<<array[i]<<" ";
}
file<<endl;
}
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 &start,int &end,int myId)//mpi开启递归调用
{
//ofstream file;
int count = 0 ;//计数为
int totalCount = 0;//总计数为
ostringstream file;
if(start>=N)goto need_not_compute;
//file.open("temp.txt",std::ios::out | std::ios::app);//输出的文件,每个进程都以追加的方式存放数据
flags = new int[N];//两个数据结构,flags用来存储位置是否被占用信息
array = new int[N];//存储第i行的棋子放的位置
memset(flags,0,sizeof(int)*N);//赋初值
memset(array,-1,sizeof(int)*N);
for(int i = start;i<end;++i)
{
flags[i] = 1;//当第一行放i的情况
array[0] = i;
range(N,1,flags,array,count,file);//开始递归
memset(flags,0,sizeof(int)*N);//清零
memset(array,-1,sizeof(int)*N);
}
//file.close();
need_not_compute:
MPI_Reduce(&count,&totalCount,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);//规约所有递归结构
end_time = MPI_Wtime();
MPI_Status status;
int deley = 0;
if (myId == 0)
{
cout<<"cout: "<<totalCount<<endl;
cout<<"using time: "<<end_time-start_time<<endl;
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;
//ofstream file;
MPI_Init(&argc,&argv);//初始化mpi
MPI_Comm_size(MPI_COMM_WORLD,&size);//获取开启的进程数量
MPI_Comm_rank(MPI_COMM_WORLD,&myId);//获取当前进程的id号
int *flags;//标记
int *array;//存放棋盘次序
//file.open("temp.txt");//用来清空文件--采用一次打开然后关闭文件,仅仅是在计算前,清空文件中原有的内容,后面文件打开均采用追加的方式
//file.close();
//划分每个进程的任务
int block = ceil(N*1.0/size);
int start = myId*block;
int end = myId==size-1?N:(myId+1)*block;
//防止end超出界限
end = end>N?N:end;
start_time=MPI_Wtime();
mpi_range(size,flags,array,start,end,myId);//开始递归计算
MPI_Finalize();//计算终止
return 0;
}
makefile
SRC=n_queue.cpp
OBJ=n_queue
CC=mpicxx
#CFLAGS
$(OBJ):$(SRC)
$(CC) $(SRC) -o $(OBJ) -lm
run:
mpirun -np 10 ./$(OBJ)
.PHONY : clean
clean:
rm $(OBJ)