八皇后问题mpi求解方案:
问题描述:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 实际上一共有92种解法。
算法描述:
数据结构采用下面的方式:
上图这种棋盘布局采用下面的数组来描述:
这将表示第0行第0列有棋子,第1行第4列有棋子,第2行第7列有棋子…..这样,用一个一维数组完全可以标示整个棋盘。
要注意的是,由于每一行每一列都不重复,即数组中存放的必是一个由0-7组成的全排列。采用一维数组的全排列,解决了同一行无重复棋子(一维数组来保证,因为数组每个下表只对应一个元素),同一列无重复棋子(全排列来保证,因为全排列每个元素在数组中只出现一次),这样先将八皇后问题转化成了求解一维数组全排列问题。
然后,要解决对角线上无冲突,如下图所示:
当要判断第七行,即数组下标是6的行元素与其对角线上的棋子是否有冲突,仅仅需要判断其试探棋子向前做加(或减)操作,获得的值是否与相应位置相同即可。当6中放的是3,向前加1为4,向前减1为2,而5中存放的是6,不等于4也不等于2,所有不冲突。现在已知2中存放的是7,当6中存放的3向前+1(5中元素比较)+1(4中元素比较)+1(3中元素比较)+1(2中元素比较)后刚好得7,明显与2中存放的数据冲突,所有6中不能存放3。通过上述的向前加一(或减一)的方式可以判断试探元素是否与其他以确定的元素在对角线上有冲突。
通过上面的描述,我们结合讨论的两种算法,最终获得问题的解。过程是这样的,先写一个求一维数组全排列的程序,这个程序是使用递归的试探的方式完成的,在试探的过程中,我们加入对对角线的判断,完成计算。
使用的flag数组:
注意,在计算全排列的时候,增加一个flags数组,用来标记当前第n个元素是否被占用。上图所示,标示第一列元素未被使用,其他的元素都被使用过了。
如果您关于vs2008下如何使用mpi不熟悉,可以参考我前一篇文章,这里不再累述。
程序源码如下:
// eightqueens.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
#include<fstream>
#ifndef MPICH_SKIP_MPICXX
#define MPICH_SKIP_MPICXX
#endif
#include <mpi.h>
using std::cout;
using std::endl;
using std::cerr;
using std::ofstream;
using std::ifstream;
#define N 8
void outPut(const int & size,int * array,ofstream & file)//输出到文件
{
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, ofstream &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开启递归调用
{
ofstream file;
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);
flags[myId] = 1;//第i个进程执行第一行为i的计算
array[0] = myId;
int count = 0 ;//计数为
int totalCount = 0;//总计数为
range(N,1,flags,array,count,file);//开始递归
MPI_Reduce(&count,&totalCount,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);//规约所有递归结构
if(myId == 0)//主进程输出计算结果
cerr<<totalCount<<endl;
file.close();
}
int _tmain(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();
mpi_range(N,flags,array,myId);//开始递归计算
MPI_Finalize();//计算终止
return 0;
}
使用控制台执行如下图:
注意-n 后的参数是8,实际您可以写更大的数字,那就不是八皇后问题了(可能是九皇后或更多皇后问题), 注意n皇后问题是个np难问题,当皇后的数量增多,问题的复杂度会以n!的方式递增。
另外通过对称性,可以缩减计算次数,请读者执行思考。
问题的答案是92,输出文件temp.txt中的内容为:
6 0 2 7 5 3 1 4
0 5 7 2 6 3 1 4
0 6 3 5 7 1 4 2
0 6 4 7 1 3 5 2
7 1 4 2 0 6 3 5
7 2 0 5 1 4 6 3
7 3 0 2 5 1 6 4
3 0 4 7 5 2 6 1
3 1 4 7 5 0 2 6
3 1 6 2 5 7 0 4
3 1 6 2 5 7 4 0
3 1 6 4 0 7 5 2
3 1 7 4 6 0 2 5
3 1 7 5 0 2 4 6
3 5 0 4 1 7 2 6
3 5 7 1 6 0 2 4
3 5 7 2 0 6 4 1
3 6 0 7 4 1 5 2
3 6 2 7 1 4 0 5
3 6 4 1 5 0 2 7
3 6 4 2 0 5 7 1
3 7 0 2 5 1 6 4
3 7 0 4 6 1 5 2
3 7 4 2 0 6 1 5
5 1 6 0 2 4 7 3
5 1 6 0 3 7 4 2
5 2 0 6 4 7 1 3
5 2 0 7 3 1 6 4
5 2 0 7 4 1 3 6
5 2 4 6 0 3 1 7
5 2 4 7 0 3 1 6
5 2 6 1 3 7 0 4
5 2 6 1 7 4 0 3
5 2 6 3 0 7 1 4
5 3 0 4 7 1 6 2
5 3 1 7 4 6 0 2
5 3 6 0 2 4 1 7
5 3 6 0 7 1 4 2
5 7 1 3 0 6 4 2
4 0 7 3 1 6 2 5
4 0 7 5 2 6 1 3
4 1 3 5 7 2 0 6
4 1 3 6 2 7 5 0
4 1 5 0 6 3 7 2
4 1 7 0 3 6 2 5
4 2 0 5 7 1 3 6
4 2 0 6 1 7 5 3
4 2 7 3 6 0 5 1
4 6 0 2 7 5 3 1
4 6 0 3 1 7 5 2
4 6 1 3 7 0 2 5
4 6 1 5 2 0 3 7
4 6 1 5 2 0 7 3
4 6 3 0 2 7 5 1
4 7 3 0 2 5 1 6
4 7 3 0 6 1 5 2
1 4 6 0 2 7 5 3
1 4 6 3 0 7 5 2
1 5 0 6 3 7 2 4
1 5 7 2 0 3 6 4
1 6 2 5 7 4 0 3
1 6 4 7 0 3 5 2
1 7 5 0 2 4 6 3
2 4 1 7 0 6 3 5
2 4 1 7 5 3 6 0
2 4 6 0 3 1 7 5
2 4 7 3 0 6 1 5
2 5 1 4 7 0 6 3
2 5 1 6 0 3 7 4
2 5 1 6 4 0 7 3
2 5 3 0 7 4 6 1
2 5 3 1 7 4 6 0
2 5 7 0 3 6 4 1
2 5 7 0 4 6 1 3
2 5 7 1 3 0 6 4
2 6 1 7 4 0 3 5
2 6 1 7 5 3 0 4
2 7 3 6 0 5 1 4
6 1 3 0 7 4 2 5
6 1 5 2 0 3 7 4
6 2 0 5 7 4 1 3
6 2 7 1 4 0 5 3
6 3 1 4 7 0 2 5
6 3 1 7 5 0 2 4
6 4 2 0 5 7 1 3
相信您,看完本文会将这种种情况还原回来。