tags:

views:

129

answers:

3

Hi all – I’m trying to put together a system to suggest consumable kits based on a requested quantity. The challenge I’m running into is that the kits have volume/bulk discounts, so it may be cheaper for the customer to order a larger quantity because the price may be less. For example, let’s say the available kits are:

  • 25 widgets for $15
  • 10 widgets for $7
  • 5 widgets for $4
  • 1 widget for $1

Right now, for a requested quantity of 74, my algorithm will suggest 2 x 25, 2 x 10, and 4 x 1 = $48. It would be cheaper however for the customer to just order 3 x 25 = $45.

Any thoughts on how to tackle this? I’m coding in C#.

Thanks!

+2  A: 

Well, starting with the 25th unit, the price is $0.60/item. So if the customer orders more than 25, forget the packages that you are calculating and just multiply the quantity by $0.60.

NinjaCat
Hi NinjaCat – thanks for the quick reply. Unfortunately these kits are pre-packaged in certain quantities, and the prices are fixed. So I can’t change the per-unit prices for the kits. Does that make sense?
Jayoaichen
+7  A: 

That looks like standard DP (dynamic programming).

// bestPrice is array of best prices for each amount
// initially it's [0, INFINITY, INFINITY, INFINITY, ...]

for (int i = 0; i <= 74; ++i) {
    for (Pack pack : packs) {
        // if (i + pack.amount) can be achieved with smaller price, do it
        int newPrice = bestPrice[i] + pack.price;
        if (newPrice < bestPrice[i + pack.amount]) {
            bestPrice[i + pack.amount] = newPrice;
        }
    }
}

The answer is min(bestPrice[74], bestPrice[74 + 1], ... bestPrice[74 + 25 - 1]). Overhead 25 - 1 is obviously enough, because otherwise you would remove one pack and amount would still be >= 74.

Some links on the topic:
http://en.wikipedia.org/wiki/Dynamic_programming
http://www.topcoder.com/tc?module=Static&amp;d1=tutorials&amp;d2=dynProg

edit
You can find optimal solution if you modify it a little. Add lastPack array, so lastPack[i] is the size of the package you used to achieve amount i. I guess, you can figure out how to update pseudo-code above.

Once algorithm is finished, you can get solution like this

int total = 74;
while (total > 0) {
    // package of size lastPack[total] was used to get amount 'total'
    // do whatever you want with this number here

    total -= lastPack[total];
}
Nikita Rybak
`"The word dynamic was chosen by Bellman because it sounded impressive, not because it described how the method worked."` Well that makes more sense reading that.
Greg
Dynamic programming should probably be called "cached programming", or something indicating that it relies on memory.
Dean J
Hi Nikita! Thank you for your answer. Please bear with me as I try to understand this, how to I know from this the pack quantities I need? It appears the bestPrice array gives me the best possible price for a given quantity, but does it tell me how to get to that number?
Jayoaichen
@Jayoaichen I added it to the post, let me know if something isn't clear.
Nikita Rybak
[Removed my comment which was wrong.]
Heinrich Apfelmus
Thanks Nikita! I put together a quick implementation of this and now it makes sense to me.
Jayoaichen
+3  A: 

So you are actually going to have to manually determine all of the breakpoints for items under a order of 25. Then basically use a lookup table type scenario to determine what to order for qty's less than 25. AS pointed out previously this is very similar to the Knapsack problem.

Basically your code would look something like;

int qtyOrder;
int qtyRemain;
int qty25pack;
int qty10pack;
int qty5pack;
int qty1pack;

//Grab as many 25 packs as possible
qty25pack = (qtyOrder % 25);
qtyRemain -= qty25Pack * 25;

//Here use your lookup table to determine what to order
// for the qty's that are less than 25

You could use some kind of greedy algorithm to determine it on the fly. Which would be ideal if the prices are expected to change a lot.

That could look something like filling the package size with an exact match and then determining the closest match that is just over the qty remaining and see if it is cheaper.

So for example:

//find the perfect product amount price
While (qtyRemain != 0) {
perfectPrice += (qtyRemain % nextSmallestSize) * nextSmallestPackagePrice;
qtyRemain -= (qtyReamin % nextSmallestSize)
}

//Find the closest match over price
While ((qtyRemain % nextSmallestSize) != 0){
closePrice += (qtyRemain % nextSmallestSize) * nextSmallestPackagePrice;
qtyRemain -= (qtyRemain % nextSmallestSize)
}
//add the last price before we reached the perfect price size
closePrice += nextSmallestPackagePrice;

//determine lowest price
if closePrice < perfectPrice {
 cost = closePrice;
}
else {
 cost = PerfectPrice;
}

This code is no where near complete but should give you an idea. Code is also probably not the greatest either.

Edit

The second chunk of code would go after the first chunk in place of the lookup

msarchet
+1: this is a great example that should lead the OP to an optimized solution.
Chris Lively
@Chris Lively thanks! that was the goal, I feel the point of SO is to give answers not solutions, anyone can copy paste.
msarchet
That won't always give you optimal solution (just like any other heuristic for knapsack problem). For example, check prices {1 : 2, 5 : 7, 10 : 10, 25 : 24} and amount 30.
Nikita Rybak
Nikita you are correct there is a slight hole in my logic at a few of those points if I'm reading your comment correctly. What it should do is force some round up conditions
msarchet
@msarchet – thank you for submitting a solution. I’ve got what I need working with Nikita’s answer, but I believe I understand where you were going with this.
Jayoaichen