I guess my question has to do with a variant of the knapsack problem, but I can't really come up with a solution for this:
Let's say you are in a hardware store and need to buy 21 screws. They only offer them in bags:
- Bag X - 16 Screws - 1.56$ per screw - 25$ Total
- Bag Y - 8 Screws - 2.25$ per screw - 18$ Total
- Bag Z - 4 Screws - 1.75$ per screw - 7$ Total
Now you have to figure out which Bags you should buy to get your 21 screws (or more!) for the lowest possible price.
So what I got is a table with all the bags and a variable to define the required amount. What I need as a result should be a table with the bagname and the required amount.
Unfortunately sqlfiddle is down.. But at least here's the example data:
declare @bags table (id int, qty int, price decimal(19,4))
insert into @bags values
(10, 16, 25.00)
,(20, 8, 18.00)
,(30, 4, 7.00)
declare @ReqQty int = 21
I really appreciate your help! Hope we can get this solved, as I need to customize our companys ERP System with this important function.
Thank you in advance!
Edit: I read the whole wikipedia article about knapsack and there it says: Overfill approximation algorithm It may be possible to generate an approximation algorithm where we can slightly overflow the allowed weight limit. You would want to achieve at least as high a total value as the given bound B, but you're allowed to exceed the weight limit... Currently the solution is unknown for this approximation algorithm.
So it seems I better use a greedy algorithm instead of inventig the wheel? ;)
Here is a possible solution. I will see if I can finish it tomorrow as it's almost 3 AM here now. The main logic is there. All that's left to be done is to trace back using the
prev_w
values. Just jump back (starting from thebest_price
row) till you reach thew=0
row. The differences between thew
s of current and the previous row give you the size of the bag you have to buy at each step.In your example, the solution route obviously is:
"w=24, w=8, w=4, w=0" which translates "to buy bags: 16, 4, 4.".
These 3 bags cost $39.
This solution assumes the person is not going to buy
more than 1000 screws (this is what @limit is there for).
Script draft:
Script final version:
I've decided to come up with a slightly different approach. This one is set based and the general idea is to find all possible combinations of amounts of bags that match the required condition, then select the cheapest one.
Steps:
@ReqQty
, for each kind of Bag find how many of such Bag does it make sense to include in expression (thats is if bag contains 5 pieces, and we want to purchase 12 pieces, it makes sense to consider 1, 2 or 3 bags, but 4 bags would be obviously too much)A
with amounts 1, 2 and 3, and Bag kindB
with amounts 1 and 2 one can try:A * 1 + B * 1
,A * 2 + B * 1
,A * 3 + B * 1
,A * 1 + B * 2
,A * 2 + B * 2
,A * 3 + B * 2
)This is whole solution with sample data OP provided:
(The solution was modified, new version is available below!)
For a given sample data this is the result:
The solution is definitely not perfect:
charindex
to eliminate same types of bagsbut I simply lacked time or skills to come up with more clever ideas. What I think is nice is that it's purely set-based, declarative solution.
EDIT
I've modified the solution a bit to get rid of
charindex
(and thus get rid of dependency of text based bag identifiers). Unfortunately I had to add0
amount for each kind of bag which made even more combinations but it seems to have no noticeable impact on performance. Also for the same price the combination with more pieces is shown. :-)