【形式化方法】PartB:LA/LP Applications(N皇后问题)

2023-11-20 16:10

本文主要是介绍【形式化方法】PartB:LA/LP Applications(N皇后问题),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

N-Queen Problem:

在作业3(挑战问题)中,我们在SAT之前解决了N个皇后的问题(4个皇后)。这个问题是关于把N个皇后放在一个N*N的棋盘上,这样就没有两个皇后互相威胁了。一种解决方案要求没有两个皇后共享同一行、列、对角线或反对角线。下图显示了N = 4的样本N -皇后谜题的解:

这个问题的目标是在一个N*N棋盘,找出存在多少个解。

SAT实现的基本思想是通过Bool值构造n-queen谜题约束。实际上,我们可以用LA来求解n-queen问题,它比SAT更容易理解,也更高效。其思路与求解子集和问题的思路相同。我们使用一个二维的0-1标志F来代表棋盘的每个单元格,F有值:

 满足 0 < i <N, 0< j < N 。我们可以建立n-queen谜题的约束条件如下:

  • 每一行只有一个皇后:   0 < i <N 
  • 每一列只有一个皇后:0 < j < N
  • 每条对角线最多有1个皇后:-N < d < N
  • 每条反对角线最多有1个皇后:  0 < d < 2N -1

Exercise 11: 阅读queen.py Python文件中的代码,完成n_queen_la()方法,该方法使用0-1 ILA解决n-queen问题。您可以通过参考我们上面讨论的模型来构造约束,或者您可以提出您自己的约束。

 

# LA算法解决N皇后问题
def n_queen_la(board_size: int, verbose: bool = False) -> int:solver = Solver()n = board_size# Each position of the board is represented by a 0-1 integer variable:#   ...    ...    ...    ...#   x_2_0  x_2_1  x_2_2  ...#   x_1_0  x_1_1  x_1_2  ...#   x_0_0  x_0_1  x_0_2  ...#board = [[Int(f"x_{row}_{col}") for col in range(n)] for row in range(n)]# only be 0 or 1 in boardfor row in board:for pos in row:solver.add(Or(pos == 0, pos == 1))# print(row)# @exercise 11: please fill in the missing code to add# the following constraint into the solver:#   each row has just 1 queen,#   each column has just 1 queen,#   each diagonal has at most 1 queen,#   each anti-diagonal has at most 1 queen.# raise Todo("exercise 11: please fill in the missing code.")for row in board:# print(row)solver.add(sum(row) == 1) # 约束1:一行只有一个皇后for col in board:# print(col)solver.add(sum(col) == 1) # 约束2: 一列只有一个皇后i = 0dia = []anti_dia = []# 对角线元素放到dia数组里面for row in board:j = 0for pos in row:if i == j:dia.append(pos)j = j + 1i = i + 1solver.add(sum(dia) <= 1)    # 约束3:对角线最多只有一个皇后# print(dia)# 反对角线元素放到anti_dia数组里面i = 0for row in board:j = 0for pos in row:if i + j == n-1 :anti_dia.append(pos)j = j + 1i = i + 1# print(anti_dia)solver.add(sum(anti_dia) <= 1)  # 约束4:反对角线最多只有一个皇后# count the number of solutionssolution_count = 0start = time.time()while solver.check() == sat:solution_count += 1model = solver.model()if verbose:# print the solutionprint([(row_index, col_index) for row_index, row in enumerate(board)for col_index, flag in enumerate(row) if model[flag] == 1])# generate constraints from solutionsolution_cons = [(flag == 1) for row in board for flag in row if model[flag] == 1]# add solution to the solver to get new solutionsolver.add(Not(And(solution_cons)))print(f"n_queen_la solve {board_size}-queens by {(time.time() - start):.6f}s")return solution_count

另一种解决N -queen问题的方法是使用回溯算法,但复杂度相对于棋盘大小N是指数级的。

Exercise 12:queen.py Python文件中的代码,在n_queen_bt()方法中有一个基于回溯的解决方案。尝试比较回溯算法和LA算法,通过改变棋盘大小N的值为其他值,哪一个更快?从结果中你能得出什么结论?

#回溯法解决N皇后问题
def n_queen_bt(board_size: int, verbose: bool = False) -> int:n = board_sizesolutions = [[]]def is_safe(col, solution):same_col = col in solutionsame_diag = any(abs(col - j) == (len(solution) - i) for i, j in enumerate(solution))return not (same_col or same_diag)start = time.time()for row in range(n):solutions = [solution + [col] for solution in solutions for col in range(n) if is_safe(col, solution)]print(f"n_queen_bt solve {board_size}-queens by {(time.time() - start):.6f}s")if verbose:# print the solutionsfor solution in solutions:print(list(enumerate(solution)))return len(solutions)

 

上述LA实现并不是求解n-queen问题的唯一算法。事实上,我们建立约束来描述问题的方式往往对算法的效率有很大的影响。

Exercise 13: 阅读queen.py Python文件中n_queen_la_opt()方法的代码。试着将此方法的效率与练习11中的实现进行比较。你的观察是什么?你能得出什么结论?

# LA优化算法解决N皇后问题
def n_queen_la_opt(board_size: int, verbose: bool = False) -> int:solver = Solver()n = board_size# We know each queen must be in a different row.# So, we represent each queen by a single integer: the column position# the q_i = j means queen in the row i and column j.queens = [Int(f"q_{i}") for i in range(n)]# each queen is in a column {0, ... 7 }solver.add([And(0 <= queens[i], queens[i] < n) for i in range(n)])# one queen per columnsolver.add([Distinct(queens)])# at most one for per anti-diagonal & diagonalsolver.add([If(i == j, True, And(queens[i] - queens[j] != i - j, queens[i] - queens[j] != j - i))for i in range(n) for j in range(i)])# count the number of solutionssolution_count = 0start = time.time()while solver.check() == sat:solution_count += 1model = solver.model()if verbose:# print the solutionsprint([(index, model[queen]) for index, queen in enumerate(queens)])# generate constraints from solutionsolution_cons = [(queen == model[queen]) for queen in queens]# add solution to the solver to get new solutionsolver.add(Not(And(solution_cons)))print(f"n_queen_la_opt solve {board_size}-queens by {(time.time() - start):.6f}s")return solution_count

 N = 4时,比较运行时间:

N = 5 时,比较运行时间:

结论:

三种算法解决N皇后问题效率的比较: 用回溯法最快、LA优化算法其次、LA算法最慢

 

#中科大软院-hbj形式化课程笔记-欢迎留言与私信交流

#随手点赞,我会更开心~~^_^

 

 

这篇关于【形式化方法】PartB:LA/LP Applications(N皇后问题)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/395837

相关文章

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::