[AI](加入随机因素)局部搜索解决N皇后问题(含源码)

来源:互联网 发布:js判断radio是否被选中 编辑:程序博客网 时间:2024/04/29 21:56

局部搜索算法(加入随机因素):主要思想是要想得到全局最优解,总沿着局部最优的方向寻找,但会存在局部最优陷阱,即到达一个极值点(非最值点),在其邻域内无法找到更优解,所以需要多次尝试。

a)   变量:

             i.     N:皇后个数;

            ii.     皇后位置数组Pos[N]:下标为行号,对应值为列号;

           iii.     冲突表CollTable[2N][2]:分别记录正负对角线上的冲突数,对于正负对角线标号如下图所示,其中正对角线如图中蓝线所示,采取逆时针方向标号;负对角线如图中绿线所示,采取顺时针方向标号。


b)   邻域的定义:

            随机选择两个皇后进行列交换得到的新序列。

c)   算法描述(只介绍N>3情况下,此时一定有解):

i. 初始化迭代次数IterCnt=0,初始化马尔科夫链长为N*N,随机生成皇后位置Pos[],每个皇后位于不同行不同列,记录冲突表CollTable,计算其冲突数collcnt;
ii. IterCnt++,判断冲突数是否为0,如果是算法结束,输出结果;否则,转iii;
iii. 判断迭代次数IterCnt是否等于链长N*N,如果等于,说明陷入局部极小,转i;否则,转iv;
iv. 随机选择两个皇后进行列号交换,更新冲突表CollTable,计算其冲突数是否减小,是则交换;否则恢复其冲突表CollTable,不进行交换;转ii。

d)   程序框图:


e) 效率表

皇后个数N

10

100

1000

10000

1

不足0.001s

约0.001s

0.033s

0.576s

2

不足0.001s

约0.006s

0.027s

0.543s

3

不足0.001s

约0.008s

0.043s

0.564s

4

约0.001s

约0.01s

0.031s

0.643s

5

不足0.001s

约0.003s

0.028s

0.634s

6

不足0.001s

约0.001s

0.029s

0.486s

7

约0.001s

约0.001s

0.064s

0.462s

8

约0.001s

约0.005s

0.061s

0.569s

9

不足0.001s

约0.006s

0.019s

0.631s

10

不足0.001s

约0.007s

0.067s

0.593s

平均

不足0.001s

约0.0048s

0.0402s

0.5701s

注:由于随机数范围问题,此程序可能对于在32768以上的N存在一些问题。

#include <cstdio>#include <cstdlib>#include <iostream>#include <cstring>#include <iterator>#include <vector>#include <ctime>#define MAXN 30000using namespace std;int N;int Pos[MAXN];int CollTable[MAXN*2][2];//冲突表,分别记录正负对角线上的冲突数void OutputSolution(){    cout << "{";    for (int i = 0; i<N - 1; i++)        cout << Pos[i] << ", ";    cout << Pos[N-1] << "}" << endl;}void InitialPos(){    for (int i = 0; i<N; i++)        Pos[i] = i;    for (int i = 0; i<N; i++)    {        int pos = rand() % N;        swap(Pos[pos], Pos[N - pos - 1]);    }    memset(CollTable,0,sizeof(CollTable));}int CalcCollision(){    int CollCnt=0;    for(int i=0;i<N;i++)    {        CollTable[Pos[i]+i][0]++;        CollTable[Pos[i]-i+N-1][1]++;    }    for(int i=0;i<2*N-1;i++)    {        for(int j=0;j<2;j++)        {            if(CollTable[i][j]>1)                CollCnt+=CollTable[i][j]-1;        }    }    return CollCnt;}int NewCollision(int pos1,int pos2,int oc){    int currcollcnt=oc;    //删去原有冲突    if(--CollTable[Pos[pos1]+pos1][0]>0)        currcollcnt--;    if(--CollTable[Pos[pos1]-pos1+N-1][1]>0)        currcollcnt--;    if(--CollTable[Pos[pos2]+pos2][0]>0)        currcollcnt--;    if(--CollTable[Pos[pos2]-pos2+N-1][1]>0)        currcollcnt--;    //计算现有冲突    if(++CollTable[Pos[pos1]+pos2][0]>1)        currcollcnt++;    if(++CollTable[Pos[pos1]-pos2+N-1][1]>1)        currcollcnt++;    if(++CollTable[Pos[pos2]+pos1][0]>1)        currcollcnt++;    if(++CollTable[Pos[pos2]-pos1+N-1][1]>1)        currcollcnt++;    return currcollcnt;}void RecoverCollision(int pos1,int pos2){    //恢复原有冲突    ++CollTable[Pos[pos1]+pos1][0];    ++CollTable[Pos[pos1]-pos1+N-1][1];    ++CollTable[Pos[pos2]+pos2][0];    ++CollTable[Pos[pos2]-pos2+N-1][1];    //删去现有冲突    --CollTable[Pos[pos1]+pos2][0];    --CollTable[Pos[pos1]-pos2+N-1][1];    --CollTable[Pos[pos2]+pos1][0];    --CollTable[Pos[pos2]-pos1+N-1][1];}int SwapQueen(int OrigCollCnt){    int pos1 = rand() % N;    int pos2 = rand() % N;    while (pos1 == pos2)        pos2 = rand() % N;    int CurrCollCnt = NewCollision(pos1,pos2,OrigCollCnt);    if (CurrCollCnt < OrigCollCnt)        swap(Pos[pos1], Pos[pos2]);    else    {        CurrCollCnt=OrigCollCnt;        RecoverCollision(pos1,pos2);    }    return CurrCollCnt;}int Queen(){    int Markov = N*N;    clock_t start=clock();    while (1)    {        int IterCnt = 0;        InitialPos();        int collcnt = CalcCollision();        if (collcnt == 0)        {            OutputSolution();            return 1;        }        while (IterCnt<Markov)        {            collcnt = SwapQueen(collcnt);            if (collcnt == 0)            {                clock_t finish=clock();                OutputSolution();                double totaltime=(double)(finish - start)/CLOCKS_PER_SEC;                cout<<"Speed:"<<totaltime<<"s"<<endl;                return 1;            }            IterCnt++;        }    }    return 0;}int main(){    srand(time(NULL));    while (cin >> N)    {        memset(Pos, -1, sizeof(Pos));        if (N <= 0)            cout << "N is not legal!" << endl;        else if(N >= MAXN)            cout << "For my PC's random range, I limit the N less than 30000" << endl;        else if (N == 2 || N == 3)                cout << "Cannot get a solution" << endl;        else            Queen();    }    return 0;}



0 0
原创粉丝点击