Python 八皇后问题
八皇后问题描述:在一个8✖️8的棋盘上,任意摆放8个棋子,要求任意两个棋子不能在同一行,同一列,同一斜线上,问有多少种解法。
规则分析:
任意两个棋子不能在同一行比较好办,设置一个队列,队列里的每个元素代表一行,就能达到要求
任意两个棋子不能在同一列也比较好处理,设置的队列里每个元素的数值代表着每行棋子的列号,比如(0,7,3),表示第一行的棋子放在第一列,第二行的棋子放在第8列,第3行的棋子放在第4列(从0开始计算列号)
任意两个棋子不能在同一斜线上,可以把整个棋盘当作是一个XOY平面,原点在棋盘的左上角,斜线的斜率为1或者-1,X为列号,Y为行号,推出斜线的表达式为Y=X+n或者Y=-X+n(n为常数,斜线确定下来之后n就确定了),进而可以推导出Y-X=n或者Y+X=n。也就是说在同一斜线上的两个棋子行号与列号之和或者之差相等。X1+Y1=X2+Y2或者X1-Y1=X2-Y2。再进行变换能够得到X1-X2=Y2-Y1或者X1-X2=Y1-Y2,也就是说|X1-Y1|=Y1-Y2。即判断两个棋子是否在同一斜线上,只要判断出两个棋子的列号之差是否等于两个棋子的行号之差的绝对值就行了。如下图:
将上述文字分析转化为代码,就可以判断棋子之间是否符合规则了(abs(num)表示取num的绝对值)
def is_rule(queen_tup, new_queen):
"""
:param queen_tup: 棋子队列,用于保存已经放置好的棋子,数值代表相应棋子列号
:param new_queen: 被检测棋子,数值代表列号
:return: True表示符合规则,False表示不符合规则
"""
num = len(queen_tup)
for index, queen in enumerate(queen_tup):
if new_queen == queen: # 判断列号是否相等
return False
if abs(new_queen-queen) == num-index: # 判断列号之差绝对值是否与行号之差相等
return False
return True
事实上,这段代买还可以简写,判断列号之差也可以写作是列号之差是否为0,这样就可以使用一个in来完成整个判断。修改后如下
def is_rule(queen_tup, new_queen):
"""判断棋子是否符合规则"""
for index, queen in enumerate(queen_tup):
if abs(new_queen-queen) in (len(queen_tup)-index, 0): # 判断表达式
return False
return True
接下来写一下摆放棋子的函数
摆放棋子其实有两种方法,第一种,求出8✖️8棋盘上每行放置一个棋子的所有方法,也就相当于全排列。然后再用冲突函数逐个判断是否符合规则,如符合就放入队列
第二种,在一行放入棋子,然后判断是否符合规则,符合的情况下再去放下一行,下一行如果所有位置都不符合,退回到上一行,上一行的棋子再放置一个新的位置,然后再进去下一行判断有没有符合规则的棋子的位置。这种方法叫做递归回溯,每一行就相当于是一个回溯点
这里我使用第二种方法写个函数,先上代码,然后再解释
def arrange_queen(num, queen_tup=list()):
"""
:param num:棋盘的的行数,当然数值也等于棋盘的列数
:param queen_tup: 设置一个空队列,用于保存符合规则的棋子的信息
"""
for new_queen in range(num): # 遍历一行棋子的每一列
if is_rule(queen_tup, new_queen): # 判断是否冲突
if len(queen_tup) == num-1: # 判断是否是最后一行
yield [new_queen] # yield关键字
else:
# 若果不是最后一行,递归函数接着放置棋子
for result in arrange_queen(num, queen_tup+[new_queen]):
yield [new_queen] + result
如果能够理解上边函数的可以不用看下面的分析了,如果不明白,接下来我将举几个代码例子来说明上面的函数
首先是yield,这个是python里的关键字,带有yield的函数被称作为生成器函数。函数在执行的时候,遇到yield关键字会暂停函数的执行,同时返回yield右边的对象到函数被调用的地方,直到函数下次被执行,将回到yield所在的地方继续执行,如果函数执行完毕还没有遇到yield,就会抛出一个异常StopIteration。而生成器函数需要使用next方法来执行。下面的代码将解释生成器函数的执行:
def demo():
yield 1
yield 2
print('end')
b = demo() # 将生成器函数的引用传递给变量b
print(next(b)) # 第一次执行生成器函数,返回 1 同时函数暂停,打印结果
print(next(b)) # 第二次执行生成器函数,返回 2 同时函数暂停,打印结果
print(next(b)) # 第三次执行生成器函数,因为没有再遇到yield,函数执行完毕,抛出异常StopIteration
但是上述放置棋子的代码中并没用调用next方法来执行生成器函数,而是使用了for循环遍历,并且在函数执行完毕之后也没有抛出StopIteration的错误。那是因为for循环在执行的时候,会不断的自动调用next方法,并且在遇到StopIteration的时候会捕捉异常并终止循环,以下代码我将模拟一下for循环来执行生成器函数
def demo():
yield 1
yield 2
print('end')
# 模拟的for循环
b = demo()
while True:
try:
next(b)
"""
此段区域写for下的代码块
"""
except StopIteration:
break
# 实际的for循环
for i in demo():
"""
for 下的代码块
"""
pass
通过这个可以知道,当使用for循环驱动生成器函数的时候,如果函数执行完毕还没有遇到yield关键字,就会直接退出for循环而不会执行for循环下的代码块。值得注意的是,上边两个循环分别是调用了两次生成器函数。生成器函数在一次执行完毕之后再继续调用是不会得到结果的
了解了生成器函数与for循环是怎么驱动生成器函数之后,关于棋子的递归函数里面还有一个就是递归函数了。以前上课的时候老师将递归函数使用的例子是数值的阶乘,这里我也使用阶乘来解释一下递归函数的执行。先介绍一下阶乘:给定一个正整数n,规定n的阶乘n!=n(n-1)(n-2).....1。也就是从1到n的累乘。(0!=1,这是规定,别问我为什么......)
def a(num):
result = num*b(num-1)
return result
def b(num):
result = num*c(num-1)
return result
def c(num):
if num == 1:
result = 1
return result
result = a(3)
print(result)
上述代码是函数嵌套,只能用作计算3的阶乘,我使用它来理解递归函数
a函数被调用执行的时候,传参3,然后调用函数b,同时传参3-1=2,函数b执行在调用函数c同时传参2-1=1,函数c执行,判断传参结果符合,返回数值result到函数c被调用的地方,然后与b的参数2相乘,得到新的结果赋值给b里面的result,然后再将result返回到b被调用的地方,再乘a的参数3赋值给a里面的result,再将a里的result返回到函数a被调用的地方,然后打印结果。
这就是利用函数的嵌套来执行出3!,那么如果想算10000的函数呢?难道写10000个函数?
这里发现a函数和b函数除了变量名字不一样,其余的形式都一摸一样,那么直接在a里面调用a函数,写成如下形式
def a(num):
result = num*a(num-1)
return result
但是这样的话,函数将不断的被调用。所以加一个函数终止的条件,变成了
def a(num):
if num == 1:
return 1
else:
return num*a(num-1)
result = a(3)
print(result)
这就是一个最简单的递归函数
分析函数的运行,函数第一次被调用,传递参数3,判断不满足终止条件。继续执行,接下来再调用函数a,传递参数3-1=2,判断不满足终止条件。继续执行,接下来再调用函数a,传递参数2-1=1,判断满足终止条件,第三次被调用的函数结束,返回1到被调用的地方,与2相乘,第二次被调用的函数结束,结果再返回到第二次函数被调用的地方,与3相乘,第一次被调用的函数结束,结果返回
这就是这个最简单的递归函数的执行过程。总结就是递归函数不断的调用自身,直至满足函数终止的条件
搞定了含有yield的生成器函数,for循环驱动生成器函数的实质,递归函数的调用,我们再来看八皇后的棋子摆放的函数,为了方便观察,将‘八皇后’改为‘四皇后’,就是只算4✖️4棋盘上放置4个棋子
def arrange_queen(num, queen_tup=list()):
"""
:param num:棋盘的的行数,当然数值也等于棋盘的列数
:param queen_tup: 设置一个空队列,用于保存符合规则的棋子的信息
"""
for new_queen in range(num): # 遍历一行棋子的每一列
if is_rule(queen_tup, new_queen): # 判断是否冲突
if len(queen_tup) == num-1: # 判断是否是最后一行
yield [new_queen] # yield关键字
else:
# 若果不是最后一行,递归函数接着放置棋子
for result in arrange_queen(num, queen_tup+[new_queen]):
yield [new_queen] + result
for i in arrange_queen(4):
print(i)
执行结果是
[1,3,0,2]
[2,0,3,1]
下面描述一下函数的执行过程:
1.放置第一行棋子。函数第一次被调用,传递参数4,空列表。放置棋子在第一行第一列,判断棋子放置符合规则,判断不是最后一行,将棋子位置信息放入列表,同时生成新的列表[0]
2.放置第二行棋子。函数第二次被调用,传递参数4,列表[0]。放置棋子在第二行第一列,判断棋子不符合规则,接着放置棋子在第二行第二列,判断棋子不符合规则,再放置棋子在第二行第三列,判断符合规则,将棋子位置信息放入列表,同时生成新的列表[0,2]
3.放置第三行棋子。函数第三次被调用,传递参数4,列表[0,2]。放置棋子在第三行第一列,判断棋子不符合规则,接着放置棋子在第三行第二列,判断不符合规则,再放置棋子到第三行第三列,判断不符合规则,再放置棋子到第三行第四列,判断还是不符合规则。第三次函数调用结束
4.回到函数第二次被调用的地方,第二次被调用的函数接着放置棋子,上一次放置到了第三列,这次放到第四列,判断符合规则,将棋子位置信息放入列表,同时生成新的列表[0,3]
5.函数被调用,用于放置第三行,从第一列再依次判断到最后一列,如果符合规则,放入棋子信息,同时生成新的列表[0,3,1]
6.函数被调用,用于放置第四行,从第一列判断到最后一列,都不符合规则,函数执行完毕,回到上一级
.......
N.当前三行的棋子放入都符合规则,而且第四行也符合规则了,此时第一次遇到yield关键字,第四级函数暂停,将棋子信息放入列表[2],返回到第三级,第三级函数也将第三级符合规则的棋子信息放入列表,同时与第四级返回的列表相加,得到一个新的列表,然后遇到第三级函数的关键字函数yield,第三级函数暂停,返回了[0,2]到第二级函数.......直到第一级函数暂停,返回结果[1,3,0,2],打印结果
然后第一级函数接着执行,驱动二级函数执行,二级驱动三级执行,三级驱动四级执行....
直到所有结果打印完毕,整个函数执行完毕
整个代码为
def is_rule(queen_tup, new_queen):
"""判断棋子是否符合规则"""
for index, queen in enumerate(queen_tup):
if abs(new_queen-queen) in (len(queen_tup)-index, 0): # 判断表达式
return False
return True
def arrange_queen(num, queen_tup=list()):
"""
:param num:棋盘的的行数,当然数值也等于棋盘的列数
:param queen_tup: 设置一个空队列,用于保存符合规则的棋子的信息
"""
for new_queen in range(num): # 遍历一行棋子的每一列
if is_rule(queen_tup, new_queen): # 判断是否冲突
if len(queen_tup) == num-1: # 判断是否是最后一行
yield [new_queen] # yield关键字
else:
# 若果不是最后一行,递归函数接着放置棋子
for result in arrange_queen(num, queen_tup+[new_queen]):
yield [new_queen] + result
for i in arrange_queen(8):
print(i)
整个代码最终要的就是递归回溯的思想,如果能真正的明白,不用用什么语法或者写什么样的函数,都能轻松解决这个八皇后的问题
接下来我贴出一个八皇后的的终极版(下面的代码来源百度百科),不使用yield关键字的。可以自行理解一下
def queen(A, cur=0):
if cur == len(A):
print(A)
return 0
for col in range(len(A)):
A[cur], flag = col, True
for row in range(cur):
if A[row] == col or abs(col - A[row]) == cur - row:
flag = False
break
if flag:
queen(A, cur+1)
queen([None]*8)
八皇后的所有解
[0, 4, 7, 5, 2, 6, 1, 3]
[0, 5, 7, 2, 6, 3, 1, 4]
[0, 6, 3, 5, 7, 1, 4, 2]
[0, 6, 4, 7, 1, 3, 5, 2]
[1, 3, 5, 7, 2, 0, 6, 4]
[1, 4, 6, 0, 2, 7, 5, 3]
[1, 4, 6, 3, 0, 7, 5, 2]
[1, 5, 0, 6, 3, 7, 2, 4]
[1, 5, 7, 2, 0, 3, 6, 4]
[1, 6, 2, 5, 7, 4, 0, 3]
[1, 6, 4, 7, 0, 3, 5, 2]
[1, 7, 5, 0, 2, 4, 6, 3]
[2, 0, 6, 4, 7, 1, 3, 5]
[2, 4, 1, 7, 0, 6, 3, 5]
[2, 4, 1, 7, 5, 3, 6, 0]
[2, 4, 6, 0, 3, 1, 7, 5]
[2, 4, 7, 3, 0, 6, 1, 5]
[2, 5, 1, 4, 7, 0, 6, 3]
[2, 5, 1, 6, 0, 3, 7, 4]
[2, 5, 1, 6, 4, 0, 7, 3]
[2, 5, 3, 0, 7, 4, 6, 1]
[2, 5, 3, 1, 7, 4, 6, 0]
[2, 5, 7, 0, 3, 6, 4, 1]
[2, 5, 7, 0, 4, 6, 1, 3]
[2, 5, 7, 1, 3, 0, 6, 4]
[2, 6, 1, 7, 4, 0, 3, 5]
[2, 6, 1, 7, 5, 3, 0, 4]
[2, 7, 3, 6, 0, 5, 1, 4]
[3, 0, 4, 7, 1, 6, 2, 5]
[3, 0, 4, 7, 5, 2, 6, 1]
[3, 1, 4, 7, 5, 0, 2, 6]
[3, 1, 6, 2, 5, 7, 0, 4]
[3, 1, 6, 2, 5, 7, 4, 0]
[3, 1, 6, 4, 0, 7, 5, 2]
[3, 1, 7, 4, 6, 0, 2, 5]
[3, 1, 7, 5, 0, 2, 4, 6]
[3, 5, 0, 4, 1, 7, 2, 6]
[3, 5, 7, 1, 6, 0, 2, 4]
[3, 5, 7, 2, 0, 6, 4, 1]
[3, 6, 0, 7, 4, 1, 5, 2]
[3, 6, 2, 7, 1, 4, 0, 5]
[3, 6, 4, 1, 5, 0, 2, 7]
[3, 6, 4, 2, 0, 5, 7, 1]
[3, 7, 0, 2, 5, 1, 6, 4]
[3, 7, 0, 4, 6, 1, 5, 2]
[3, 7, 4, 2, 0, 6, 1, 5]
[4, 0, 3, 5, 7, 1, 6, 2]
[4, 0, 7, 3, 1, 6, 2, 5]
[4, 0, 7, 5, 2, 6, 1, 3]
[4, 1, 3, 5, 7, 2, 0, 6]
[4, 1, 3, 6, 2, 7, 5, 0]
[4, 1, 5, 0, 6, 3, 7, 2]
[4, 1, 7, 0, 3, 6, 2, 5]
[4, 2, 0, 5, 7, 1, 3, 6]
[4, 2, 0, 6, 1, 7, 5, 3]
[4, 2, 7, 3, 6, 0, 5, 1]
[4, 6, 0, 2, 7, 5, 3, 1]
[4, 6, 0, 3, 1, 7, 5, 2]
[4, 6, 1, 3, 7, 0, 2, 5]
[4, 6, 1, 5, 2, 0, 3, 7]
[4, 6, 1, 5, 2, 0, 7, 3]
[4, 6, 3, 0, 2, 7, 5, 1]
[4, 7, 3, 0, 2, 5, 1, 6]
[4, 7, 3, 0, 6, 1, 5, 2]
[5, 0, 4, 1, 7, 2, 6, 3]
[5, 1, 6, 0, 2, 4, 7, 3]
[5, 1, 6, 0, 3, 7, 4, 2]
[5, 2, 0, 6, 4, 7, 1, 3]
[5, 2, 0, 7, 3, 1, 6, 4]
[5, 2, 0, 7, 4, 1, 3, 6]
[5, 2, 4, 6, 0, 3, 1, 7]
[5, 2, 4, 7, 0, 3, 1, 6]
[5, 2, 6, 1, 3, 7, 0, 4]
[5, 2, 6, 1, 7, 4, 0, 3]
[5, 2, 6, 3, 0, 7, 1, 4]
[5, 3, 0, 4, 7, 1, 6, 2]
[5, 3, 1, 7, 4, 6, 0, 2]
[5, 3, 6, 0, 2, 4, 1, 7]
[5, 3, 6, 0, 7, 1, 4, 2]
[5, 7, 1, 3, 0, 6, 4, 2]
[6, 0, 2, 7, 5, 3, 1, 4]
[6, 1, 3, 0, 7, 4, 2, 5]
[6, 1, 5, 2, 0, 3, 7, 4]
[6, 2, 0, 5, 7, 4, 1, 3]
[6, 2, 7, 1, 4, 0, 5, 3]
[6, 3, 1, 4, 7, 0, 2, 5]
[6, 3, 1, 7, 5, 0, 2, 4]
[6, 4, 2, 0, 5, 7, 1, 3]
[7, 1, 3, 0, 6, 4, 2, 5]
[7, 1, 4, 2, 0, 6, 3, 5]
[7, 2, 0, 5, 1, 4, 6, 3]
[7, 3, 0, 2, 5, 1, 6, 4]
最后最后,对比其他语言解决八皇后的代码量
作者:NicolasLinJieChuang
来源:CSDN
原文:https://blog.csdn.net/weixin_41865447/article/details/80034433
版权声明:本文为转载文章
Python 八皇后问题的更多相关文章
- 带你轻而易举的学习python——八皇后问题
首先我们来看一下这个著名的八皇后问题 八皇后问题:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 在这个问题提出之后人们又将 ...
- 流动python - 八皇后问题简单解决方案
思维: 使用DFS. 坐标的一维阵列的表达,在标行,元素列.A[i]=j它表示第一i女王就行了j柱. 以穿越线,由线(从上到下),决定其列(左到右),所以,不要推断冲突的行,和主斜线副斜线冲突. (行 ...
- Python学习二(生成器和八皇后算法)
看书看到迭代器和生成器了,一般的使用是没什么问题的,不过很多时候并不能用的很习惯 书中例举了经典的八皇后问题,作为一个程序员怎么能够放过做题的机会呢,于是乎先自己来一遍,于是有了下面这个ugly的代码 ...
- Python解决八皇后问题
最近看Python看得都不用tab键了,哈哈.今天看了一个经典问题--八皇后问题,说实话,以前学C.C++的时候有这个问题,但是当时不爱学,没搞会,后来算法课上又碰到,只是学会了思想,应该是学回溯法的 ...
- 八皇后,回溯与递归(Python实现)
八皇后问题是十九世纪著名的数学家高斯1850年提出 .以下为python语句的八皇后代码,摘自<Python基础教程>,代码相对于其他语言,来得短小且一次性可以打印出92种结果.同时可以扩 ...
- 【算法导论】八皇后问题的算法实现(C、MATLAB、Python版)
八皇后问题是一道经典的回溯问题.问题描述如下:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8*8个方格),使它们谁也不能被吃掉? 看到这个问题,最容易想 ...
- 【算法】八皇后问题 Python实现
[八皇后问题] 问题: 国际象棋棋盘是8 * 8的方格,每个方格里放一个棋子.皇后这种棋子可以攻击同一行或者同一列或者斜线(左上左下右上右下四个方向)上的棋子.在一个棋盘上如果要放八个皇后,使得她们互 ...
- 八皇后问题的Python实现和C#实现
看到八皇后问题的解决思路, 感觉很喜欢. 我用C#实现的版本之前贴在了百度百科上(https://baike.baidu.com/item/%E5%85%AB%E7%9A%87%E5%90%8E%E9 ...
- Python解决八皇后问题的代码【解读】
八皇后问题 来自于西方象棋(现在叫 国际象棋,英文chess),详情可见百度百科. 在西方象棋中,有一种叫做皇后的棋子,在棋盘上,如果双方的皇后在同一行.同一列或同一斜线上,就会互相攻击. 八皇后问题 ...
随机推荐
- NIO原理及案例使用
什么是NIO Java提供了一个叫作NIO(New I/O)的第二个I/O系统,NIO提供了与标准I/O API不同的I/O处理方式.它是Java用来替代传统I/O API(自Java 1.4以来). ...
- js 实现数据结构 -- 栈
原文: 在 Javascript 中学习数据结构与算法. 概念: 栈是一种遵从先进后出 (LIFO) 原则的有序集合:新添加的或待删除的元素都保存在栈的末尾,称作栈顶,另一端为栈底.在栈里,新元素都靠 ...
- webpack4 学习 --- webpack和webpack-dev-server
以前了解过webpack2, 所以对webpack 不是很陌生,就直接入主题吧.新建一个文件夹,就叫它webpack-tut吧.然后在文件中新建一个src 文件夹,存放我们的源文件,再在src 文件夹 ...
- jQuery之标签操作和返回顶部、登录验证、全选反选、克隆示例
一.样式操作 1.JQ中的样式类 somenode.addClass();// 添加指定的CSS类名. somenode.removeClass();// 移除指定的CSS类名. somenode.h ...
- Django+Vue打造购物网站(七)
个人中心功能开发 drf文档注释 http://www.django-rest-framework.org/topics/documenting-your-api/ 动态设置serializer和pe ...
- ☆ [POI2007] ZAP-Queries 「莫比乌斯反演」
题目类型:莫比乌斯反演 传送门:>Here< 题意:求有多少对正整数对\((a,b)\),满足\(0<a<A\),\(0<b<B\),\(gcd(a,b)=d\) ...
- poj 2955 Brackets (区间dp 括号匹配)
Description We give the following inductive definition of a “regular brackets” sequence: the empty s ...
- 题解-洛谷P1184 高手之在一起
https://www.luogu.org/problemnew/show/P1184 (题目出处) 见到地名,自然就想到字符串了.可以从第一天开始,将她的位置与高手方便取得地方一一比较,(char字 ...
- linux串口编程设置(转载)
(转载)在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串 ...
- 教你如何写出高效整洁的 css 代码——css优化(转载)
css 写起来并不难,但在大型项目中,就变得难以管理,特别是不同的人在 css 书写风格上稍有不同,团队上就更加难以沟通,为此总结了一些如何实现高效整洁的 css 代码原则. css 优化的原则 1. ...