The knapsack problem ( Wiki link ) is a problem in combinatorial optimisation. Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. Let's see how we can solve it using simulated annealing.
A Basic Understanding
Suppose we are planning a hiking trip; and we are, therefore, interested in filling a knapsack with items that are considered necessary for the trip. There are N different item types that are deemed desirable; these could include bottle of water, apple, orange, sandwich, and so forth. Each item type has a given set of two attributes, namely a weight (or volume) and a value that quantifies the level of importance associated with each unit of that type of item. Since the knapsack has a limited weight (or volume) capacity, the problem of interest is to figure out how to load the knapsack with a combination of units of the specified types of items that yields the greatest total value. What we have just described is called the knapsack problem.
Implementation
knapsack.js
/** * Will hold each knapsack item */ function Item(w,v,index){ this.weight = w; //Weight of the item this.value = v; //Weight of the value this.index = index; //Index of the value this.print = function(){ document.write("Item : w : "+this.weight+", v : "+this.value+"<br/>"); } } /** * Knapsack bag */ function Bag(size){ this.size = size; this.itemset = []; //Will hold the overall dataset this.currentSolution = []; //will hold the current solution /** * Populates the itemset with the dataset provided above */ this.prepare = function(){ for(var i = 0;i<dataset.length;i++){ var item = new Item(dataset[i][0],dataset[i][1],i); this.itemset.push(item); } var temp = getRandomAsInt(0,this.itemset.length); this.currentSolution.push(this.getItemBasedOnIndex(this.itemset, temp)); } /** * Gets the items from the itemset provided as an argument based on the index */ this.getItemBasedOnIndex = function(itemset,index){ var item = null; for(var i=0;i<itemset.length;i++){ if(itemset[i].index==index){ item = itemset[i]; break; } } return item; } /** * Calculates and returns the summation of list of item weights */ this.getWeightForList = function(itemset){ var sum = 0; for(var i=0;i<itemset.length;i++){ sum += itemset[i].weight; } return sum; } /** * Calculates and returns the summation of list of item values */ this.getValueForList = function(itemset){ var sum = 0; for(var i=0;i<itemset.length;i++){ sum += itemset[i].value; } return sum; } /** * Checks whether current selection is overweight or not */ this.checkoverweight = function(itemset){ if(this.getWeightForList(itemset) > this.size){ return true; }else return false; } /** * Returns any random item from the itemset which is not in the currentSolution of the bag */ this.getRandomItemFromItemSet = function(){ var temp = getRandomAsInt(0,this.itemset.length); var item = this.getItemBasedOnIndex(this.itemset,temp); while(this.getItemBasedOnIndex(this.currentSolution,temp)!=null){ temp = getRandomAsInt(0,this.itemset.length); item = this.getItemBasedOnIndex(this.itemset,temp); } return item; } /** * Modifies the current selection */ this.modifySelection = function(){ var modified = this.currentSolution.clone(); var item = this.getRandomItemFromItemSet(); modified.push(item); while(this.checkoverweight(modified)){ var dropIndex = getRandomAsInt(0,modified.length); modified.removeItem(dropIndex); console.log(dropIndex); console.log(modified); } return modified; } /** * Calculates the remaining space in the bag */ this.calculateRemainingSpace = function(itemset){ return (this.size - this.getValueForList(itemset)); } this.printsolution = function(itemset){ for(var i=0;i<itemset.length;i++){ var item = itemset[i]; item.print(); } document.write("Total value is : "+this.getValueForList(itemset)); } this.prepare(); } /** * Returns a random number between minimum (inclusive) and maximum (exclusive) */ function getRandom(min, max) { return Math.random() * (max - min) + min; } /** * Returns a random integer between minimum (inclusive) and maximum (exclusive) */ function getRandomAsInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } Array.prototype.clone = function() { return this.slice(0); }; Array.prototype.removeItem = function( index){ var i = 0; while( i < this.length ){ if(i == index ){ this.splice(i,1); } i++; } };
index.html
<script src="knapsack.js"></script> <script> /** * Constants for the program */ var MAX_WEIGHT = 15; var TEMPERATURE = 500; var COOLING_FACTOR = 0.2; /** * The dataset used in the program * [weight,value] */ var dataset = [ [1,2],[4,2],[5,4],[3,6],[9,4],[10,8],[8,5],[6,7],[7,3],[11,8] ]; /** Acceptance function **/ function acceptanceProbability(freespace,newfreespace,temperature){ if(newfreespace < freespace){ return 1.0; } return Math.exp((freespace - newfreespace) / temperature); } window.onload = function(){ var bag = new Bag(MAX_WEIGHT); var best = bag.currentSolution; var temperature = TEMPERATURE; while(temperature > 0){ var freespaceLeft = bag.calculateRemainingSpace(bag.currentSolution); var modifiedSolution = bag.modifySelection(); var newfreespaceleft = bag.calculateRemainingSpace(modifiedSolution); if(acceptanceProbability(freespaceLeft, newfreespaceleft, temperature)>=Math.random()){ bag.currentSolution = modifiedSolution; } if(bag.getValueForList(bag.currentSolution) > bag.getValueForList(best)){ best = bag.currentSolution; } temperature -= COOLING_FACTOR; } bag.printsolution(best); } </script>
No comments:
Post a Comment