(1)、算法原理
分支限界法类似于回溯法,也是在问题的解空间上搜索问题解的算法。一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出解空间中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。
由于求解目标不同,导致分支限界法与回溯法对解空间的搜索方式也不相同。回溯法以深度优先的方式搜索解空间,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间。分支限界法的搜索策略是,在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一扩展结点。为了有效地选择下一扩展结点,加速搜索的进程,在每一个活结点处,计算一个函数值(限界),并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。这种方式称为分支限界法。人们已经用分支限界法解决了大量离散最优化的问题。
(2)、解决0-1背包算法分析
在解0-1背包问题的优先队列式分支限界法中,活结点优先队列中结点N的优先级由该结点的上界函数UBound计算出的值uprofit给出。子集树中以结点N为根的子树中任一结点的价值不超过N.profit。应用了Java封装的优先队列类实现。队列中元素类型为HeapNode,其私有成员有uprofit,profit,weight,level和inpacket。对于任意活结点N,N.weight是结点N所相应的重量;N.profit是N所相应的价值;N.uprofit是结点N的价值上界,优先队列以这个值作为优先级。
(3)、算法实现
import java.util.*;
import javax.swing.*;
public class Knapsack extends JFrame{
private int num;//物品数量
private int[] value;//物品价值
private int[] weight;//物品重量
private int capacity;//背包容量
private int flag[];//原来顺序数组
final JScrollPane scrollPane = new JScrollPane();
public static JTextArea resulttextArea;
public JLabel l1 = new JLabel("最优解");
public JLabel l3 = new JLabel("所用时间");
public static JTextField t1 = new JTextField();
public static JTextField t2 = new JTextField();
final JLabel label = new JLabel();
final JLabel label_1 = new JLabel();
//构造函数
public Knapsack(int n,int[] v,int[] w, int c){
num = n;
value = v;
weight = w;
capacity = c;
flag = new int[num];
//初始化标志数组
for(int i=0; i<num; i++){
flag[i] = i;
}
//根据单位重量价值进行排序
for(int i=1; i<=num; i++){
int vtmp = 0;
int wtmp = 0;
int ftmp = 0;
for(int j=0; j <num - i; j++){
if(((double) value[j])/weight[j] < ((double) value[j+1])/weight[j+1]){
vtmp = value[j];
wtmp = weight[j];
value[j] = value[j+1];
weight[j] = weight[j+1];
value[j+1] = vtmp;
weight[j+1] = wtmp;
ftmp = flag[j];
flag[j] = flag[j+1];
flag[j+1] = ftmp;
}
}
}
this.setResizable(false);
this.setTitle("分支限界算法计算0-1背包");
this.getContentPane().setLayout(null);
this.setBounds(100, 100, 670, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
scrollPane.setBounds(190, 25, 454, 293);
getContentPane().add(scrollPane);
resulttextArea = new JTextArea();
resulttextArea.setEditable(false);
scrollPane.setViewportView(resulttextArea);
label.setHorizontalTextPosition(SwingConstants.RIGHT);
label.setHorizontalAlignment(SwingConstants.RIGHT);
label.setText("最优解:");
label.setBounds(10, 42, 66, 18);
getContentPane().add(label);
t1.setHorizontalAlignment(SwingConstants.RIGHT);
t1.setHorizontalAlignment(SwingConstants.RIGHT);
t1.setBounds(80, 42, 66, 18);
getContentPane().add(t1);
label_1.setHorizontalTextPosition(SwingConstants.RIGHT);
label_1.setHorizontalAlignment(SwingConstants.RIGHT);
label_1.setText("所用时间:");
label_1.setBounds(10, 75, 66, 18);
getContentPane().add(label_1);
t2.setHorizontalAlignment(SwingConstants.RIGHT);
// t1.setHorizontalTextPosition(SwingConstants.RIGHT);
t2.setHorizontalAlignment(SwingConstants.RIGHT);
t2.setBounds(80, 75, 66, 18);
getContentPane().add(t2);
}
public void LCKnap(){
int initCap = 32;
int bestV = 0;
int w = 0,v = 0;
KnapNode bestNode = null;
//使用JAVA中封装的优先队列
PriorityQueue<KnapNode> q = new PriorityQueue<KnapNode>(initCap, new Comparator<KnapNode>(){
//函数重载
public int compare(KnapNode o1, KnapNode o2) {
return o2.CApprxm()-o1.CApprxm();
//return o1.getUBound() - o2.getUBound();
}
});
q.add(new KnapNode(null,0,0,0, 0));
while(!q.isEmpty()){
//查找并删除优先队列中的优先级最高的元素
KnapNode curNode = q.poll();
if(curNode.getUBound() >= bestV){
int i = curNode.level;
if (i < num && curNode.cw + weight[i] <= capacity) {
w = curNode.cw + this.weight[i];
v = curNode.cv + this.value[i];
KnapNode nextL = new KnapNode(curNode, i+1, w, v, 1);
if(nextL.getUBound() > bestV){
q.add(nextL);
}
if(bestV < v){
bestV = v;
bestNode = nextL;
}
i++;
}else{
i++;
}
KnapNode nextR = new KnapNode(curNode, i, curNode.cw, curNode.cv, 0);
if(nextR.getUBound() > bestV){
q.add(nextR);
}
}
}
int bv = 0;
resulttextArea.append("选入背包中的物品如下:/n");
while(bestNode.parent != null){
resulttextArea.append(" x["+ flag[bestNode.level-1]+"]="+bestNode.inKnap+";/n ");
System.out.print(" ["+ flag[bestNode.level-1]+"]="+bestNode.inKnap+"; ");
bv += bestNode.inKnap * value[bestNode.level -1];
bestNode = bestNode.parent;
}
System.out.println();
System.out.println("Best Values is: " + bv);
t1.setText(""+bv);
}
/**
* @param args
*/
public static void main(String[] args) {
int[] v = new int[]{220,208,198,192,180,180,165,162,160,158,155,130,125,122,120,
118,115,110,105,101,100,100,98,96,95,90,88,82,80,77,75,73,70,69,66,65,63,60,
58,56,50,30,20,15,10,8,5,3,1,1};
int[] w = new int[]{80,82,85,70,72,70,66,50,55,25,50,55,40,48,50,32,22,60,30,32,40,38,35,
32,25,28,30,22,50,30,45,30,60,50,20,65,20,25,30,10,20,25,15,10,10,10,4,4,2,1};
int c = 1000;
int n = v.length;
long start = System.currentTimeMillis();
Knapsack sack = new Knapsack(n, v, w, c);
sack.LCKnap();
sack.setVisible(true);
long end = System.currentTimeMillis();
t2.setText((end - start)+"ms");
System.out.println("/n Time consuming: " + (end - start));
}
class KnapNode{
public static final int YES = 1;
public static final int NO = 0;
private KnapNode parent;
private int level;
private int cw;
private int cv;
private int inKnap;
private int upV;
public KnapNode(KnapNode p, int lv, int w, int v, int isLeft){
this.parent = p;
level = lv;
cw = w;
cv = v;
inKnap = isLeft;
}
public int getIsInKnap(){
return this.inKnap;
}
public KnapNode getParent(){
return this.parent;
}
public int getUBound(){
if(upV == 0){
upV = UBound();
}
return upV;
}
//计算结点价值上界
private int UBound(){
if(level == 0) return 0;
int k = level - 1;
int num = weight.length;
int v = cv;
int w = cw;
while( (k < num) && ((w += weight[k]) <= capacity )){
v += value[k];
k++;
}
if(k < num){
w -= weight[k];
return (int)(v + ((float)(capacity - w))/weight[k] * value[k])+1;
}
return v;
}
//计算结点的当前最优价值
public int CApprxm(){
int k = level - 1;
int num = weight.length;
int v = cv;
int w = cw;
for(; k<num; k++){
if(w + weight[k] <= capacity){
w += weight[k];
v += value[k];
}
}
return v;
}
}
}