基于回溯法和分支限界法求解01背包问题

编程入门 行业动态 更新时间:2024-10-05 01:18:36

基于回溯法和分支<a href=https://www.elefans.com/category/jswz/34/1743057.html style=限界法求解01背包问题"/>

基于回溯法和分支限界法求解01背包问题

基于回溯法和分支限界法求解01背包问题

问题描述

现有n个物品,1个背包。对物品i,其价值为 v i v_i vi​ ,重量为 W i W_i Wi​,背包的容量为 W W W,如何选取物品使得背包里转入物品的总价值最大?

在约束条件为:选取物品的重量小于等于背包重量的情况下,尽可能让背包中物品的总价值最大。

算法设计

01背包问题是自己选取问题,该问题的解空间可用子集树表明。设currentTotalValue是当前已经装入背包的物品的总价值、residueValue是剩余的未知是否装入物品的总价值,也就是未被搜索到物品的总价值、pbest是当前已经找到的最优解的价值。

使用两个等长的一维数组存储物品的重量 w e i g h t s [ n ] weights[n] weights[n]和价值 v a l u e s [ n ] values[n] values[n],这里第 i i i个物品的重量就是 w e i g h t s [ i ] weights[i] weights[i],价值就是 v a l u e s [ i ] values[i] values[i]。

回溯法

回溯法的基本思想,在包含问题所有解的解空间树中,按照深度优先的策略,从根节点出发搜索解空间树。搜索解空间的任意一个节点时候,只要其左儿子节点是一个可行节点,搜索就进入其左子树。当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。

分支限界法

分支限界法解空间的定义、解空间的组织结构、剪枝函数都与回溯法相同。不同的是解空间的搜索方式,这里采用广度优先搜索。

  • 针对当前扩展节点,一次性生成其所有孩子节点,如果孩子节点满足约束条件和限界条件,则将该孩子节点插入到活结点队列末尾;反之则舍弃。
  • 从活结点对了中选取待扩展节点。
  • 搜索过程直到活结点队列为空时停止。

算法描述

回溯法

backtrack(i, weights[n], values[n], W){if i > npbest = currentTotalValuereturnif weights[i] + currentTotalWeight <= Wx[i] = 1currentTotalWeight += weights[i]currentTotalValue += values[i]backtrack(i+1, weights[n], values[n], W-currentTotalWeight)currentTotalWeight -= weights[i]currentTotalValue -= values[i]if bound(i+1, W-currentTotalWeight, currentTotalValue, weights[n], values[n]) > pbestx[i] = 0backtrack(i+1, weights[n], values[n], W-currentTotalWeight)
}

分支限界法

branch(weights[n],values[n],W){i = 0, currentTotalWeight = 0, currentTotalValue = 0, queue = nullup = 0while true{if currentTotalWeight+w[i] < Wif currentTotalValue + values[i] > pbestpbest = currentTotalValue + values[i]up = bound(i+1)if up > pbestqueue.push(new Node(currentTotalWeight, currentTotalValue,up , false , i+1))node = queue.back()if(node = null)breakscurrentTotalWeight = node.currentTotalWeightcurrentTotalValue = node.currentTotalValueup = node.upi = node.levelif i == n+1stNode = node.ptr}if(stNode != null)for j=n to 1 by -1x[j] = stNode.isLeft?1:0stNode = stNode.parent}
}

正确性证明

复杂性分析

回溯法

  • 判断约束函数需要 O ( 1 ) O(1) O(1),在最坏的情况下有 2 n − 1 2^n-1 2n−1个左孩子,约束函数耗时最坏为 O ( 2 n ) O(2^n) O(2n)。

  • 计算上界界限函数需要 O ( n ) O(n) O(n)时间,最坏情况下有 2 n − 1 2^n-1 2n−1个右孩子需要计算上界,界限函数耗时最坏为 O ( n 2 n ) O(n2^n) O(n2n)。

时间复杂度: O ( 2 n ) + O ( n 2 n ) = O ( n 2 n ) O(2^n)+O(n2^n)=O(n2^n) O(2n)+O(n2n)=O(n2n)

空间复杂度: O ( n ) O(n) O(n),即递归深度。

分支限界法

计算上界限界函数需要 O ( n ) O(n) O(n)时间, 而最坏情况有 2 ( n + 1 ) – 2 2^(n +1) –2 2(n+1)–2个节点; 若对每个结点用限界函数判断,则其时间复杂度为 O ( n ∗ 2 n ) O(n*2^n) O(n∗2n)​,算法中时间复杂度主要依赖限界函数。最坏情况下需搜索 2 ( n + 1 ) − 2 2^(n +1)-2 2(n+1)−2个节点,需 O ( 2 n ) O(2^n ) O(2n)个空间存储节点。

  • 时间复杂度: O ( n ∗ 2 n ) O(n*2^n) O(n∗2n)

  • 空间复杂度: O ( 2 n ) O(2^n ) O(2n)

实现与测试

import java.util.LinkedList;
import java.util.Queue;public class Solution {Package01 package01;double currentTotalValue;double currentTotalWeight;double pbest;int [] X;private void init(){double currentTotalValue = 0;double currentTotalWeight = 0;double pbest = 0;}Solution(Package01 package01){this.package01 = package01;X = new int[package01.n];}class Node{double currentTotalValue;double currentTotalWeight;double up;int level;Node parent;boolean isLeft;public Node(double currentTotalValue, double currentTotalWeight, double up, int level, Node parent, boolean isLeft) {this.currentTotalValue = currentTotalValue;this.currentTotalWeight = currentTotalWeight;this.up = up;this.level = level;this.parent = parent;this.isLeft = isLeft;}}void branch(){init();int i = 0;double up = bound(0, package01.W, 0);Queue<Node> queue = new LinkedList<>();Node current = null;Node stNode = null;while (true){if (i < package01.n && currentTotalWeight + package01.weights[i] <= package01.W ){if (currentTotalValue + package01.values[i] > pbest){pbest = currentTotalValue + package01.values[i];queue.offer(new Node(pbest,currentTotalWeight+package01.weights[i],up,i+1,current,true));}}up = bound(i+1, package01.W-currentTotalWeight,currentTotalValue);if (up > pbest)queue.offer(new Node(currentTotalValue,currentTotalWeight,up,i+1,current,false));current = queue.poll();if(current == null){break;}currentTotalWeight = current.currentTotalWeight;currentTotalValue = current.currentTotalValue;up = current.up;i = current.level;if(i == package01.n){stNode = current;}}for (int j = package01.n-1; j >= 0 ; j--) {if(stNode != null) {X[j] = stNode.isLeft ? 1 : 0;stNode = stNode.parent;}}}void backtrack(){backtrack(0);}void backtrack(int i){init();if(i >= package01.n){pbest = currentTotalValue;return;}if (package01.weights[i] + currentTotalWeight <= package01.W){X[i] = 1;currentTotalWeight += package01.weights[i];currentTotalValue += package01.values[i];backtrack(i+1);currentTotalWeight -= package01.weights[i];currentTotalValue -= package01.values[i];}if (bound(i+1, package01.W-currentTotalWeight,currentTotalValue) > pbest){X[i] = 0;backtrack(i+1);}}double bound(int i, double cleft, double currentTotalValue){if(i >= package01.n)return -1;double up = currentTotalValue;while(i < package01.n && package01.weights[i] <= cleft){cleft -= package01.weights[i];up += package01.values[i];i++;}if(i < package01.n){up += package01.values[i]/package01.weights[i] * cleft;}return up;}
}class Package01 {// 背包容量double W;// 物品数量int n;double [] weights;double [] values;Package01(double [] weights, double [] values, double W){this.weights = weights;this.values = values;this.W = W;n = weights.length;}
}

测试

import java.util.Arrays;public class Main {public static void main(String[] args) {double W = 7;double []weights = {3, 5, 2, 1};double []values = {9,10,7,4};Package01 package01 = new Package01(weights,values,W);Solution solution = new Solution(package01);solution.backtrack();System.out.println("回溯法结果向量:"+Arrays.toString(solution.X));solution.branch();System.out.println("分支限界法结果向量"+Arrays.toString(solution.X));}
}
回溯法结果向量:[1, 0, 1, 1]
分支限界法结果向量[1, 0, 1, 1]

心得体会

分支限界法和回溯法非常类似,都是在问题的解空间树上搜索问题解的算法。。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

由于求解的目标不同,两种方法的搜索方式也不同:

  • 回溯法以深度优先的方式搜索解空间树;
  • 分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树

更多推荐

基于回溯法和分支限界法求解01背包问题

本文发布于:2024-02-07 05:45:03,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1753569.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:限界   分支   背包

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!