【形式化方法】Part B: LA/LP Applications(子集和问题)

2023-11-20 16:10

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

在本节中,我们将研究如何利用LA/LP理论,借助Z3求解器建立模型和解决问题。

具体地说,我们将解决几个非常难的问题,这里的“难”是指这些问题都是np完全的,也就是说,对于这些问题一般没有已知的多项式算法。

但是,您很快就会发现使用LA/LP求解器(如Z3)解决这些问题是多么容易(和多么有趣)。一般来说,您将认识到基于求解器的问题解决策略的威力和简单性。

我们要解决的np完全问题是(其中一些已经在我们之前的讲座中讨论过):

  1. 子集和问题
  2. N-queen问题
  3. 背包问题
  4. 线性回归

请注意,这并不是基于SMT的求解器可以处理的问题的完整列表,在您学习了此技术之后,还鼓励您尝试解决其他np完全问题。

子集和问题

子集问题是一个著名的可满足性问题:给定一个多集(一个多集就像一个普通的集合,除了元素可以被重复之外),我们应该确定一个非空子集T (S),这样

 ,在集合中找到子集,使得这个子集的和为0比如这里有个集合:{-7,-3,-2,7000,5,9}。显然这里有个解:{-3,-2,5}

这个问题是np完全的,关于子集问题的更多背景信息,请参考这篇文章:子集和问题

解释: 我们要找到一个集合中的某个子集相加为0,只需要构造一个X = [x_0,x_1,...x_n]列表。比如在这个集合中{-7,-3,-2,7000,5,9},对应构造的列表是X  =[x_0,x_1,x_2,x_3,x_4,x_5]

需满足条件①x_0 + x_1 + x_2 + x_3 + x_4 + x_5 !=0  (因为如果全为0,条件②就永远成立)

                 ②(-7*x_0) + (-3*x_1) + (-2*x_2) + (7000*x_3) + (5*x_4) + (9*x_5) = 0 

能找到满足条件①②的结果就是:x_1 = 0,x_2 = 1,x_3 = 1,x_4 = 1,x_5 = 0,  这样,我们就知道索引为2,3,4对应的值,即[-3, -2, 5] 这个子集之和为0.问题解决。

Exercise 9:阅读subset_sum.py Python文件中的代码,完成subset_sum_la()方法,该方法使用0-1 ILA解决子集和问题。基本思想是为目标集合T中的每个数字创建标志F,我们只需要添加约束:其中N是目标集合S的长度(即实现上述问题的代码)

# LA-based solution
def subset_sum_la(target_set: list):solver = Solver()flags = [Int(f"x_{i}") for i in range(len(target_set))]# 0-1 ILAfor flag in flags:solver.add(Or(flag == 0, flag == 1))# print(flags)# the selected set must be non-emptysolver.add(sum(flags) != 0)# @exercise 9: please fill in the missing code to add# the following constraint into the solver.#       sum_i flags[i]*target_set[i] = 0# raise Todo("exercise 9: please fill in the missing code.")i = 0con = []for t in target_set:con.append(t * flags[i])i = i+1solver.add(sum(con) == 0)# print(con)if __name__ == '__main__':# a small test casesmall_set = [-7, -3, -2, 9000, 5, 8]print(subset_sum_la(small_set))

输出结果:  可满足,并且找到子集 [-3, -2, 5]

 

Exercise 10:subset_sum.py Python文件中的代码,subset_sum_dp()方法已经提供了基于动态编程(DP)的解决方案。并给出了另一种基于拉普拉斯算子的subset_sum_la_opt()方法,该方法利用Z3伪布尔约束条件进行优化。试着比较DP、LA算法和优化的LA算法的效率,通过将max_nums的值更改为其他值,例如200,2000,20000,…你的观察结果是什么?从这些数据中你能得出什么结论?

# LA 优化算法
def subset_sum_la_opt(target_set: list):solver = Solver()# enable Pseudo-Boolean solver# to get more information about Pseudo-Boolean constraints# refer to https://theory.stanford.edu/~nikolaj/programmingz3.htmlsolver.set("sat.pb.solver", "solver")# use Pseudo-Boolean constraints for each flagflags = [Bool(f"x_{i}") for i in range(len(target_set))]#solver.add(AtLeast(flags + [1]))# the selected set must be non-emptysolver.add(PbGe([(flags[i], 1) for i in range(len(target_set))], 1))# selected set must sum to zerosolver.add(PbEq([(flags[i], target_set[i]) for i in range(len(target_set))], 0))start = time.time()result = solver.check()print(f"time used in LA optimized: {(time.time() - start):.6f}s")if result == sat:return True, [target_set[index] for index, flag in enumerate(flags) if solver.model()[flag]]return False, result
# DP算法
def subset_sum_dp(target_set):def subset_sum_dp_do(the_set, target, index):if index == 0:return Falseif target == the_set[index - 1]:return Trueif subset_sum_dp_do(the_set, target, index - 1):return Truereturn subset_sum_dp_do(the_set, target - the_set[index - 1], index - 1)start = time.time()result = subset_sum_dp_do(target_set, 0, len(target_set))print(f"time used in DP: {(time.time() - start):.6f}s")return result
 
def gen_large_test(n):nums = [10000] * nnums[len(nums) - 2] = 1nums[len(nums) - 1] = -1# print(nums)return numsif __name__ == '__main__':# a large test casemax_nums = 20large_set = gen_large_test(max_nums)"""# @exercise 10: compare the efficiency of the DP and theLP algorithm, by changing the value of "max_nums" to othervalues, say, 200, 2000, 20000, 200000, ...what's your observation? What conclusion you can draw from these data?raise Todo("exercise 10: please fill in the missing code.")""""""max_num = 20time used in LA: 0.070020stime used in LA optimized: 0.020002stime used in DP: 0.667767sLA优化算法比LA算法速度快,DP算法速度最慢,到max_num = 200时,DP算法用时更长了。"""print(subset_sum_la(large_set))print(subset_sum_la_opt(large_set))print(subset_sum_dp(large_set))
运行结果:
结论:max_num = 20
time used in LA: 0.070020s
time used in LA optimized: 0.020002s
time used in DP: 0.667767s
LA优化算法比LA算法速度快,DP算法速度最慢,到max_num = 200时,DP算法用时更长了(没有运行下去)。

 

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

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

 

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



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

相关文章

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma

Mybatis Plus Join使用方法示例详解

《MybatisPlusJoin使用方法示例详解》:本文主要介绍MybatisPlusJoin使用方法示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录1、pom文件2、yaml配置文件3、分页插件4、示例代码:5、测试代码6、和PageHelper结合6

MySQL 设置AUTO_INCREMENT 无效的问题解决

《MySQL设置AUTO_INCREMENT无效的问题解决》本文主要介绍了MySQL设置AUTO_INCREMENT无效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录快速设置mysql的auto_increment参数一、修改 AUTO_INCREMENT 的值。