Python:爬山法/随机重启爬山法/允许侧移的爬山法解决八皇后问题


1 八皇后问题

有一个8乘8的棋盘,现在要将八个皇后放到棋盘上,满足:对于每一个皇后,在自己所在的行、列、两个对角线都没有其他皇后。
在这里插入图片描述
不了解爬山法随机重启爬山法允许侧移的爬山法的话,请看这里

规定棋盘的同列只能出现一个皇后。每一个棋盘,对应于一个长度为8的序列,每一个数的范围是[1, 8],第k个数字所代表的含义是第k列中皇后所在的行数,如[3,2,5,4,3,2,1,3]代表棋盘上从第一列到第八列,皇后所摆放的行数分别为第3,2,5,4,3,2,1,3行。任意状态(包括初始状态)的所有后继状态为【从当前状态开始,将任意一个皇后移到同列的其他7个格子后的所有状态】。对于任意一个已有八个皇后的棋盘(当然同一列有且仅有一个皇后)的后继状态均有8*7=56个。


2 程序代码

2.1 程序1

程序1:generate_init_seq.py。如果8个皇后在8*8的棋盘上可以随意摆放,当然是不能在同一个格子里放超过一个皇后的情况下,本来所有需要测试是否满足要求的序列共有64*63*…*57=1.78e+14个,这太多了。所以此程序的工作是筛选出那些【每行与每列都只有一个皇后存在】的序列,这样的序列有8*7*6*5*4*3*2=40320个,可以大大缩减后续程序的运行时间,而且这样在后面处理每个序列时只需要考虑两条对角线上和所在的行上有没有其他皇后即可(不用考虑列)。如下:

import json
import time

start = time.time()

seq = [[i, j, k, l, m, n, o, p]
       for i in range(1, 9)
       for j in range(1, 9)
       for k in range(1, 9)
       for l in range(1, 9)
       for m in range(1, 9)
       for n in range(1, 9)
       for o in range(1, 9)
       for p in range(1, 9)
       if all([i != j, i != k, i != l, i != m, i != n, i != o, i != p,
               j != k, j != l, j != m, j != n, j != o, j != p,
               k != l, k != m, k != n, k != o, k != p,
               l != m, l != n, l != o, l != p,
               m != n, m != o, m != p,
               n != o, n != p,
               o != p])]  # 筛选出【每行与每列都只有一个皇后】的序列

print('有' + str(len(seq)) + '个可能的序列')

with open('seq.json', 'w') as file_object:
    json.dump(seq, file_object)

end = time.time()

print('Successful!')
print('已将生成的序列存储到文件seq.json中,用时' + str('%.2f' % (end - start)) + 's')

输出如下。注意会生成一个文件seq.json,我上传到了csdn上,你可以看看这里,你也可以运行程序1,就可以在自己电脑上得到一个文件,除了运行时间有区别,其他输出和我这个是一样的:

40320个可能的序列
Successful!
已将生成的序列存储到文件seq.json中,用时17.61s

2.2 程序2

程序2:functions.py。包括两个函数:attacked_queens_pairs, display_board,分别完成【计算序列对应棋盘的互相攻击的皇后对数】和【打印输出序列对应的棋盘】的功能。如下:

import numpy as np

def attacked_queens_pairs(seqs):
    """
    计算序列对应棋盘的【互相攻击的皇后对数n】,0<=n<=28,最优解要满足n=0
    只需要检查当前棋盘的八个皇后在各自的行和两条对角线上是否有其他皇后,不需判断同列是否有其他皇后
    """
    a = np.array([0] * 81)  # 创建一个有81个0的一维数组
    a = a.reshape(9, 9)  # 改为9*9二维数组。为方便后面使用,只用后八行和后八列的8*8部分,作为一个空白棋盘
    n = 0  # 互相攻击的皇后对数初始化为0

    for i in range(1, 9):
        a[seqs[i - 1]][i] = 1  # 根据序列,按从第一列到最后一列的顺序,在空白棋盘对应位置放一个皇后,生成当前序列对应的棋盘

    for i in range(1, 9):
        for k in list(range(1, i)) + list(range(i + 1, 9)):  # 检查每个皇后各自所在的行上是否有其他皇后
            if a[seqs[i - 1]][k] == 1:  # 有其他皇后
                n += 1
        t1 = t2 = seqs[i - 1]
        for j in range(i - 1, 0, -1):  # 看左半段的两条对角线
            if t1 != 1:
                t1 -= 1
                if a[t1][j] == 1:
                    n += 1  # 正对角线左半段上还有其他皇后

            if t2 != 8:
                t2 += 
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值