Python3的八皇后问题

一、生成器的回溯(Generator Traceback)

对于逐步得到结果的复杂递归算法,非常适用生成器来实现。

要在不使用生成器的情况下实现这些算法,通常必须通过额外的参数来传递部分结果,让递归能够接着往下计算。

通过使用生成器,所有的递归调用都只需生成其负责部分的结果。

二、问题

八皇后:你需要将8个皇后放在棋盘上,条件是任何一个皇后都不能威胁其他皇后,即不能在同一列和对角线。

这是一个典型的回溯问题:在棋盘的第一行尝试为第一个皇后选择一个位置,再在第二行尝试为第二个皇后找一个位置,以此类推。在发现无法为一个皇后选择合适的位置后,回溯到前一个皇后,并尝试为她找另一个位置。最后,要么尝试完所有的可能性,要么找到答案。

当然现实中的问题可能不仅仅是八个,可以是任意多个。

三、状态表示

可简单的使用元组(或列表)来表示可能的解(或其中一部分),其中每个元素表示相应行中皇后所在的位置(列)。

如state[0] == 3,就说明第一行的皇后放在第四列(因为是从0开始计数的,范围[0,7])。

在特定的递归层级(特定的行),你只知道上面各皇后的位置,因此状态元组的长度小于8(即皇后总数)。

四、检测冲突

函数conflict(冲突)接受(用状态元组表示的)既有皇后的位置,并确定下一个皇后的位置是否会导致冲突。

1 def conflict(state,nextX):  #conflict 冲突
2     nextY = len(state)  # num = [1,2,3] len(num) == 3
3     for i in range(nextY):# range取不到nextY,range(3)取:0,1,2
4         if abs(state[i] - nextX) in (0, nextY - i):  # abs()取绝对值
5             return True
6     return False

nextX表示下一个皇后的水平位置(X坐标,即列);

nextY表示下一个皇后的垂直位置(y坐标,即行)

该函数对既有的每个皇后执行简单的检查:如果下一个皇后与当前皇后的x坐标相同或在同一条对角线上,将发生冲突,因此返回True;如果没有冲突就返回False。

abs(state[i] - nextX) in (0, nextY - i)

表示:如果下一个皇后和当前皇后的水平距离为0(在同一列)或与它们的垂直距离(位于一条对角线上),这个表达式为真,否则为假。

五、基线条件

最后一个皇后

def queens(num, state):
    if len(state) == num - 1:
        for pos in range(num):   # pos:位置
            if not conflict(state, pos):
                yield pos

如果只剩下最后一个皇后没有放好,就遍历所有可能的位置,并返回那些不会引发冲突的位置。

num为皇后总数

state是一个元组,包含已放好的皇后的位置。

六、递归条件

你希望递归调用返回什么样的结果?你希望它返回当前行下面所有皇后的位置。

假设这些位置是以元组的方式返回的,因此需要修改基线条件,使其返回一个(长度为1的)元组。

对于递归调用,向它提供的是由当前行上面的额皇后位置组成的元组。对于当前换后的每个合法位置,递归调用返回的是由下面的皇后位置组成的元组。

为了让这个过程不断进行下去,只需将当前皇后的位置插入返回的结果开头。

def queens(num, state):
    if len(state) == num - 1:
        for pos in range(num):
            if not conflict(state, pos):
                yield pos
    else:
        for pos in range(num):
            if not conflict(state, pos):
                for result in queens(num, state + (pos, ))
                    yield (pos,) + result

这里的for pos和if not conflict部分与前面相同,所以可以简化代码:

def queens(num = 8, state = ()):
    if 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 pretyprint(solution):
    def line(pos, length = len(solution)):
        return '. '*(pos) + 'X '+'. '*(length - pos - 1)
    for pos in solution:
        print(line(pos))

pretyprint(random.choice(list(queens(8))))

 

posted @ 2018-06-07 23:55  无证骑士  阅读(329)  评论(0编辑  收藏  举报