N Queens:由Python生成器实现的回溯解决方案

时间:2014-07-31 07:15:02

标签: python generator n-queens

这台发电机如何工作?它显然在外部for循环中改变了。生成器是否在for循环期间进行评估?

代码改编自http://rosettacode.org/wiki/N-queens_problem#Python
如果我导入代码,则显示:
[[1,3,0,2],[2,0,3,1]]

在代码之前说:
"对上面代码的一个非常简单的改变(将列表理解改为生成器表达式)产生了一个回溯解决方案:"

class Solution:
    # @return a list of lists of string
    def under_attack(self, col, queens):
        return col in queens or any(abs(col - x) == len(queens)-i for i,x in enumerate(queens))

    def solve(self,n):
        solutions = [[]]
        for row in range(n):
            solutions = (solution+[i] for solution in solutions
                                      for i in range(n)    
                                      if not self.under_attack(i, solution))
        print(list(solutions))
        return solutions


A=Solution()
list(A.solve(4))

1 个答案:

答案 0 :(得分:2)

该算法的工作方式与使用列表推导的算法完全相同,唯一的区别在于它使用了generator表达式 - 实际上,它创建了一个生成器,其中嵌套了更多的生成器的!因此,不是一次列出所有可能的解决方案,而是根据需要懒惰地生成更多解决方案。

每次调用under_attack时,您可以通过增加一些全局定义的计数器变量来轻松验证这一点,并查看此计数器增加了多少次。

gen = solve(8) # just create the generators, under_attack called 0 times
next(gen)      # first solution,             under_attack called 876 times
list(gen)      # all remaining solutions,    under_attack called 15720

如果您执行list(gen),则会生成所有解决方案,而next(gen)只计算一个。


现在为实际算法。这在非生成器版本中更容易解释。此外,不需要课程。

def under_attack(col, queens):
    return col in queens or any(abs(col - x) == len(queens)-i         # (1)
                                for i,x in enumerate(queens))         # (2)

def solve(n):
    solutions = [[]]
    for row in range(n):                                              # (3)
        solutions = [solution+[i] for solution in solutions           # (4)
                                  for i in range(n)                   # (5)
                                  if not under_attack(i, solution)]   # (6)
        print solutions # remove this when using generator!           # (7)
    return solutions

首先,under_attack功能。给定一个女王的列和已经放置的女王的列,这将检查同一列(1or之前)是否已有女王,或any是否为2女王在同一对角线上(solve)。

现在为solutions:这会循环遍历棋盘的所有行,每行放置一个女王。 [[3,1]]是部分解决方案的列表,其中每个子列表包含到目前为止放置的皇后的列。 3意味着一个(部分)解决方案,其中第0行,第3列中有一个女王,第1行第1行中有一个。现在,对于每一行(4),它会更新部分解决方案迄今为止行(5)的部分解决方案的每个组合以及新女王(6)的列,其中女王不会受到攻击(7)。

我选择非生成器版本来解释的原因是这样我们可以在循环的每次迭代中打印部分解决方案(n=4)。使用生成器这是不可能的,因为只打印列表将耗尽生成器,并且将不再有部分解决方案可以构建。对于[[0], [1], [2], [3]] # 1st queen [[0, 2], [0, 3], [1, 3], [2, 0], [3, 0], [3, 1]] # 2nd queen [[0, 3, 1], [1, 3, 0], [2, 0, 3], [3, 0, 2]] # 3rd queen [ [1, 3, 0, 2], [2, 0, 3, 1]] # 4th queen

{{1}}

第一个女王可以放在任何一列中。这已经限制了第二女王的可能位置,现在只有一两个可能的位置。第三个女王甚至更受限制,对于女王1和2的某些职位,根本找不到任何职位。最后,放置了最后一个女王,这就是解决方案集。