刚开始学习python,八皇后问题困扰了半天,各种查资料理解,以下是根据自己对代码的理解,欢迎大家批评指正,非常感谢!本文代码来自《Python基础教程(第2版.修订版)》 非常感谢大佬们提出批评建议!非常感谢!
一、皇后在棋盘上的状态表示
由元组表示皇后的位置。每个元组中元素指示相应行的皇后的位置(也就是列)。例如:如果state[0] == 3,就表示,第一个皇后位于棋盘的第1行的第4列。state中存储的是皇后的“列”位置信息。如:state=(1,3,0),即:state[0]=1,state[1]=3,state[2]=0,第1个皇后位于1行2列,第2个皇后位于2行4列,第3个皇后位于3行1列,也就是说,state存储的是皇后的 “列”信息,“索引参数”是行信息。
先附上实现八皇后的代码,下来分部来理解学习。
def conflict(state,nextX): nextY = len(state) for i in range(nextY): if abs(state[i]-nextX) in (0,nextY-i): return True return False def queens(num=8,state=()): for pos in range(num): if not conflict(state,pos): if len(state) == num-1: yield (pos,) else: for result in queens(num, state + (pos,)): yield (pos,) + result def prettyprint(solution): def line(pos,length = len(solution)): return '. '*pos + '* ' + '. '*(length - pos - 1) for pos in solution: print(line(pos))
调用:
import random prettyprint(random.choice(list(queens(8))))
![]()
二、判断下一个的皇后的位置会不会有冲突
#1.conflict(state,nextX) #有冲突:Ture,无冲突:False def conflict(state,nextX): nextY = len(state) for i in range(nextY): if abs(state[i]-nextX) in (0,nextY-i): return True return Fals
什么是冲突?
新的皇后与之前的皇后 同列,同行,同对角线为冲突信息。从冲突想办法转换为数学关系式:
根据我自身学习遇到的困难,为了更好的理解代码,现用4皇后来说明上述代码。
conflict函数中理解:
if abs(state[i]-nextX) in (0,nextY-i):
是关键,首先明确state[i],nextX,nextY,i的含义。为了更好的说明,结合下面的代码(已知前n-1皇后的位置,如何确定最一个(第n个)皇后的位置):
def queens(num,state): if len(state) == num-1: for pos in range(num): if not conflict(state,pos): yield pos
以4皇后问题来说明 if abs(state[i]-nextX) in (0,nextY-i): 代码,如图:
![]()
前3个皇后位置已经确定,“列”信息以元组的形式(1,3,0)存储。获得第4个皇后位置调用语句:
yield pos,返回的是列的值,输出[ 2 ],3行(前3个皇后已经确定,现在是确定最后一个皇后的位置)2列list(queens(4,(1,3,0)))
再回到if abs(state[i]-nextX) in (0,nextY-i): 代码上来,queens(4,(1,3,0)),queens参数num(这里等于4)是皇后总数,state元组是“列”信息。
queens中if len(state) == num-1:(意思到了最后一行),for pos in range(num),pos由0,1,2,3迭代,可知表示的是最后一行的第“0,1,2,3列”;conflict(state,nextX)中,nextY = len(state) (nextY=3),for i in range(nextY),由state[ i ] 知 i 表示的是“行”;最后:i——行,nextX(也就是pos,是从0,1,2,3列迭代)——列,state[i]——第i行的第state[i]列,nextY——目前皇后数的总行数(这里已经3个皇后,所以是3行)。
新的皇后与已有的皇后“同列”满足——state[ i ] = nextX,即:state[i] - nextX = 0——(1式);
新的皇后与已有的皇后“对角线”满足——右对角,比如(0,1)(1,2),(2,3)有后列-前列=后行-前行,即:nextX - state[i]= nextY – i;类似也可求得左对角线的情况:state[i] - nextX = nextY - i,也就是abs(state[i]-nextX) = nextY - i ——(2式)
综合(1式)与(2式),可知判定条件为:只要state[i] - nextX在(0,nextY - i)之间,就可知新的皇后与已有的皇后 发生了位置冲突。所以就有我们看到的代码:if abs(state[i]-nextX) in (0,nextY-i):
三、递归
conflict函数理解后,下来看看解决方案中的递归部分。
那么递归调用会得到什么结果呢?我们想得到的是所有底层皇后的位置,是这样的。我们将位置信息作为一个元组返回。在这种情况下,需要修改基本情况也返回一个元组(长度为1)。
这样一来,程序从前面的皇后得到了包含位置信息的元组,并且要为后面的皇后提供当前皇后的每种合法的位置信息。为了让程序继续运行下去,接下来需要做的就是把当前的位置信息添加到元组中并传递给后面的皇后。
def queens(num=8,state=()): for pos in range(num): if not conflict(state,pos): #不发生位置冲突 if len(state) == num-1: #如果现在的行数=皇后总数-1, yield (pos,) else: #没有到确定第8个皇后的位置时 for result in queens(num, state + (pos,)): yield (pos,) + result
代码分两大部分:if 和 else 。
if中,意义很明显,是在前n-1个位置确定后 用来确定 最后一个(第n个)的位置。
else中, for result in queens(num, state + (pos,)): 可以这样理解,没错,还是遍历当前行的所有位置,(因为第一行放皇后,第二行放皇后,第三行放皇后。。。,恰好到不是最后一行的当前行)。执行到此处时,进入queens()生产器,执行2行代码的for pos in range(num): pos从0-7迭代,不发生位置冲突且又不是最后一行,执行到yield (pos,) + result时,返回(pos,)=(pos,) + result,将(pos,)赋值给result。state元组和(pos,)相加,也就是说yield (pos,) + result这一步,已经将之前的皇后位置信息写入了state元组。继续以现有皇后的位置信息进行递归确定下一行新皇后的位置。
yield (pos,) + result,pos是当前行不与之前的皇后们冲突的位置(那就是当前行的“第pos列”),本质是取值0-7的整型,而在小括号里的pos再加上一个“,”表明(pos,)是只有一个元素的元组,而result也是元组,这样pos和result才能相加,元组无法和数值类型的相加。
四、显示
最后一部分prettyprint()函数是将皇后位置以类似于棋盘模式显示出来。
len(list(queens(8))) #显示八皇后共有多少种解决方法