问题:有一个8乘8的棋盘,现在要将八个皇后放到棋盘上,满足:对于每一个皇后,在自己所在的行、列、两个对角线都没有其他皇后。
想法:首先规定同列只能出现一个皇后。每一个棋盘,对应于一个长度为8的串,每一个数的范围是[1, 8],第k个数字所代表的含义是第k列中皇后所在的行数,如[3,2,5,4,3,2,1,3]代表棋盘上从第一列到第八列,皇后所摆放的行数分别为第3,2,5,4,3,2,1,3行。
程序1:generate_init_seq.py。如果8个皇后在8*8的棋盘上可以随意摆放,当然是不能在同一个格子里放超过一个皇后的情况下,本来所有需要测试是否满足要求的序列共有64*63*…*57=1.78e+14个,这太多了。所以此程序的工作是筛选出那些【每行与每列都只有一个皇后存在】的序列,这样的序列有8*7*6*5*4*3*2=40320个,可以大大缩减了后续程序的运行时间,而且这样在后面处理每个序列时只需要考虑两条对角线上有没有其他皇后即可。如下:
import json, time
start = time.time()
seq = [[i, j, k, l, m, n, o, p]
for i in range(1, 9)
for j in range(1, 9)
for k in range(1, 9)
for l in range(1, 9)
for m in range(1, 9)
for n in range(1, 9)
for o in range(1, 9)
for p in range(1, 9)
if all([i!=j, i!=k, i!=l, i!=m, i!=n, i!=o, i!=p,
j!=k, j!=l, j!=m, j!=n, j!=o, j!=p,
k!=l, k!=m, k!=n, k!=o, k!=p,
l!=m, l!=n, l!=o, l!=p,
m!=n, m!=o, m!=p,
n!=o, n!=p,
o!=p])] # 筛选出那些【每行与每列都只有一个皇后存在】的序列
print('有' + str(len(seq)) + '个可能的序列')
with open('seq.json', 'w') as file_object:
json.dump(seq, file_object)
end = time.time()
print('Successful!')
print('已将生成的序列存储到文件seq.json中,用时' + str('%.2f' % (end-start)) + 's')
输出如下。注意会生成一个文件seq.json,我上传到了csdn上,你可以看看这里,你也可以运行程序1,在自己电脑上得到一个文件,和我这个是一样的:
有40320个可能的序列
Successful!
已将生成的序列存储到文件seq.json中,用时23.78s
程序2:main.py。如下:
import json
import numpy as np
with open('seq.json', 'r') as file_object:
seq = json.load(file_object) # 载入保存好的序列
solutions = 0 # 记录有几个解
for s in seq:
a = np.array([0] * 81) # 在开始时创建一个有81个0的一维数组
a = a.reshape(9,9) # 改变为9*9二维数组,为了后面方便使用,只用后八行和后八列的8*8部分,作为一个空白棋盘
flag = 1 # 假设当前序列对应的棋盘满足条件
for i in range(1, 9):
a[s[i-1]][i] = 1 # 根据序列,从第一列到最后一列的顺序,在对应位置放一个皇后,生成当前序列对应的棋盘
for i in range(1, 9): # 检查当前序列的八个皇后在各自的两条对角线上是否有其他皇后
t1 = t2 = s[i-1]
for j in range(i-1, 0, -1): # 看左半段
if t1 != 1:
t1 -= 1
if a[t1][j] == 1:
flag = 0 # 正对角线左半段上有其他皇后,表示当前序列不满足条件,不用再检查次对角线左半段、正对角线右半段、次对角线右半段
break
if t2 != 8:
t2+= 1
if a[t2][j] == 1:
flag = 0 # 次对角线左半段上有其他皇后,表示当前序列不满足条件,不用再检查正对角线右半段、次对角线右半段
break
if flag == 0:
break # 当前序列不满足条件,不用再检查正对角线右半段、次对角线右半段
t1 = t2 = s[i-1] # 如果能到这步,说明正对角线左半段、次对角线左半段符合条件,继续检查右半段
for j in range(i+1, 9): # 看右半段
if t1 != 1:
t1 -= 1
if a[t1][j] == 1:
flag = 0 # 正对角线右半段上有其他皇后,表示当前序列不满足条件,不用再检查次对角线右半段
break
if t2 != 8:
t2+= 1
if a[t2][j] == 1:
flag = 0 # 次对角线右半段上有其他皇后,表示当前序列不满足条件
break
if flag == 1: # 经过层层筛选,如果序列符合条件则执行下面内容
solutions += 1 # 计数+1
print('第' + str(solutions) + '个解,此序列:'+ str(s) + ' 符合条件,对应棋盘如下:')
for i in a[1:]: # 输出对应棋盘
for j in i[1:]:
print(j, ' ',end="") # 有了end="",print就不会换行
print() # 输出完一行后再换行,这里不能是print('\n'),否则会换两行
print('---------------') # 分割线
print('共' + str(solutions) + '个解') # 最后再明确一下有几个解
输出:
第1个解,此序列:[1, 5, 8, 6, 3, 7, 2, 4] 符合条件,对应棋盘如下:
1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0
---------------
第2个解,此序列:[1, 6, 8, 3, 7, 4, 2, 5] 符合条件,对应棋盘如下:
1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 1 0 0 0 0 0
---------------
第3个解,此序列:[1, 7, 4, 6, 8, 2, 5, 3] 符合条件,对应棋盘如下:
1 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 0 1 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 1 0 0 0
......
第92个解,此序列:[8, 4, 1, 3, 6, 2, 7, 5] 符合条件,对应棋盘如下:
0 0 1 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 1 0 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 0
---------------
共92个解
在不考虑对称性的情况下,解共有92个,我在上面的输出中只放了第1、2、3、92个解。因为太多,都放的话会占太多篇幅。想获取全部的解,你可以下载这个文件,也可以在自己电脑上运行程序2,可以得到一个文件,和我这个是一样的。
评价:自己认为程序中写的注释很好,哈哈!
END