八皇后问题
八皇后问题
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
这个问题我思考了好几天,然后自己用python实现的,详细思路代码中体会,这个应该是最简单的实现方式了
实现方式:遍历枚举
直接上代码:
1 # 这一段是搞出来一个坐标系 2 # lisx = [] 3 # for i in range(8): 4 # x = [i for j in range(8)] 5 # lisx.append(x) 6 # lisy = list(range(8)) 7 # 8 # lis = [(list(zip(lisx[i], lisy))) for i in range(8)] 9 10 11 # 3种方法,反转列表 12 # lis.reverse() # reverse方法没有返回值,它修改的是lis列表本身 13 # print(lis) 14 # print(reversed(lis)) # reversed返回的是一个对象,<list_reverseiterator object at 0x00000000024E9B00> 15 # print(list(reversed(lis))) 16 # print(lis[::-1]) 17 # print(lis.sort(reverse=True)) # lis.sort()方法也是没有返回值的; 18 # 也可以指定参数key用哪个关键参数,可以指定key等于一个函数 19 # print(lis) 20 import random 21 22 lis = [[0, 0, 0, 0, 0, 0, 0, 0], 23 [0, 0, 0, 0, 0, 0, 0, 0], 24 [0, 0, 0, 0, 0, 0, 0, 0], 25 [0, 0, 0, 0, 0, 0, 0, 0], 26 [0, 0, 0, 0, 0, 0, 0, 0], 27 [0, 0, 0, 0, 0, 0, 0, 0], 28 [0, 0, 0, 0, 0, 0, 0, 0], 29 [0, 0, 0, 0, 0, 0, 0, 0]] 30 31 32 # 这里的(x,y)对应lis的行和列 33 34 # 为了方便8*8的国际棋盘对应成搞成坐标系的样子,更好的处理对角线的排查,K斜率(对角线) 35 # [[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7)], 36 # [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7)], 37 # [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7)], 38 # [(3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7)], 39 # [(4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7)], 40 # [(5, 0), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7)], 41 # [(6, 0), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7)], 42 # [(7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 7)]] 43 44 45 def check_position(y, x): 46 """ 47 初始状态每个格子都是0 48 判断的标志是,如果已经放了皇后lis[i][j] ,这里就是1 ,代表已经被占了 49 50 """ 51 global lis 52 for i in range(8): 53 # 横向检查 54 if lis[y][i] == 1: 55 return False 56 print(lis[y]) 57 print('横向检查完毕') 58 for j in range(8): 59 # 纵向检查 60 if lis[j][x] == 1: 61 return False 62 63 print(lis[j][x]) 64 print('纵向检查完毕') 65 66 # 对角线的检测思路是,把对角线上的 横坐标 = 0 ~ 7 上的点全部判断一遍 67 # 接下来 i 就是横坐标, j 就是纵坐标 68 for i in range(8): 69 # 一次函数 y = kx + b 70 # 45°角对角线,斜率k=1 71 b = y - x 72 j = i + b # 带入方程得纵坐标 73 if 0 <= i < 8 and 0 <= j < 8: 74 75 print((i, j)) 76 if lis[j][i] == 1: 77 return False 78 79 print('45°角检查完毕') 80 81 for i in range(8): 82 # 一次函数 y = kx + b 83 # 135°角对角线,斜率k=-1 84 b = y + x 85 j = -i + b # 带入方程得纵坐标 86 if 0 <= i < 8 and 0 <= j < 8: 87 88 print((i, j)) 89 if lis[j][i] == 1: 90 return False 91 print('135°角检查完毕') 92 return True 93 94 95 def scan(y): 96 """ 扫描整层 ,也就是一行 97 右移旧皇后产生新皇后返回True 98 没有旧皇后,扫描该行产生新皇后,返回 True 99 否则,产生不了新皇后 ,返回False 100 """ 101 result = False 102 103 if 1 in lis[y]: # 去找该行的旧皇后 104 x = lis[y].index(1) # 获取旧皇后的横坐标 105 lis[y][x] = 0 # 废掉旧皇后 106 while x < 7: # 这里不是8,而是7,因为x = x+1 = 6+1 旧皇后已经在最右侧,说明无法右移了 107 x = x + 1 108 if check_position(y, x): 109 lis[y][x] = 1 # 获取新皇后 110 result = True 111 break 112 else: # 该行没有旧皇后就去找新皇后 113 x = 0 114 while x < 8: 115 x = x + 1 116 if check_position(y, x): 117 lis[y][x] = 1 # 获取新皇后 118 result = True 119 break 120 return result 121 122 123 124 125 def go_back(y): 126 """递归回溯, 127 返回 128 如果前一层,有旧皇后,废掉旧皇后,右移得到新皇后的 y+1 ;也就是纵坐标 129 如果前一层,没有旧皇后,就扫描得到该行的新皇后的 y+1 ; 130 如果前一层得不到新皇后,就继续向前一层的前一层回溯,及 y-1 131 132 直到回溯到第0层,必然能够取到新皇后,就返回新皇后的 y +1 133 134 综上就是返回 前一层新皇后的 y + 1 135 136 """ 137 global lis 138 139 if y > 0: 140 if scan(y): 141 return y + 1 142 else: 143 if scan(y): 144 return y + 1 145 146 return go_back(y - 1) 147 148 149 def get_count(): 150 """ 计算count """ 151 count = 0 152 for i in range(8): 153 if 1 in lis[i]: 154 count += 1 155 return count 156 157 158 159 160 def get_queen(lis): 161 """ 获得我们的皇后 """ 162 x = random.randint(0, 7) #random.randint(a,b) 包括a,b 163 y = 0 164 while 0 <= y < 8 and 0 <= x < 8: 165 166 print('x = {}'.format(x)) 167 print('y = {}'.format(y)) 168 if check_position(y, x): 169 lis[y][x] = 1 170 print('第{}个皇后,在{}层,{}列'.format(get_count(), y, x)) 171 y += 1 172 x = 0 173 else: 174 x += 1 175 if x == 8: 176 y = go_back(y-1) 177 x = 0 178 print('回溯到上一层{}'.format(y)) # 我们回溯到第1层,其实就已经扫描了第0层 179 180 # 下面是一个判断跳出循环的条件;用了一个count变量来计数 181 # 如果已经放满八个皇后了,就退出 182 count = get_count() 183 if count == 8: 184 break 185 print('-' * 50, get_count()) 186 return lis 187 188 189 queen_lis = get_queen(lis) 190 print(queen_lis) 191 192 # [[0, 0, 0, 0, 0, 1, 0, 0], 193 # [1, 0, 0, 0, 0, 0, 0, 0], 194 # [0, 0, 0, 0, 1, 0, 0, 0], 195 # [0, 1, 0, 0, 0, 0, 0, 0], 196 # [0, 0, 0, 0, 0, 0, 0, 1], 197 # [0, 0, 1, 0, 0, 0, 0, 0], 198 # [0, 0, 0, 0, 0, 0, 1, 0], 199 # [0, 0, 0, 1, 0, 0, 0, 0]] 200 201 # 细节 202 """ 203 Help on method randint in module random: 204 205 randint(a, b) method of random.Random instance 206 Return random integer in range [a, b], including both end points 207 """
我写完了,想不能优化一下代码,
我的get_queen()函数里面的while 判断break条件是,写了一个get_count()函数,判断跳出循环;
大家,给我指点一下,有没有更好的判断跳出,条件
还有个小的问题,想问一下大伙,我怎么去判断总共有多少种摆放方法呢?
他们是在怎么知道总共92种的?
我想过,用一个集合把每次的lis结果,add到集合里,但是我需要循环多少次呢?
换句话说哦就是,8*8的棋盘放8个皇后,理想状态下,最多有多少种方法,不考虑皇后相互吃掉的限制。
ps:(饿了,吃饭去了┯┯┯)
今天再看廖雪峰大神的python教程,领悟,更加简洁的方式快速生成,(8*8的国际象棋的坐标系)
1 >>> [(x,y) for x in range(8) for y in range(8)] 2 [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7),
(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7),
(2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7),
(3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7),
(4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7),
(5, 0), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7),
(6, 0), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7),
(7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 7)]
生成棋盘初始化,状态
1 >>> lis = [0 for i in range(8) for j in range(8)] 2 >>> len(lis) 3 64 4 >>> def get_result(L = []): 5 res_lis = [] 6 n = len(lis) 7 for i in lis: 8 L.append(i) 9 n = n - 1 10 if n%8 == 0 and n != len(lis): 11 res_lis.append(L) 12 L = [] 13 return res_lis 14 15 >>> res_lis = get_result() 16 >>> res_lis 17 [[0, 0, 0, 0, 0, 0, 0, 0], 18 [0, 0, 0, 0, 0, 0, 0, 0], 19 [0, 0, 0, 0, 0, 0, 0, 0], 20 [0, 0, 0, 0, 0, 0, 0, 0], 21 [0, 0, 0, 0, 0, 0, 0, 0], 22 [0, 0, 0, 0, 0, 0, 0, 0], 23 [0, 0, 0, 0, 0, 0, 0, 0], 24 [0, 0, 0, 0, 0, 0, 0, 0]] 25 >>> len(res_lis) 26 8

浙公网安备 33010602011771号