做递归题的时候,做到了八皇后问题,又再去回顾了一下生成器(generator)的用法。以下仅是个人学习笔记,因此着重记录的都是本人在学习过程中的理解难点。欢迎讨论指点^^
八皇后问题:八皇后问题,一个古老而著名的问题,是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
展现计算结果是以列表的形式,列表的索引为行,对应的索引值为列。
递归回溯:
def queens(ls,cur=0): # ls初始为一个含有n个数据项的列表,cur为当前行
if cur == len(ls): # 基本结束条件
print(ls)
return 0 #没懂这里return 0的作用是什么,不加这一行程序也能运行
else:
for col in range(len(ls)):
ls[cur],flag = col,True #在当前行cur遍历所有列
for row in range(cur): # 遍历cur之前的所有行
if abs(ls[row]-col) in (0,cur-row): #判断是否重复
flag = False
if flag :
queens(ls,cur+1) #继续对下一行调用queens()
queens([None]*8)
上述代码中,从0行开始,一行一行进行“皇后”摆放。else中,遍历当前行(cur)的全部列0~7(col),判断是否与已经完成摆放的行(row)的“皇后”位置相冲突,即在同一列(ls[row]-col=0)或者在对角线上(ls[row]-col=cur-row),只要有一个冲突,flag变为False。当遍历完之前的行以后,flag仍为True时,表示与前面所有位置都不冲突,此时的col是符合的,并且已经在代码第7行记录了位置(ls[cur]=col),再对下一行(cur+1)递归调用。
基本结束条件是,当前行数cur=8时(棋盘行列序号为0-7),表示已经全部结束摆放了,摆放位置的结果全部记录在列表ls中,直接打印出来。
递归生成器的方法:
def conflict(nextX,queens_ls):
nextY = len(queens_ls)
for i in range(len(queens_ls)):
if abs(nextX-queens_ls[i]) in (0,nextY-i):
return True
return False
def queens(num,queens_ls=list()): #初始构造一个空的queens_ls列表,方便后面添加元素
for col in range(num):
if not conflict(col,queens_ls):
if len(queens_ls) == num-1: #基本结束条件,判断当前正在摆放的是否是最后一行。
yield [col] #最后一行直接返回该列
else:
for result in queens(num,queens_ls+[col]):
yield [col]+result
遍历生成器:
for i in queens(8):
print(i)
运行结果:
[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]
生成器(generator)是指含有yield的函数,注意:yield仅在函数中可以使用。函数运行到yield时,返回yield后面的数据,随即停止运行。在下一次调用该函数,从上次结束的地方开始运行。generator需要用next()函数来调用。
如:
def foo():
print('第一次调用')
yield 1
print('第二次调用')
yield 2
f = yield 3
print('f:',f)
>>> next(g) #第一次调用,遇到yield停止
第一次调用
1
>>> next(g) #第二次调用,遇到yield停止
第二次调用
2
>>> next(g) #第三次调用,遇到yield停止
3
>>> next(g) #第四次调用下面没有遇到yield了,就会报错,停止迭代。虽然f有赋值操作,但是3已经被yield返回出去了,所以f是空值。
f: None
Traceback (most recent call last):
File "<pyshell#33>", line 1, in <module>
next(g)
StopIteration
g.send()可以用来赋值给f,但必须是在函数进行到f右边的数被yield出去以后使用。
print(next(g))
print(next(g))
print(next(g))
print(g.send('赋值'))
输出如下:
第一次调用
1
第二次调用
2
3
f: 赋值 #g.send()自带next(),因此赋值之后紧接着自动next(g)