1. 程式人生 > >【人工智慧】八皇后問題-啟發式求解

【人工智慧】八皇后問題-啟發式求解

摘要

八皇后問題是回溯演算法的典型案例,在回溯法中,常常是盲目搜尋,耗費過多的搜尋時間。在本次實驗中,使用了啟發式搜尋,搜尋時不是任取一個分支,而是選擇最佳的分支往下搜尋。通過定義狀態空間、操作規則、搜尋策略,我們可以清晰快速地得到原問題的一個解。

導言

八皇后問題是一個以國際象棋為背景的問題:如何能夠在 8×8 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處於同一條橫行、縱行或斜線上。通過計算機程式設計,我們可以快速地求出問題的解。

實驗過程

狀態空間

(i,C[i]), i = 0,1,…,7;
(i
,C[i])表示第i行的皇后放置在第C[i]列。 初始狀態為C[i] = -1, i = 0,1,…,7;表示所有行都不放置皇后。 目標狀態為C[i] != -1, i = 0,1,…,7;表示所有行都已經放置了皇后。

操作規則

第一個皇后放在第一行;
第二個皇后放在第二行且不與第一個皇后在同一列或對角線的空格上;
……
第i個皇后放在第i行且不與前面i-1個皇后在同一列或對角線的空格上。

搜尋策略

    由於在某一步放置某個皇后時,可能有多個空格可以使用,所以定義啟發式函式:

        fx = 剩下未放行中能夠用來放皇后的空格數

    如果第i行的皇后放在第j
列合法,計算啟發式函式的值fx(i,j)。計算出第i行所有空格的fx後,將第i個皇后放到第i行中那個與前面i-1個皇后不在同一列或對角線上且fx值最大的空格中(相同時取第一個)。 如果當前策略無法求解,則回溯至上一步,選擇fx值次大的空格放置皇后,依次類推,直至找到一個合法的解。

C++原始碼

#include<iostream>
#include<cstring>
using namespace std;

const int n = 8;
int C[8];       //狀態空間,C[i]表示第i行的皇后放在第C[i]列; 
int fx[8][8
]; //fx值,fx[i][j]表示在i,j處放置皇后後,剩下行中可以放Q的空格數 int ansflag = 0;//標記是否已經找到答案 bool vis[3][15];//vis[0][j]表示第j列有無皇后,vis[1 or 2][i+j]表示(i,j)正反對角線有無皇后 int f(int row) { //找出剩下行可以放Q的空格數。 int cnt = 0; for (int i = row+1; i < n; i++) for (int j = 0; j < n; j++) if (!vis[0][j] && !vis[1][i+j] && !vis[2][i-j+n]) cnt++; return cnt; } void search(int cur) { if (cur == n) ansflag++; //所有行都合法地放置了皇后,結束 else { int flag = 0; //標記該行是否可以放置皇后 for(int i = 0; i < n; i++) //對cur行,測試每一個空格 { if (!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n])//不與前面皇后衝突 { flag = 1; //該行可以放置皇后 C[cur] = i; //嘗試將皇后放在第i列 vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1; fx[cur][i] = f(cur); //計算fx值 vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;//計算完fx將皇后拿掉,嘗試下一個格子 } } if (flag) //該行可以放置皇后,接下來嘗試求解 { while(!ansflag) //沒有找到答案之前,進行回溯。 { int max = -1; int col = -1; //記錄fx最大的列 for (int i = 0; i < n; i++) //找fx最大的列 { if (fx[cur][i] > max) { max = fx[cur][i]; col = i; } } if (max == -1) //在本行任一空格放置皇后都無法求解,回溯 { fx[cur-1][C[cur-1]] = -1; //將原來的最大值置為-1,那麼下一次回溯找的是次大值。 return; } C[cur] = col; //找到fx最大的列,放置皇后,搜尋下一行。 vis[0][col] = vis[1][cur+col] = vis[2][cur-col+n] = 1; search(cur+1); vis[0][col] = vis[1][cur+col] = vis[2][cur-col+n] = 0; } } else //如果該行無法放置皇后,將上一行中放置皇后的位置對於的fx置-1,回溯。 fx[cur-1][C[cur-1]] = -1; } } int main() { memset(C, -1, sizeof(C)); memset(fx, -1, sizeof(fx)); search(0); for (int i = 0; i < n; i++) cout<<"第"<<i<<"個皇后放置在第"<<i<<"行第"<<C[i]<<"列"<<endl; cout<<endl<<endl; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (j == C[i]) cout<<"Q"<<' '; else cout<<"X"<<' '; } cout<<endl; } return 0; }

結果

一個可行的解:
第0個皇后放置在第0行第0列
第1個皇后放置在第1行第5列
第2個皇后放置在第2行第7列
第3個皇后放置在第3行第2列
第4個皇后放置在第4行第6列
第5個皇后放置在第5行第3列
第6個皇后放置在第6行第1列
第7個皇后放置在第7行第4列

解對應的棋盤:
Q X X X X X X X
X X X X X Q X X
X X X X X X X Q
X X Q X X X X X
X X X X X X Q X
X X X Q X X X X
X Q X X X X X X
X X X X Q X X X