努力是为了不平庸~
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。
目录
一、问题描述
二、问题解决思路
1. 建立数据结构:
2. 约束条件的实现:
3. 结果展示:
4. 拓展至n皇后问题:
三、代码展示
四、n皇后问题的思考
解决用户不能自由输入问题
解决方案显示和保存的优化
一、问题描述
八皇后问题(英文:Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。计算机发明后,有多种计算机语言可以编程解决此问题。
二、问题解决思路
1. 建立数据结构:
我们可以使用一个一维数组来表示皇后的位置,数组的索引代表皇后所在的列,数组的值代表皇后所在的行。
例如,对于八皇后问题,数组 [0, 4, 7, 5, 2, 6, 1, 3] 表示第一列的皇后在第0行,第二列的皇后在第4行,以此类推。
在拓展至n皇后问题时,数组的长度即为n,每个值的范围为0到n-1,表示每一列的皇后所在的行。
2. 约束条件的实现:
在回溯算法中,我们需要实现约束条件来确保每个皇后的位置是合法的,即它们不会相互攻击。
同一行和同一列的约束条件很容易实现,只需检查数组中是否有相同的值即可。
对于对角线的约束条件,我们可以通过判断两个皇后的行差与列差的绝对值是否相等来确定它们是否处于同一对角线上。
在代码中,可以使用一个辅助函数来检查每个位置是否满足约束条件,如果满足,则继续递归处理下一列。
3. 结果展示:
结果展示可以使用matplotlib库来绘制棋盘,其中皇后的位置可以用不同的颜色或符号表示。
可以创建一个函数,接收一个皇后位置的数组作为参数,然后使用matplotlib绘制一个方格棋盘,并在对应位置上绘制皇后。
4. 拓展至n皇后问题:
要拓展至n皇后问题,我们需要对代码进行适当修改。主要的修改是将数组的长度从8修改为n,表示棋盘的大小为n×n。
在约束条件的实现中,需要注意判断对角线的条件,即判断两个皇后的行差与列差的绝对值是否相等。
在结果展示中,需要根据n的大小来动态调整棋盘的大小,并绘制对应的皇后位置。
三、代码展示
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def is_safe(board, row, col):
# 检查当前位置是否安全(不受其他皇后的攻击)
for i in range(col):
if board[i] == row or board[i] == row - (col - i) or board[i] == row + (col - i):
return False
return True
def solve_n_queens_util(board, col, solutions):
if col == len(board):
# 所有列都已经尝试过,找到一个解决方案
# 将解决方案添加到DataFrame中
solutions.loc[len(solutions)] = board
return
for row in range(len(board)):
if is_safe(board, row, col):
# 在当前位置放置皇后
board[col] = row
# 递归尝试下一列
solve_n_queens_util(board, col + 1, solutions)
def solve_n_queens():
board = [-1] * 8
solutions = pd.DataFrame(columns=['Q{}'.format(i+1) for i in range(8)])
solve_n_queens_util(board, 0, solutions)
return solutions
def draw_board(board, solution_number):
plt.figure()
plt.xlim(0, 8)
plt.ylim(0, 8)
# 绘制棋盘格子
for i in range(8):
for j in range(8):
if (i + j) % 2 == 0:
plt.fill([j, j, j + 1, j + 1], [i, i + 1, i + 1, i], 'white')
else:
plt.fill([j, j, j + 1, j + 1], [i, i + 1, i + 1, i], 'gray')
# 绘制皇后位置
for col, row in enumerate(board):
plt.text(row + 0.5, col + 0.5, 'Q', fontsize=20, ha='center', va='center', color='black')
plt.axis('off')
plt.savefig('solution_{}.png'.format(solution_number), bbox_inches='tight')
plt.close()
if __name__ == '__main__':
solutions = solve_n_queens()
print('总共找到 {} 个解决方案'.format(len(solutions)))
for i, solution in solutions.iterrows():
print('解决方案 {}:'.format(i + 1))
draw_board(np.array(solution), i + 1)
四、n皇后问题的思考
n皇后的代码相对于之前的八皇后问题代码只是做了很少的处理。
-
解决用户不能自由输入问题
在八皇后问题中,n的值是固定的(n=8),直接在代码中进行指定。而在修改过后的代码中,使用了input函数,让用户可以通过键盘输入n的值,从而实现了根据用户需求解决不同规模的n皇后问题。
-
解决方案显示和保存的优化
在原先的代码中,所有找到的解决方案都会被显示和保存为图片文件。而在这个代码中,先输出找到的解决方案的数量,然后询问用户是否要保存解决方案的图片文件,根据用户的选择进行相应的操作。这样可以提供更灵活的选择,避免不必要的图片保存。
总体而言,这个代码在八皇后问题的求解上没有本质性的改变,主要是在用户交互和结果展示方面进行了改进,增加了代码的灵活性和可定制性。
下面展示部分修改的代码段:
def is_safe(board, row, col):
# 检查当前位置是否安全(不受其他皇后的攻击)
for i in range(col):
if board[i] == row or board[i] == row - (col - i) or board[i] == row + (col - i):
return False
return True
def solve_n_queens_util(board, col, solutions):
if col == len(board):
# 所有列都已经尝试过,找到一个解决方案
# 将解决方案添加到DataFrame中
solutions.loc[len(solutions)] = board
return
for row in range(len(board)):
if is_safe(board, row, col):
# 在当前位置放置皇后
board[col] = row
# 递归尝试下一列
solve_n_queens_util(board, col + 1, solutions)
def solve_n_queens(n):
board = [-1] * n
solutions = pd.DataFrame(columns=['Q{}'.format(i+1) for i in range(n)])
solve_n_queens_util(board, 0, solutions)
return solutions
n = int(input("请输入皇后的数量:"))
solutions = solve_n_queens(n)
print('总共找到 {} 个解决方案'.format(len(solutions)))
user_input = input("是否保存解决方案图片?(yes/no): ")
if user_input.lower() == "yes":
for i, solution in solutions.iterrows():
draw_board(np.array(solution), i + 1)
print("解决方案 {} 已保存为图片".format(i + 1))
else:
print("解决方案未保存为图片")
其他部分与八皇后问题一致。