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

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

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 30000
using 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;
}



你可能感兴趣的:(人工智能,n皇后问题,局部搜索)