queens

八皇后问题,回溯和递归

$ # -*- coding:utf-8 -*-
$ #python默认为ascii编码,中文编码可以用utf-8
$
$ import random
$ def conflict(state,col):
$     row=len(state)
$     for i in range(row):
$          if abs(state[i]-col) in (0,row-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 queenprint(soultion):   
$     def line(pos,length=len(solution)):
$          return '. '*(pos)+'X'+'. '*(length-pos-1)
$     for pos in solution:
$          print line(pos)
$        
$ for solution in list(queens(8)):
$     print solution
$
$ print ' total number is '+str(len(list(queens())))'
$ print ' one of the range is:\n'
$ 
$ queenprint(random.choice(list(queens())))

源码解析

主要利用冲突函数检测冲突,如果冲突则回溯,递归用到python的yield语句,该语句涉及python的生成器

冲突函数

$ def  conflict(state,col):
$     #冲突函数,row为行,col为列(不论行和列都是从0开始的)
$    row=len(state)
$     for i in range(row):
$          if abs(state[i]-col) in (0,row-i):
$             return True
$    return False
$注释:state为已知的皇后的状态,类型是一个元组,例如(7,3,0,2,5,1,6,4),元组是不可变对象,一经创建不能修改,元组是创建生成器的一种方法(第一行的皇后在第七列,第二行的皇后在第三列,等等)

假设第一行到第三行的皇后都没冲突,这个时候要检测第四行皇后是否冲突。如第一行皇后在第五列,第二行皇后在第八列,第三行皇后在第四列,检验第四行皇后放在哪一列不会冲突。

$ * * * * X * * *
$ * * * * * * * X
$ * * * X * * * *
$ 这时state=(4,7,3),col=?

1 得出目前没冲突的行数row

$ row = len(state)

2 从1~row行依次检测已知的皇后是否与row+1行皇后有冲突

$ for i in range(row):

3 如果row+1行皇后所在的列col与其它行皇后的列相同或处于对角线,则冲突

$ if abs(state[i]-col) in (0,row-i):#判断是否冲突
$    return True

以上语句翻译为(其它已知的皇后所在的列-要求检测的皇后所在的列)等于0或者等于(row-i)则冲突

傻瓜式教学:第一行与第四行冲突,要么在同一列,要么在对角线,当对角线列数相差3,因为第一行与第二行对角线相差1,第二行与第三行对角线相差1,则第一行与第三行对角线相差2,以此类推,第一行与第四行冲突,则相差3

当第四行所在列col=4,这时abs(state[0]-4) in (0,3-0)为真,因为4-4=0,如图:

$ * * * * X * * *
$ * * * * * * * X
$ * * * X * * * *
$ * * * * X * * *     同列冲突

当第四行所在列col=2,这时abs(state[2]-2) in (0,3-2)为真,因为abs(3-2)=1,如:

$ * * * * X * * *
$ * * * * * * * X
$ * * * X * * * *
$ * * X * * * * *     对角线冲突

生成器函数:

$ def queen(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

生成器:通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且,创建一个包含100万个元素的列表,不仅占用很到的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了,所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,python中,这种一遍循环一边计算的机制,称为生成器

步骤

1 下面该语句为构建所有皇后摆放情况打下基础,可以尝试所有情况

$ for pos in range(num):

2 如果不冲突,则递归构造棋盘

$ if not conflict(state,pos):

3 如果棋盘状态state已经等于num-1,即到达倒数第二行,而这时最后一行皇后又没冲突,直接yield,打出其位置(pos,),python在显示只有1个元素的元组时,也会加上一个逗号,以免你误解成数学计算意义上的括号,否则递归,打印(pos,)+result

$ if len(state)==num-1:
$     yield(pos,)
$ else:
$    for result in queens(num,state+(pos,)):
$        yield (pos,)+result

傻瓜式教学:例如pos=0,第0行放在第0列,这时不会冲突,但是不会进入if,因为还没到达倒数第二行,进入else后,再调用queens(num,state+(pos,)),这时进入第二行,再次递归展开则是queens(num,state+(pos,)+(pos,)),到达最后一行时返回(pos,),再返回倒数第二行,再返回倒数第三行,最后到达最开始那层(pos,)+result, pos为第0行皇后所在列,result包含第一行皇后和另一个result

打印所有结果

$ for solution in queens(8):
$     print solution

queens(8)因为生成器函数的for循环,每一次循环都会yield一个元组出来,所以有很多种情况,可以把它全部打印出来
也可以用list包装列表在统计一下多少种数目

$ print 'total number is '+str(len(list(queens())))

随机优美打印一个棋盘情况

$ print ' one of the range is:\n'
$ queenprint(random.choice(list(quees())))

文章作者: 阿培
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 阿培 !
 上一篇
字符串内置函数之-strip、split函数 字符串内置函数之-strip、split函数
strip函数解析声明: s为字符串,rm为要删除的字符序列 >>> s.strip(rm): 删除s字符串中开头、结尾处,位于rm删除序列的字符 >>> s.lstrip(rm): 删除s字符串中开头处,位于rm删除序列的字符 >>>
2017-06-10
下一篇 
linux-concurrent linux-concurrent
查看Web服务器(Nginx Apache)的并发请求数及其TCP连接状态:$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' $ netstat -
2017-05-03
  目录