java背包算法回溯法_【算法分析】实验 4. 回溯法求解0-1背包等问题
[TOC]
实验内容
本实验要求基于算法设计与分析的一般过程(即待求解问题的描述、算法设计、算法描述、算法正确性证明、算法分析、算法实现与测试),通过回溯法的在实际问题求解实践中,加深理解其基本原理和思想以及求解步骤。求解的问题为0-1背包。
作为挑战:可以考虑回溯法在其他问题(如最大团问题、旅行商、图的m着色问题)。
实验目的
理解回溯法的核心思想以及求解过程(确定解的形式及解空间组织,分析出搜索过程中的剪枝函数即约束函数与限界函数)。
掌握对几种解空间树(子集树、排列数、满m叉树)的回溯方法。
从算法分析与设计的角度,对0-1背包问题的基于回溯法求解有更进一步的理解。
实验结果
步骤1:描述与分析
给定n种物品和一个背包。物品$i$的重量是$w_i$ ,其价值为$v_i$,背包的容量为W。一种物品要不全部装入背包,要不不装入背包,不允许部分物品装入的情况。装入背包的物品的总重量不能超过背包的容量,在这种情况下,问如何选择转入背包的物品,使得装入背包的物品的总价值最大?需要采用回溯的方法进行问题的求解。
分析:
(1)问题的解空间:
将物品装入背包,有且仅有两个状态。第$i$种物品对应$(x_1,x_2,...,x_n)$,其中$x_i$可以取0或1,分别代表不放入背包和放入背包。解空间有$2^n$种可能解,也就是$n$个元素组成的集合的所有子集的个数。采用一颗满二叉树来讲解空间组织起来,解空间树的深度为问题的规模$n$。
(2)约束条件: $$ \sum_{i=1}^nw_ix_i \le W $$ (3)限界条件:
0-1背包问题的可行解不止一个,而目标是找到总价值最大的可行解。因此需要设置限界条件来加速找出最优解的速度。如果当前是第t个物体,那么1-t物体的状态都已经被确定下来,剩下就是t+1~n物体的状态,采用贪心算法计算当前剩余物品所能产生的最大价值是否大于最优解,如果小于最优解,那么被剪枝掉。
步骤2:策略以及数据结构
采用回溯法进行问题的求解,也就是具有约束函数/限界条件的深度优先搜索算法。
采用回溯法特有框架:
回溯算法()
如果到达边界:
记录当前的结果,进行处理
如果没有到达边界:
如果满足限界条件:(左子树)
进行处理
进行下一层的递归求解
将处理回退到处理之前
如果不满足限界条件:(右子树)
进行下一层递归处理
步骤3
描述算法。希望采用源代码以外的形式,如伪代码或流程图等;
伪代码:
递归式回溯算法:
BACKTRACK-REC(t) //t为扩展结点在树中所处的层次
if t > n //已到叶子结点,输出结果
OUTPUT(x)
//检查扩展结点的每个分支。s(n,t)与e(n,t)分别为当前扩展结点处未搜索过
//的子树的起始编号和终止编号
else
for i from s(n,t) to e(n,t)
x[t] = h(i) // h[i]: 在当前扩展结点处 x[t]的第i个可选值
if CONSTRAINT(t) && BOUND(t) //约束函数与限界函数
BACKTRACK-REC(t+1) //进入t+1层搜索
迭代式回溯算法:
BACKTRACK-ITE()
t = 1 //t为扩展结点在树中所处的层次
while t > 0:
if s(n,t) <= e(n,t)
for i from s(n,t) to e(n,t)
do x[t] = h(i) //h[i]: 在当前扩展结点处x[t]的第i个可选值
if CONSTRINT(t) && BOUND(t) //满足约束限界条件
if t > n //已到叶子结点,输出结果
OUTPUT(x);
else
t ++ //前进到更深层搜索
else
t -- //回溯到上一层的活结点
步骤4
算法的正确性证明。需要这个环节,在理解的基础上对算法的正确性给予证明
回溯算法适用条件:多米诺性质
假设解向量是n维的,则下面的k满足:$0
在0-1背包问题中,解空间为:$(x_1,x_2,...,x_n)$, 如果当前结果$P_1 = (x_1,x_2,...,x_n)$是最优解,那么$P_2=(x_1,x_2,...,x_{n-1})$的时候,也就是减少一个物品但不改变背包容量的时候,可以想到$P_2$依然是该问题的最优解。从子集树角度来看,也就是最后一层结点全部去掉后的结果,那么当前结果也是最优的。
步骤5
算法复杂性分析,包括时间复杂性和空间复杂性;
算法的复杂性分析:
时间复杂度:$$ T(n)=O(2^n)+O(n2^n)+O(nlog(n)) = O(n2^n)$$
空间复杂度:$$O(nlog(n))$$
步骤6
算法实现与测试。附上代码或以附件的形式提交,同时贴上算法运行结果截图;
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 22 08:49:13 2018
@author: pprp
"""
BV=0 # best value
CW=0 # current weight
CV=0 # current value
BX=None # best x result
def output(x):
for i in x:
print(" ",i,end="")
print()
class node(object):
def __init__(self,v,w):
self.v = v
self.w = w
self.per = float(v)/float(w)
def Bound(t):
print("bound:",t)
LC = c-CW # left C
B = BV # best value
#sort
nodes = []
for i in range(n):
nodes.append(node(v[i],w[i]))
nodes.sort(key=lambda x:x.per,reverse=True)
# 装入背包
while t < n and w[t] <= LC:
LC -= w[t]
B += v[t]
t += 1
if t < n:
B += float(v[t])/float(w[t]) * LC
return B
def backtrack(t,n):
"""当前在第t层"""
print("current:",t)
global BV,CV,CW,x,BX
if t >= n:
if BV < CV:
BV=CV
BX=x[:]
else:
if CW+w[t] <= c: # 搜索左子树,约束条件
x[t]=True
CW += w[t]
CV += v[t]
backtrack(t+1,n)
CW -= w[t]
CV -= v[t]
if Bound(t) > BV: # 搜索右子树
x[t]=False
backtrack(t+1,n)
if __name__ == "__main__":
n=10
c=10
x=[False for i in range(n)]
w=[2,2,6,5,4,4,3,4,6,3]
v=[6,3,5,4,6,2,8,3,1,7]
backtrack(0,n)
print("Best Value :",BV)
print("Best Choice:",BX)
运行结果:
验证:6+3+8+7=24
实验总结
回溯法的思想:
能进则进,不进则换,不换则退.
回溯算法的框架:
以DFS的方式进行搜索,在搜索的过程中用剪枝条件(限界函数)避免无效搜索。约束函数,在扩展结点处剪去得不到可行解的子树;限界函数:在扩展结点处剪去得不到最优解的子树。
回溯算法求解问题的一般步骤:
1、 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
2 、确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
3 、以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
常用剪枝函数: 用约束函数在扩展结点处剪去不满足约束的子树; 用限界函数剪去得不到最优解的子树。
子集树、满m叉树、排列树区别:
子集树:从n个元素的集合S中找到满足某种性质的子集时,相应的解空间树就成为了子集树(典型问题:01背包问题)
满m叉树:所给问题中每一个元素均有m中选择,要求确定其中的一种选择,使得对这n个元素的选择结果组成的向量满足某种性质(经典问题:图的m着色问题)
排列树:从n个元素的排列树中找出满足某种性质的一个排列的时候,相应的解空间树称为排列树(经典问题:TSP问题,n皇后问题)
java背包算法回溯法_【算法分析】实验 4. 回溯法求解0-1背包等问题相关推荐
- 单片机里如何使用冒泡法实现数据从大到小排列_单片机实验一冒泡法排序.doc...
单片机实验一冒泡法排序 实验一:冒泡法排序实验 实验要求 实验目的:掌握控转移指令的功能,以及冒泡法排序的原理. 实验原理 循环嵌套结构.循环程序的设计方法和一样的,要分别考虑重循环的控制条件.内循环 ...
- java中驼峰编码,驼峰式命名法_小驼峰式命名法编程_java中getter和setter
人们交流靠各种语言,每行都有每行的所谓的"行话".程序员也不例外,众所周知,程序员都是用代码进行交流的.那么除了在代码中的注释之外, 程序员如何读懂别人的程序呢? 当然,程序员之间 ...
- sql相同顺序法和一次封锁法_数学专题 | Ep01 隔板法的妙用
数学专题(一) 隔板法的妙用 浓度常见哪些问题? 排列组合分堆?涂色?到底掌握透彻了吗? 解析几何与韦达定理? 公式总是记不住?应用题还不会解? 除了写作(写作听我的).逻辑(逻辑说)专题外,本周起 ...
- python数独伪代码回溯法_数独的暴力回溯解法和Python GUI
数独起源于18世纪初瑞士数学家欧拉等人研究的拉丁方阵,20世纪70年代,经过美国及日本学者的推广和改良,定名为数独(Sudoku),大致的意思是"独个的数字"或"只出现一 ...
- 最大公约数算法_更相减损法_辗转相除法(即欧几里得算法)
package algorithm;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStr ...
- 回溯法求解0 1背包的优化
优化方法: 剪枝一:可以进行剪枝,因为很多情况是没有意义的,当重量大于背包容量的时候,没有必要对剩下的物品再来决策了. 剪枝二:将剩下的所有物品都选取其总价值也没有目前已经求得的方案的价值还大的话, ...
- java贪心算法 区间调度_贪心算法-区间调度问题解之证明(示例代码)
一.贪心算法 定义:一个算法是贪心算法,如果它是通过一些小的步骤来一个求解,并且在每一步根据局部情况选择一个决定,使得某些主要的指标得到优化. 二.区间调度问题 1. 问题:我们有一组需求{1,2,3 ...
- python的回溯信息_基于Python的回溯算法
我试图实现一个算法,它包含两个整数n和k,其中n是一排的座位数,k是试图坐在那一排的学生数.问题是每一个学生必须在两边至少有两个座位.我所拥有的是一个生成所有子集的函数(一个0或1s的数组,1表示有人 ...
- java测试类写三角形_软件测试实验一——使用junit判断三角形
一.简单描述下安装 junit, hamcrest and eclemma的过程 ①当然,有了eclipse软件,安装的过程会显得比较轻松 对于安装junit和hamcrest来说需要在官网(或者其它 ...
- java模拟退火算法求函数_模拟退火算法从原理到实战【基础篇】
模拟退火算法来源于固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小 ...
最新文章
- mysql算法函数_十个实用MySQL函数
- ArchiMate - 发布【企业架构语言ArchiMate v0.5.pdf】
- q版地图制作软件_Flash动画的图形元件实例-Q版人物侧面行走
- [jQuery] 判断复选框checkbox是否选中checked
- (转)JVM监控工具介绍
- UE3 移动设备主页
- c+oracle+bulk,C#使用OracleBulkCopy
- 【Flink】flink zookeeper HA 实现分析
- 函数专题:sum、row_number、count、rank\dense_rank over
- EasyUI 中自定义组件 icon 图标
- nvme固态硬盘开机慢_win10 Samsung NVMe固态硬盘测速很慢的解决方法
- Visual Studio2019使用nmake编译调用libcurl库
- 桌面运维转网络要做什么准备,高级网工学习路线分享
- 厦门宏发有机器人_2020新版福建省厦门工业机器人工商企业公司名录名单黄页大全23家...
- 基于Linux利用PPP实现4G模块联网
- 基于波动率的期权交易策略分析
- 关于NX UG11.0在visual studio中不能创建模板的问题
- Http Headers各属性简介及常见安全攻击
- Android禁止多点触控
- 英语语法基础01(句子结构)