Imagine you have a shop.
One of your products comes in boxes of 3 different sizes:
3
, 5
, and 10
items per box
The bigger the box, the lower the price per item. So in order to give your customer the best price, when the customer request a number of items, you want to sell them the cheapest combination of boxes each time.
This is how it should work:
# Problem 1 (easy):
total_items = 25
box_sizes = [10, 5]
box_size_allocations = {
10: 2,
5: 1,
}
assert allocate_items_to_boxes(total_items, box_sizes) == box_size_allocations
# Problem 2 (easy):
total_items = 35
box_sizes = [10, 1]
box_size_allocations = {
10: 3,
1: 5,
}
assert allocate_items_to_boxes(total_items, box_sizes) == box_size_allocations
# Problem 3 (hard):
total_items = 54
box_sizes = [10, 5, 3]
box_size_allocations = {
10: 4,
5: 1,
3: 3,
}
assert allocate_items_to_boxes(total_items, box_sizes) == box_size_allocations
Originally I created a rather crude approach that simply loops through the box sizes, largest to smallest, and allocates as many unallocated items as possible to each box. Something like this:
def allocate_items_to_boxes(total_items: int, box_sizes: list):
unallocated = total_items
box_sizes = sorted(box_sizes, reverse=True)
allocations = {}
for box_size in box_sizes:
allocations[box_size] = number_of_boxes = floor(unallocated / box_size)
unallocated -= box_size * number_of_boxes
if unallocated:
raise Exception(f"{unallocated} items were not allocated to boxes!")
return allocations
This handles problem #1
and #2
but fails on problem #3
.
One idea I have, is to first find all the different permutations of possible box allocations where no item remains unallocated and then pick the cheapest one. Is that a good idea? Is there a better approach? How can I do it?
Bonus: Does the problem I described have a name?