使用 pure SAT 求解 N-Queen 问题,并对比 SMT 的实现效率

实验目的

N-Queen问题是经典的八皇后问题的推广,即在一个N×N的棋盘上放置N个皇后,使得任意两个皇后都不能在同一行、同一列或同一斜线上。这是一个NP完全问题,因此可以使用形式化方法中的pure SAT和SMT求解。本文使用 pure SAT 求解 N-Queen 问题, 并对比 PPT 中 SMT 的实现效率。

pure SAT 编码思路

1.导入Z3库和time库定义n皇后问题的求解函数n_queen_sat(n)创建一个solver实例

2.创建一个n x n的棋盘,用布尔变量表示每个格子是否放置皇后

3.添加约束条件,保证每个格子至少有一个皇后

4.添加约束条件,保证任意两个皇后不在同一行、同一列或同一对角线上

5.使用Z3求解器找到一个满足约束条件的解

6.输出解计算求解时间并输出

7.分别求解n=4,8,12,16,20,24,32的N皇后问题

和SMT对比

Time\Method

pure SAT

SMT

4

0.13403749465942383 s

0.26999998092651367 s

8

0.12152719497680664 s

0.304002046585083 s

12

0.18607354164123535 s

0.3803980350494385 s

16

0.21963977813720703 s

0.37200021743774414 s

20

0.30319929122924805 s

0.7380886077880859 s

24

0.3517324924468994 s

0.7714138031005859 s

32

0.7708442211151123 s

2.959766149520874 s

40

0.7223820686340332 s

26.008009433746338 s

50

71.10545802116394 s

26.6474769115448 s

60

160.44336414337158 s

27.320754528045654 s

分析pure SAT和SMT 的效率

从表格中可以看出,在N值较小的情况下,两个代码的运行时间差别不大。但是当N值增大到50以上时,pure SAT的运行时间明显比SMT长很多。这是因为pure SAT使用了布尔变量来表示每个格子是否放置了皇后,然后约束每个格子至少放置一个皇后,以及每行、每列、每个对角线上最多只能有一个皇后。这样的约束条件是比较复杂的,因此在N值增大时,求解器需要花费更长的时间来找到可行解。而SMT则使用了整数变量来表示每行皇后所在的列数,然后约束每行皇后的列数不同,并且不允许出现对角线上的冲突。这样的约束条件比较简单,因此求解器可以更快地找到可行解。因此,在N值较大的情况下,SMT的效率比pure SAT高。

具体代码及注释:

1.pure SAT

from z3 import *
import time

def n_queen_sat(n):
    # create a solver instance
    solver = Solver()

    # create a boolean variable 
    board = [[Bool("b_{}_{}".format(i, j)) for j in range(n)] for i in range(n)]

    # ensure at least one queen is placed
    for i in range(n):
        solver.add(Or([board[i][j] for j in range(n)]))

    # cannot contain two queens
    for i in range(n):
        for j in range(n):
            for k in range(j+1, n):
                solver.add(Or(Not(board[i][j]), Not(board[i][k])))
                solver.add(Or(Not(board[j][i]), Not(board[k][i])))
            for k in range(1, n):
                if i+k < n and j+k < n:
                    solver.add(Or(Not(board[i][j]), Not(board[i+k][j+k])))
                if i+k < n and j-k >= 0:
                    solver.add(Or(Not(board[i][j]), Not(board[i+k][j-k])))

    # find a satisfying assignment to the constraints
    start_time = time.time()
    if solver.check() == sat:
        model = solver.model()
        for i in range(n):
            row = ['Q' if is_true(model[board[i][j]]) else '.' for j in range(n)]
            print(' '.join(row))
    else:
        print('No solution found')

    end_time = time.time()
    print('Time:', end_time - start_time, 's')

if __name__ == '__main__':
    n_queen_sat(4)
    n_queen_sat(8)
    n_queen_sat(12)
    n_queen_sat(16)
    n_queen_sat(20)
    n_queen_sat(24)
    n_queen_sat(32)
    n_queen_sat(40)
    n_queen_sat(50)
    n_queen_sat(60)

2. SMT 

from z3 import *
import time

def n_queen_smt(n):
    solver = Solver()

    rows = [Int("r_{}".format(i)) for i in range(n)]

    for i in range(n):
        solver.add(rows[i] >= 0)
        solver.add(rows[i] < n)
        for j in range(i):
            solver.add(rows[i] != rows[j])
            solver.add(rows[i] != rows[j] + (i - j))
            solver.add(rows[i] != rows[j] - (i - j))

    start_time = time.time()
    if solver.check() == sat:
        model = solver.model()
        for i in range(n):
            row = ['Q' if model[rows[i]] == j else '.' for j in range(n)]
            print(' '.join(row))
    else:
        print('No solution found')

    end_time = time.time()
    print('Time:', end_time - start_time, 's')

if __name__ == '__main__':
    n_queen_smt(4)
    n_queen_smt(8)
    n_queen_smt(12)
    n_queen_smt(16)
    n_queen_smt(20)
    n_queen_smt(24)
    n_queen_smt(32)
    n_queen_smt(40)
    n_queen_smt(50)
    n_queen_smt(60)
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值