Below is an R program that will optimize a particular knapsack using the Metropolis-Hasting algorithm, a Monte Carlo Markov chain. The beautiful MH algorithm has been a recent focus of mine and I am finding that it’s applications are basically limitless. I’ve also posted this one to my hub. The knapsack problem (in this case, a 0/1 knapsack problem) is a classic optimization problem in computer science. Many approaches exist for solving problem, but I haven’t seen many that exceed MCMC in terms of sheer efficiency and elegance.
#############################################################################
#The function "knapsack(x)" will calculate the appropriate items to bring
#on the hypothesized hiking trip. Supply the function with the number of
#proposals. While testing, 100,000 proposals were able to produce the desired
# maximum for the given seed.
#############################################################################
#name the function
knapsack<-function(x){
#set the seed for reproducibility
set.seed(23)
#Enter the list of items, weights, and values
item=c("map","compass","water","sandwich","glucose","tin","banana","apple","cheese",
"beer","suntan_cream","camera","T-shirt","trousers","umbrella",
"waterproof_trousers","waterproof_overclothes","note-case","sunglasses","towel",
"socks","book")
weight=c(9,13,153,50,15,68,27,39,23,52,11,32,24,48,73,42,43,22,7,18,4,30)
value=c(150,35,200,160,60,45,60,40,30,10,70,30,15,10,40,70,75,80,20,12,50,10)
V=400
#initialize all of the objects that the program requires
i=1
l=1
selection<-sample(c(rep(0,11),rep(0,11)))
best_selection=c(1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,1,1,1,1,0,1,0)
candidates<-list();benefits<-list();total_weight<-list()
selection_new<-selection
chooseitem<-c(1:22)
#Selection Loop: here we repeatedly propose and accept/reject scenarios to take
#on the trip
for(m in 1:x){
#here we tune the algorithm to help produce more exploration in the cases where
#the chain gets stuck
if(m/i>500){l<- .15}
if(m/i<=500){l<- 1}
#Generation/Proposal Step: Here we randomly select a bit to flip
j<-sample(chooseitem,1)
selection_new<- selection
if(selection[j]==1){selection_new[j]<-0}
if(selection[j]==0){selection_new[j]<-1}
#Accept/Reject Step: Here we either advance or fail to advance our Markov
#chain based on the acceptance criteria that the weight be less than V
#and the choice meets the Metropolis-Hasting criteria.
if(sum(weight*selection_new)>V){selection<-selection}
if(sum(weight*selection_new)<=V &&
rbinom(1,1,min(1,exp(l*(sum(selection_new*value)-sum(selection*value)))))==1){
selection<-selection_new;
candidates[[i]]<-selection_new;
benefits[[i]]<-sum(value*selection_new);
total_weight[[i]]<-sum(weight*selection_new);
i<-i+1
}
}
#produce a plot of the markov chain to show that the algorithm is working as intended
plot(unlist(benefits))
lines(unlist(benefits))
#output the information regarding the max.
cat("The max value is ",max(unlist(benefits)),"\n")
cat("This value occurred at index: ",which.max(unlist(benefits)),"\n")
cat("The number of candidates generated is: ",length(benefits),"\n")
cat("The optimal selection string is: ",best_selection,"\n")
cat("Our selection string is: ",candidates[[which.max(unlist(benefits))]],"\n")
cat("The difference between our selection and the optimal selection is:
",sum(abs(best_selection-candidates[[which.max(unlist(benefits))]])), "items","\n")
}
#call the function
knapsack(100000)
#The max value is 1030
#This value occurred at index: 122
#The number of candidates generated is: 200
#The optimal selection string is: 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 1 1 1 1 0 1 0
#Our selection string is: 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 1 1 1 1 0 1 0
#The difference between our selection and the optimal selection is: 0 items
# user system elapsed
# 3.583 0.030 3.646