Python学习二(生成器和八皇后算法)

看书看到迭代器和生成器了,一般的使用是没什么问题的,不过很多时候并不能用的很习惯

书中例举了经典的八皇后问题,作为一个程序员怎么能够放过做题的机会呢,于是乎先自己来一遍,于是有了下面这个ugly的代码

def table(m, lst):
'''绘制m列的棋盘,每行有个皇后旗子'''
head = '┌' + '─┬' * (m-1) + '─┐'
row = lambda x: '│' + ' │' * x + '╳│' + ' │' * (m - x - 1)
trow = '├' + '─┼' * (m-1) + '─┤'
tail = '└' + '─┴' * (m-1) + '─┘'
print(head)
for i, n in zip(range(len(lst)), lst):
print (row(n))
print (trow) if i < len(lst) - 1 else print (tail)
queens = [0]*8
def rightqueen(lst, m):
'''判断lst的所有皇后是否与下一行的m位置皇后干涉,不干涉则返回True'''
for i, n in zip(range(len(lst)), lst):
if(n == m or len(lst) - i == m - n or len(lst) - i == n - m):
return False
return True
def calqueen(lst, n):
'''求解第n个皇后与之前的皇后不干涉
如果第n行成功则求解n+1行,否则求解n-1行且复位n行'''
for i in range(lst[n], 8):
if rightqueen(lst[:n], i):
lst[n] = i
calqueen.count += 1
print(calqueen.count)
table(8, lst[:n+1])
if n < 7: calqueen(lst, n+1)
break
else:
#如果遍历8个之后仍然没有解则重新求解上一行
lst[n] = 0
lst[n-1] += 1
print('求解上一行')
calqueen(lst, n-1)
calqueen.count = 0
calqueen(queens, 0)

前面那个table函数只是用来绘制棋盘的,写完后感觉Python确实是很简洁的语言,当然可以更简洁,现在我的代码风格还保留很强的C语言风格,这个转变估计还需要一段时间

说他ugly倒并不是因为他没有使用迭代器,主要的问题在于函数的两个分支都使用了迭代,只有达到第八层时才会结束迭代,这个代码迭代次数要达到100多次,这样多的迭代其内存占用以及运行效率都是不太好的

因此改进版的代码应该是在失败的时候返回运行结果以便减少迭代次数,调整后的代码如下:

def table(m, lst):
'''绘制m列的棋盘,每行有个皇后旗子'''
head = '┌' + '─┬' * (m-1) + '─┐'
row = lambda x: '│' + ' │' * x + '╳│' + ' │' * (m - x - 1)
trow = '├' + '─┼' * (m-1) + '─┤'
tail = '└' + '─┴' * (m-1) + '─┘'
print(head)
for i, n in zip(range(len(lst)), lst):
print (row(n))
print (trow) if i < len(lst) - 1 else print (tail)
queens = [0]*8
def rightqueen(lst, m):
'''判断lst的所有皇后是否与下一行的m位置皇后干涉,不干涉则返回True'''
for i, n in zip(range(len(lst)), lst):
if(n == m or len(lst) - i == m - n or len(lst) - i == n - m):
return False
return True
def calqueen(lst, n):
'''求解第n个皇后与之前的皇后不干涉
如果第n行成功则求解n+1行,否则求解n-1行且复位n行'''
for i in range(8):
if rightqueen(lst[:n], i):
lst[n] = i
calqueen.count += 1
print(calqueen.count)
table(8, lst[:n+1])
if n < 7:
if not calqueen(lst, n+1): continue
return True
else:
#如果遍历8个之后仍然没有解则重新求解上一行
print('求解上一行')
calqueen.count -= 1
return False
calqueen.count = 0
calqueen(queens, 0)

减少了迭代次数后至少程序是比较合理的了,不过在看过使用生成器,迭代器的代码之后还是觉得我对Python以及迭代器编程的感觉太少了,为了增强感觉,合上书按照自己的理解又盲写了一遍,当然代码跟书上的不太一致了,但思想差不多

def table(m, lst):
'''绘制m列的棋盘,每行有个皇后旗子'''
head = '┌' + '─┬' * (m-1) + '─┐'
row = lambda x: '│' + ' │' * x + '╳│' + ' │' * (m - x - 1)
trow = '├' + '─┼' * (m-1) + '─┤'
tail = '└' + '─┴' * (m-1) + '─┘'
print(head)
for i, n in zip(range(len(lst)), lst):
print (row(n))
print (trow) if i < len(lst) - 1 else print (tail)
def rightqueen(lst, m):
'''判断lst的所有皇后是否与下一行的m位置皇后干涉,不干涉则返回True'''
for i, n in zip(range(len(lst)), lst):
if(n - m in (0, len(lst) - i, i - len(lst))):
return False
return True
def calqueen(lst, n):
'''求解第n个皇后与之前的皇后不干涉
只在第八层提交数据,其余每一层都是把本层的所有可能和后面的所有可能相加'''
for i in range(8):
calqueen.total += 1
if rightqueen(lst, i):
calqueen.count += 1
if n == 7:
calqueen.number += 1
print(calqueen.number, calqueen.count, calqueen.total)
yield lst + [i]
else:
for l in calqueen(lst + [i], n + 1):
yield l
calqueen.total = 0
calqueen.count = 0
calqueen.number = 0
for l in calqueen([], 0):
table(8, l)

事实上两个函数的核心代码只有13行,使用in和元组来替代3个条件判断,用迭代器替代对函数返回值的判断,因为如果迭代器为空,则上层调用函数遍历就会失败,自动解决了我原来算法的问题

而且这个程序能够遍历出92个八皇后的解法,虽然我之前的代码改改应该也能实现,但是不会有这么精简

因为生成器的存在,使得迭代器在使用时非常的方便,后期在处理一些需要循环层进迭代的方法时也应该优先考虑生成器,迭代器的方式.

上一篇:不同服务器数据库之间的数据定时同步到sql server (支持mysql,oracle数据库同步到sql server)


下一篇:OpenJudge1700:八皇后问题 //不属于基本法的基本玩意