八皇后问题

八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于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

 


 





posted @ 2018-08-11 19:26  mhh4399  阅读(91)  评论(2)    收藏  举报