# MARKETING OPTIMIZATION R-SCRIPT # Bob Agnew, raagnew@comcast.net, http://home.comcast.net/~raagnew # The algorithm imbedded in this script optimizes the assignment of offers to # prospects, subject to stipulated offer quantity constraints and the limit of # at most one offer per prospect. Remarkably, this demo problem, with a million # prospects and three offers, solves in about a minute on my home PC. Previously, # I solved it using SAS PROC NLP on a big server and the time required was much # greater. Ref: # http://home.comcast.net/~raagnew/Downloads/Dual_Solution_Marketing_Optimization.pdf # This script can be adapted for a different number of offers and alternative # equality or inequality constraints, as long as they can be linearized, although # the dual must be formulated for solution. Inequality constraints would require # a different R solver (nlminb) which incorporates lower bounds. It is also possible # to allow more than one offer per prospect. R for Windows or Mac can be downloaded # free from www.r-project.org. This script can be opened, highlighted, and run from # the R console. It can also be run essentially in batch using the source function, # e.g., source("c:/Optimization/R/mktg_opt.R"). # Number of Prospects n <- 1000000 # Initialize prospect offer and profit at zero, i.e., no offer. Profit <- Offer <- array(0,dim = n) # Simulate Prospect Profits for Three Offers # Different profit vectors will be generated for each execution, which has the # advantage of showing that solution is robust across various instances. p1 <- 100*runif(n,0,1) - 10 p2 <- .6*p1 + .4*(60*runif(n,0,1) - 6) p3 <- .4*p1 + .6*(40*runif(n,0,1) - 4) # Ordinarily, offer profit "scores" would be input from an external text file using the # scan function, i.e., inp <- scan("infile", list(0,0,0)); p1 <- inp[[1]]; # p2 <- inp[[2]]; p3 <- inp[[3]]. You can check this out by saving an instance of the # simulated vectors via write(rbind(p1,p2,p3), file = "infile", ncolumns = 3) and then # use a different version of this script with scan rather than simulation. # Mean Offer Profits c(mean(p1),mean(p2),mean(p3)) # Stipulated Offer Quantities b <- c(300000,200000,100000) # Dual Function dual <- function(u) { d1 <- p1 - u[1]; d2 <- p2 - u[2]; d3 <- p3 - u[3] v <- (d1>=0&d1>=d2&d1>=d3)*d1 + (d2>=0&d2>d1&d2>=d3)*d2 + (d3>=0&d3>d1&d3>d2)*d3 y <- b%*%u + sum(v) y } # Dual Minimization using Nonlinear Minimization Function out <- nlm(dual,c(0,0,0)) # Dual Minimization Output out # Keep dual minimum and estimates. mindual <- out$minimum u <- out$estimate d1 <- p1 - u[1]; d2 <- p2 - u[2]; d3 <- p3 - u[3] v <- (d1>=d2&d1>=d3)*d1 + (d2>d1&d2>=d3)*d2 + (d3>d1&d3>d2)*d3 ord <- order(v, decreasing = TRUE) s <- c(0,0,0) h <- 100000000 for (j in 1:sum(b)) { k <- s < b m <- ord[j] e1<-k[1]*d1[m]-(1-k[1])*h;e2<-k[2]*d2[m]-(1-k[2])*h;e3<-k[3]*d3[m]-(1-k[3])*h e <- c(e1,e2,e3) i <- order(e)[3] Offer[m] <- i p <- c(p1[m],p2[m],p3[m]) Profit[m] <- p[i] s[i] <- s[i] + 1 } # Dual Minimum (Upper Bound) mindual # Primal Optimum (Maximum Profit) sum(Profit) # Note that we are extremely close, in some instances right on. # Offer Quantities s # Offer Profits c(sum(Profit[Offer == 1]),sum(Profit[Offer == 2]),sum(Profit[Offer == 3])) # In actual application, you would export a text file containing the optimal # prospect offers, i.e., write(Offer, file = "outfile", ncolumns = 1).