做递归题的时候,做到了八皇后问题,又再去回顾了一下生成器(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)