回溯法解0-1背包问题。

0-1背包问题:有一个贼在偷窃一家商店时,发现有n件物品,第i件物品价值vi,重wi。他希望带走的东西越值钱越好,但他的背包中至多只能装下C的东西。应该带走哪几样东西,使得装入背包的物品价值最大?这个问题之所以称为0-1背包,是因为每件物品或被带走;或被留下;小偷不能只带走某个物品的一部分或带走同一物品两次。

《算法设计与分析》P133给出的算法并不完整,不利于通过代码来辅助理解该算法的基本思想。书中的代码存在太多槽点,尤其对于double类型直接使用==进行相等比较,可以说是非常的不严谨,不知道代码作者是否实际执行过代码进行验证。

采用java对该算法的改进和完整实现如下,共3个文件。

文件1: BacktrackingBackpack.java , 回溯法算法的实现;

文件2: Element.java 辅助文件 ,  表示物品的单位重量的价值;

文件3: MergeSort.java 辅助文件,合并算法对 Element数组进行降序排列。

BacktrackingBackpack.java :

代码中添加了一些辅助调试的代码,通过debug标记可以关闭或打开。

package ch05.book;

import ch05.book.MergeSort;

/**
 * This class express the backtracking solution for 0-1 backpack question.
 * @author wanyongquan
 *
 */
public class BacktrackingBackpack {


	final static boolean debug= true;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		double[] weight = {35, 30, 60, 50, 40, 10, 25};
		double[] profit = {10, 40, 30, 50, 35, 40, 30};
		
		backpack(profit, weight, 150);
		System.out.printf("best profit: %f", bestProfit);
	}

	static double capacity; // the capacity of backpack;
	static double[] weight; // the weight array of  items;
	static double[] profit; // the profit array of items
	static double currentWeight, currentProfit;// current weight and profit;
	static double bestProfit;   // the possible best profit base on current selection;
	
	// selection array 
	static int[] selectedNode; // just for debug,  display selected node information;
	
	public static void backpack(double[] theProfit, double[] theWeight, double theCapacity) {
		// initialization
		capacity = theCapacity;
		int n = theWeight.length;
		currentWeight = 0.0;
		currentProfit = 0.0;
		bestProfit = 0.0;
		
		try {
			// initialize the array of virtual profit/weight node.
			Element[] priority = new Element[n];
			for (int i = 0; i < n; i++) {
				priority[i] = new Element(i, theProfit[i] / theWeight[i]);
			}
			
			// sort Element array in descend order;
			MergeSort.mergeSort(priority);
			
			selectedNode = new int[n];
			for (int i=0; i<n; i++) {
				//initial selection of each node as 0; 1 means node is selected; 
				selectedNode[i] = 0;
			}
			
			profit = new double[n];
			weight = new double[n]; 
			
			// sort the weight and profit data in descend order;
			for (int i = 0; i< n ; i++) {
				// update the weight and profit array using the data of Element array in profit/weight descend order;
				profit[i] = theProfit[priority[i].id];
				weight[i] = theWeight[priority[i].id];
			}
			
			// run backtrack from first Element; 
			backtrack(0);
			
			
		}
		catch(Exception e) {
			System.out.println(e.getMessage());
			
		}
		
	}
	
	/**
	 * put feasible element into backpack using backtracking algorithm;
	 * @param i the index of element node in priority array.
	 */
	private static void backtrack(int i) {
		if( i> profit.length -1) {
			// reach leaf node of solution tree, record currentProfit as best profit and return;
			bestProfit = currentProfit;
			System.out.printf("Find a  best profit is %4.1f  at node %d .\r\n ", bestProfit, i-1);
			
			return ; 
		}
		
		// search sub tree
		if (currentWeight + weight[i]<= capacity) {
			// the left child node is feasible if the weight is NOT exceed capacity
			// put left child node in
			currentWeight += weight[i];
			currentProfit += profit[i];
			selectedNode[i] = 1;
			displayState (); //System.out.printf("PUT Element %d in backpack, current weight: %f, current profit: %f \r\n", i, currentWeight, currentProfit);
			//search the next level nodes of solution tree
			backtrack(i+1);
			
			// backtrack
			currentWeight -= weight[i];
			currentProfit -= profit[i];
			selectedNode[i] = 0;
			//System.out.printf("Backtrack , REMOVE Element %d from backpack, current weight: %f, current profit: %f \r\n", i, currentWeight, currentProfit);
			displayState();
		}
		else {
			// just for debug;
			if (debug) {
				System.out.printf("Cannot put Element %d in backpack\r\n", i);
			}
		}
		if (debug) {
			System.out.printf("check upper bound for right child tree of  ");
			displayState();
		}
		// search the right child tree if bound of current node is greater than bestProfit
		// otherwise, just ignore right child tree;
		if (bound(i+1) > bestProfit) {
			backtrack(i+1);
		}
		else {
			// just for debug;
			if (debug) {
				System.out.printf("The upper bound of  right child of node %d is less than current best profit, so cut it\r\n", i);
			}
		}
	}
	
	/**
	 * return the upper bound at current tree node by put Priority Elements into backpack virtually;
	 * @param i the index of element node in priority array
	 * @return
	 */
	private static double bound(int i) {
		
		int nodeIndex = i ;// just for debug display;
		double capacityLeft = capacity - currentWeight; // the remained capacity
		double bound = currentProfit;  // upper bound of profit;
		
		// add  Elements(the whole element) to backpack in priority descend order; 
		while(i < profit.length && weight[i] <= capacityLeft) {
			// the Element i can be put into backpack;
			
			// update capacityleft and bound 
			capacityLeft -= weight[i];
			bound += profit[i];
			i++;
		}
		
		// if the backpack still has some capacity remained and NO Single element can been add to backpack,
		// then add part of the Element to backpack until the backpack is fullfilled.
		if (i<profit.length) {
			bound += profit[i] * capacityLeft / weight[i];
			
		}
		if (debug) {
			System.out.printf("the upper bound with node %d not selected : %f\r\n", nodeIndex-1, bound);
		}
		return  bound;
	}

	/**
	 * this method display the current state of selection ,current weight, current profit, current best profit;
	 */
	private static void displayState() {
		System.out.print("current selection: ");
		for(int k=0; k<selectedNode.length;k++) {
			System.out.printf("%d," , selectedNode[k]);
		}
		
		System.out.printf("; current Weight: %4.1f, current Profit: %4.1f, bestProfit :%4.1f\r\n", currentWeight, currentProfit, bestProfit);
		
	}
}


Element.java:

package ch05.book;

/**
 * This class represent a virtual profit/weight node that be used for process;
 * @author wanyongquan
 *
 */
 public class Element implements Comparable<Element>{
	int id;
	double valuePerUnitWeight;
	
	public Element(int id, double valuePerUnitWeight) {
		this.id = id;
		this.valuePerUnitWeight = valuePerUnitWeight;
	}
	@Override
	public int compareTo(Element other) {
		// TODO Auto-generated method stub
		
		return Double.compare(this.valuePerUnitWeight , other.valuePerUnitWeight);
			
	}
	
	@Override
	public boolean equals(Object other) {
		return Math.abs(this.valuePerUnitWeight-((Element)other).valuePerUnitWeight) < 0.0001;
	}
	
}

MergeSort.java 

采用合并排序算法对数组进行降序排序的代码,不是本话题重点,暂不提供。

main()方法中给出的测试数据的执行,对应的调试代码如下,可以看到算法的运行过程,对于理解算法很有帮助。

current selection: 1,0,0,0,0,0,0,; current Weight: 10.0, current Profit: 40.0, bestProfit : 0.0
current selection: 1,1,0,0,0,0,0,; current Weight: 40.0, current Profit: 80.0, bestProfit : 0.0
current selection: 1,1,1,0,0,0,0,; current Weight: 65.0, current Profit: 110.0, bestProfit : 0.0
current selection: 1,1,1,1,0,0,0,; current Weight: 115.0, current Profit: 160.0, bestProfit : 0.0
Cannot put Element 4 in backpack
check upper bound for right child tree of  current selection: 1,1,1,1,0,0,0,; current Weight: 115.0, current Profit: 160.0, bestProfit : 0.0
the upper bound with node 4 not selected : 177.500000
Cannot put Element 5 in backpack
check upper bound for right child tree of  current selection: 1,1,1,1,0,0,0,; current Weight: 115.0, current Profit: 160.0, bestProfit : 0.0
the upper bound with node 5 not selected : 170.000000
current selection: 1,1,1,1,0,0,1,; current Weight: 150.0, current Profit: 170.0, bestProfit : 0.0
Find a  best profit is 170.0  at node 6 .
 current selection: 1,1,1,1,0,0,0,; current Weight: 115.0, current Profit: 160.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 1,1,1,1,0,0,0,; current Weight: 115.0, current Profit: 160.0, bestProfit :170.0
the upper bound with node 6 not selected : 160.000000
The upper bound of  right child of node 6 is less than current best profit, so cut it
current selection: 1,1,1,0,0,0,0,; current Weight: 65.0, current Profit: 110.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 1,1,1,0,0,0,0,; current Weight: 65.0, current Profit: 110.0, bestProfit :170.0
the upper bound with node 3 not selected : 167.500000
The upper bound of  right child of node 3 is less than current best profit, so cut it
current selection: 1,1,0,0,0,0,0,; current Weight: 40.0, current Profit: 80.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 1,1,0,0,0,0,0,; current Weight: 40.0, current Profit: 80.0, bestProfit :170.0
the upper bound with node 2 not selected : 175.000000
current selection: 1,1,0,1,0,0,0,; current Weight: 90.0, current Profit: 130.0, bestProfit :170.0
current selection: 1,1,0,1,1,0,0,; current Weight: 130.0, current Profit: 165.0, bestProfit :170.0
Cannot put Element 5 in backpack
check upper bound for right child tree of  current selection: 1,1,0,1,1,0,0,; current Weight: 130.0, current Profit: 165.0, bestProfit :170.0
the upper bound with node 5 not selected : 170.714286
Cannot put Element 6 in backpack
check upper bound for right child tree of  current selection: 1,1,0,1,1,0,0,; current Weight: 130.0, current Profit: 165.0, bestProfit :170.0
the upper bound with node 6 not selected : 165.000000
The upper bound of  right child of node 6 is less than current best profit, so cut it
current selection: 1,1,0,1,0,0,0,; current Weight: 90.0, current Profit: 130.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 1,1,0,1,0,0,0,; current Weight: 90.0, current Profit: 130.0, bestProfit :170.0
the upper bound with node 4 not selected : 160.000000
The upper bound of  right child of node 4 is less than current best profit, so cut it
current selection: 1,1,0,0,0,0,0,; current Weight: 40.0, current Profit: 80.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 1,1,0,0,0,0,0,; current Weight: 40.0, current Profit: 80.0, bestProfit :170.0
the upper bound with node 3 not selected : 147.857143
The upper bound of  right child of node 3 is less than current best profit, so cut it
current selection: 1,0,0,0,0,0,0,; current Weight: 10.0, current Profit: 40.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 1,0,0,0,0,0,0,; current Weight: 10.0, current Profit: 40.0, bestProfit :170.0
the upper bound with node 1 not selected : 167.500000
The upper bound of  right child of node 1 is less than current best profit, so cut it
current selection: 0,0,0,0,0,0,0,; current Weight:  0.0, current Profit:  0.0, bestProfit :170.0
check upper bound for right child tree of  current selection: 0,0,0,0,0,0,0,; current Weight:  0.0, current Profit:  0.0, bestProfit :170.0
the upper bound with node 0 not selected : 157.500000
The upper bound of  right child of node 0 is less than current best profit, so cut it
best profit: 170.000000
发布了12 篇原创文章 · 获赞 5 · 访问量 7465

猜你喜欢

转载自blog.csdn.net/redredxcar/article/details/86027195