八皇后问题是回溯算法的典型案例,在回溯法中,常常是盲目搜索,耗费过多的搜索时间。在本次实验中,使用了启发式搜索,搜索时不是任取一个分支,而是选择最佳的分支往下搜索。
目录
前言:
XDU作业:启发式算法解决八皇后问题。
问题描述:问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
一、初始化参数:
n:代表皇后的个数,因为规定每行放置一个皇后,所以也可以理解为行数。
checkboard:代表最后呈现出来的矩阵,这里用‘0’表示该位置为空位,既没有放置皇后;用‘1’代表该位置已经放置皇后了。
queen_position: 代表每一行皇后所在改行的列数,因为题目默认是8行,所以设置了8个元素的数组初始值为-1,如果第一行的元素放在第1列,则queen_position[0] = 1。
available_position: 检测该行的某列是否满足可以放置皇后的条件,为三行十五列的矩阵,available_position[0][i]代表i列是否已经放置过皇后了,available_position[1][i]代表该位置的右对角线是否放置过皇后,available_position[2][i]代表该位置左对角线是否放置过皇后。
fx:代表代价。即若该位置皇后后,后面可以放置皇后的位置还有多少个。
answer: 代表该行是否符合可以放置皇后。
二、检测某位置是否可以放置皇后
输入函数的参数为行数和列数。
如果该位置的左对角线,右对角线,及该位置所在的列数都没有放置过皇后,则满足条件。
返回True,则该位置可以放置皇后,返回False,则该位置不满足放置皇后的条件。
#to test the position of [row, col] could be able to place queen or not
def queen_admission(row, col):
global available_position
global queen_position
if(available_position[0][col] and available_position[1][row+col] and available_position[2][row-col+n-1]):
return True
else:
return False
三、某位置放置皇后的调整
输入函数的参数为行数和列数。
在该位置放置皇后,则该列的0要置1,左对角线和右对角线的相应的位置都要置1。
并记录该行皇后所在的位置。
#place the queen into [row,col]
def place_queen(row, col):
global available_position
global queen_position
queen_position[row] = col
available_position[0][col] = 0
available_position[1][row+col] = 0
available_position[2][row-col+n-1] = 0
return None
四、删除某位置的皇后
输入函数的参数为行数和列数。
与放置皇后的操作相反,将该行皇后的位置置-1,及不确定该行皇后所在的列数。
依次将该位置所在的列数,左右对角线相应的位置清零。
#delete the queen in [row,col]
def delete_queen(row, col):
global available_position
global queen_position
queen_position[row] = -1
available_position[0][col] = 1
available_position[1][row+col] = 1
available_position[2][row-col+n-1] = 1
return None
五、计算剩余行所剩可以放置皇后位置的个数
输入函数的参数为行数和列数。
依次遍历剩余行所有的列数,并判断每个位置是否为可以放置皇后位置的位置,如果满足条件则计数+1。
#find the available number
def commute_beneficial(row, col):
global n
global available_position
remain_available_number = 0
place_queen(row, col)
for i in range(row+1, n):
for j in range(n):
if(queen_admission(i, j)):
remain_available_number += 1
delete_queen(row, col)
return remain_available_number
六 、呈现最后的矩阵
取每行皇后所在列数,遍历checkboard并更改其值,最后输出checkboard。
#presentation the checkerboard
def final_show():
global n
global queen_position
global checkerboard
for i in range(n):
queen_position[i] = np.argmax(fx[i])
for i in range(n):
for j in range(n):
if j == queen_position[i]:
checkerboard[i][j] = 1
print(checkerboard)
运行结果:
七、启发式搜索
该算法采用递归的思想,从第一行开始,直到最后一行,每列所取的列数为fx最大的那个列数。如果到某一行发现没有所满足的列数,则返回上一行,取fx次大的列数继续直到cur的值为8。
#启发式搜索
def search(cur):
global n
global queen_position
global checkerboard
global fx
global answer
if(cur == n):
answer = True
else:
flag = False
for i in range(n):
if(queen_admission(cur, i)):
flag = True
fx[cur][i] = commute_beneficial(cur, i)
if(flag):
while(answer == False):
max = -1
max_col = -1
max = np.max(fx[cur])
max_col = np.argmax(fx[cur])
if(max == -1):
fx[cur-1][queen_position[cur-1]] = -1
return
queen_position[cur] = max_col
place_queen(cur, max_col)
search(cur+1)
delete_queen(cur, max_col)
else:
fx[cur-1][queen_position[cur-1]] = -1
八、完整代码:
'''
由于在某一步放置某个皇后时,可能有多个空格可以使用,所以定义启发式函数:
fx = 剩下未放行中能够用来放皇后的空格数
如果第i行的皇后放在第j列合法,计算启发式函数的值fx(i,j)。计算出第i行所有空格的fx后,
将第i个皇后放到第i行中那个与前面i-1个皇后不在同一列或对角线上且fx值最大的空格中(相同时取第一个)。
如果当前策略无法求解,则回溯至上一步,选择fx值次大的空格放置皇后,依次类推,直至找到一个合法的解。
'''
import numpy as np
n = 8
checkerboard = np.zeros((8, 8), dtype=int)
queen_position = np.zeros(8, dtype=int)-1
available_position = np.ones((3, 15), dtype=int)
fx = np.zeros((n, n), dtype=int) - 1
answer = False
#to test the position of [row, col] could be able to place queen or not
def queen_admission(row, col):
global available_position
global queen_position
if(available_position[0][col] and available_position[1][row+col] and available_position[2][row-col+n-1]):
return True
else:
return False
#place the queen into [row,col]
def place_queen(row, col):
global available_position
global queen_position
queen_position[row] = col
available_position[0][col] = 0
available_position[1][row+col] = 0
available_position[2][row-col+n-1] = 0
return None
#delete the queen in [row,col]
def delete_queen(row, col):
global available_position
global queen_position
queen_position[row] = -1
available_position[0][col] = 1
available_position[1][row+col] = 1
available_position[2][row-col+n-1] = 1
return None
#find the available number
def commute_beneficial(row, col):
global n
global available_position
remain_available_number = 0
place_queen(row, col)
for i in range(row+1, n):
for j in range(n):
if(queen_admission(i, j)):
remain_available_number += 1
delete_queen(row, col)
return remain_available_number
#presentation the checkerboard
def final_show():
global n
global queen_position
global checkerboard
for i in range(n):
queen_position[i] = np.argmax(fx[i])
for i in range(n):
for j in range(n):
if j == queen_position[i]:
checkerboard[i][j] = 1
print(checkerboard)
#启发式搜索
def search(cur):
global n
global queen_position
global checkerboard
global fx
global answer
if(cur == n):
answer = True
else:
flag = False
for i in range(n):
if(queen_admission(cur, i)):
flag = True
fx[cur][i] = commute_beneficial(cur, i)
if(flag):
while(answer == False):
max = -1
max_col = -1
max = np.max(fx[cur])
max_col = np.argmax(fx[cur])
if(max == -1):
fx[cur-1][queen_position[cur-1]] = -1
return
queen_position[cur] = max_col
place_queen(cur, max_col)
search(cur+1)
delete_queen(cur, max_col)
else:
fx[cur-1][queen_position[cur-1]] = -1
if __name__ == "__main__":
search(0)
final_show()