Showing posts with label dynamic programming. Show all posts
Showing posts with label dynamic programming. Show all posts

Friday, November 14, 2008

Facebook Puzzler in Scala

An associate of mine pointed out some fun programming puzzles on Facebook. This one was interesting to me. It was interesting because I think it is poorly. Well at the very least it confused on first read. When specifying the cost/weight, if they would have just used the word 'each' in there it would have been far less confusing... Anyways, I decided to solve the problem in Scala, since I've been doing a lot more Scala programming lately. Here is my Solver:

object Solver {
def cache = new scala.collection.mutable.HashMap[(List[Int], Int), List[List[Int]]]()

/**
* Generates a list of solutions to the Diophantine inequality
* w1*x1 + w2*x2 +... wN*xN >= max
* where weights = (w1,w2,...wN)
* Each solution is a minimal solution.
* This means that if (x1,x2,...xN) is a solution
* then (x1,x2, ... ,-1 + xM , ... xN) is NOT a solution
*/
def solve(weights: List[Int], max: Int): List[List[Int]] = {
if (cache.contains((weights,max))){
return cache((weights,max))
}
if (weights.length == 1) {
return List(List(max / weights(0) + 1))
}
var all: List[List[Int]] = Nil
var a = 0
while (a * weights(0) < max) {
all = all ++ solve(weights.drop(1).toList, max - a * weights(0)).map(a :: _)
a += 1
}
val solution = (a :: weights.drop(1).map(_ * 0)) :: all
cache.put((weights,max), solution)
solution
}

/**
* For a given set of weights (w1, w2, ... wN) and costs (c1, c2, ... cN)
* This finds the solution (x1,x2,...xN) to the inequality
* w1*x1 + w2*x2 + ... wN*xN <= max that minimizes the total cost
* c1*x1 + c2*x2 + ... cN*xN
* It returns the solutions as a Tuple where the first element is
* the solution (x1,x2,...xN) and the second is the minimal total cost
*/
def optimizer(costs: List[Int], weights: List[Int], max: Int): (List[Int], Int) = {
val solutions = solve(weights, max)
var answer: List[Int] = Nil
var best = (answer, Integer.MAX_VALUE)
solutions.foreach((solution) => {
val cost = solution.zip(costs).foldLeft(0){(a,b) => a + b._1*b._2}
if (cost < best._2) {
best = (solution, cost)
}
})
best
}
}

I put in the cache for memoization purposes, but it doesn't always help. For example, with their sample input/output, the cache is useless. Anyways, I showed the problem to other smarter folks who immediately pointed out that this was a variant of the unbounded knapsack problem and that my solution uses dynamic programming.

Here's a dirty little secret about yours truly. I studied mathematics in college, not computer science. So it's always amusing for me to come across a problem like this and have people start talking about CS terms. Personally I looked it as a Diophantine equation (inequality to be more accurate.) Of course maybe if I was a CS person, then I would have written a nicer solution.