Python解决八皇后问题

回溯法

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。 

基本思想 

在回溯法中,每次扩大当前部分解时,都面临一个可选的状态集合,新的部分解就通过在该集合中选择构造而成。这样的状态集合,其结构是一棵多叉树,每个树结点代表一个可能的部分解,它的儿子是在它的基础上生成的其他部分解。树根为初始状态,这样的状态集合称为状态空间树。

回溯法与穷举法有某些联系,它们都是基于试探的。穷举法要将一个解的各个部分全部生成后,才检查是否满足条件,若不满足,则直接放弃该完整解,然后再尝试另一个可能的完整解,它并没有沿着一个可能的完整解的各个部分逐步回退生成解的过程。而对于回溯法,一个解的各个部分是逐步生成的,当发现当前生成的某部分不满足约束条件时,就放弃该步所做的工作,退到上一步进行新的尝试,而不是放弃整个解重来。

回溯和递归唯一的联系就是,回溯法可以用递归思想实现。 

例如问题:列举集合 {1,2,3} 中所有子集的问题 

使用回溯法。从集合的开头元素开始,对每个元素都有两直到集合最后一个元素。 其中的每个操作都可以看作是一次尝试,每次尝试都可以得出一个结果。将得到的 结果综合起来,就是集合的所有子集。回溯法的求解过程实质上是先序遍历“状态树”的过程。树中每一个叶子结点,都有可能是问题的答案。如下图所示:

 

八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 

算法思路:

1. 从棋盘的第一行开始,从第一个位置开始,依次判断当前位置是否能够放置皇后, 判断的依据为:同该行之前的所有行中皇后的所在位置进行比较,如果在同一列, 或者在同一条斜线上(斜线有两条,为正方形的两个对角线),都不符合要求, 继续检验后序的位置。

2. 如果该行所有位置都不符合要求,则回溯到前一行,改变皇后的位置,继续试探。

3. 如果试探到最后一行,所有皇后摆放完毕,则直接打印出 8 * 8 的棋盘。最后一定要记得将棋盘恢复原样,避免影响下一次摆放。 

下面我们用代码实现八皇后问题,首先,创建一个函数,用来判断下一行要放置的皇后是否与之前所放置的皇后冲突,代码如下:

def conflict(state, nextColumn):
    nextRow = rows = len(state)  
    for row in range(rows): 
        column = state[row]
        if abs(column - nextColumn) in (0, nextRow - row):
            """
                如果差值等于0,两个皇后在同一列, 则代表冲突, 返回True;
                如果列的差值等与行的差, 两个皇后在对角线上, 则代表冲突, 返回True;
            """
            return True
    return False

然后根据上面的思路用递归的方式写主函数,代码如下:

def queens(num, state=()):
    """
    采用生成器的方式来产生每一个皇后的位置,并用递归来实现下一个皇后的位置。
    num: 皇后的数量
    state: 标记已经排好的每个皇后的位置
    """
    for pos in range(num):  # 八皇后的数量N=0, 1, 2, 3, 4, 5, 6 , 7 你要在哪一列放置皇后
        # 如果不冲突,则递归构造棋盘。
        if not conflict(state, pos):  # 回溯法的体现
            # 如果棋盘状态state已经等于num-1,即到达倒数第二行,而这时最后一行皇后又没冲突,直接yield,打出其位置(pos, )
            if len(state) == num - 1:  # state=()
                yield (pos,)
            else:  # (0, )
                for result in queens(num, state + (pos,)):
                    yield (pos,) + result

最后再创建一个棋盘的绘制函数,皇后用x表示,代码如下:

def prettyprint(solution): 
    """
    友好展示: 为了直观表现棋盘,用X表示每个皇后的位置
    """

    def line(pos, length=len(solution)): 
        return ' . ' * (pos) + ' x ' + ' . ' * (length - pos - 1)

    for pos in solution:
        print(line(pos))

最后经过测试,八皇后问题的所有可能就都出来了,测试代码如下:

if __name__ == '__main__':
    solutions = queens(8)
    for index, solution in enumerate(solutions):
        print("第%d种解决方案:" % (index + 1), solution)
        prettyprint(solution)
        print('*' * 100)

由于有92种结果,在这里只选部分截图,如下:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值