I am creating a class that takes an array of items and splits them up into an arbitrary number of buckets in a way such that the sum of the items' sizes in each bucket is balanced.
I am using a naïve approach, where I simply put the next item in the bucket with the lowest total size until I run out of items. This works great for most scenarios, but sometimes fails, due to the lack of "planning". I've illustrated this with an example below.
Code:
SplitCollection class:
# split_collection.rb
class SplitCollection
attr_reader :buckets
def initialize(collection, buckets = 2)
@collection = collection
@buckets = Array.new(buckets) { Bucket.new }
split
end
private
def split
@collection.each do |item|
@buckets.min { |x, y| x.total <=> y.total } << item
end
end
end
Bucket class:
class Bucket
def initialize
@items = []
end
def total
@items.reduce(0) { |sum, item| sum + item.size }
end
def <<(item)
@items << item
end
end
Item class:
class Item
attr_reader :size
def initialize(size)
@size = size
end
end
Here is some code that demonstrates a scenario where the approach fails:
sizes = [2, 4, 2, 2, 4, 2]
#=> [2, 4, 2, 2, 4, 2]
collection = sizes.map { |size| Item.new(size) }
#=> [#<Item:0x007fcdc301fe80 @size=2>, #<Item:0x007fcdc301fe58 @size=4>, #<Item:0x007fcdc301fe30 @size=2>, #<Item:0x007fcdc301fe08 @size=2>, #<Item:0x007fcdc301fde0 @size=4>, #<Item:0x007fcdc301fdb8 @size=2>]
SplitCollection.new(collection).buckets.map(&:total)
#=> [8, 8] (Works!)
SplitCollection.new(collection, 3).buckets.map(&:total)
#=> [6, 4, 6] (Works!)
SplitCollection.new(collection, 4).buckets.map(&:total)
#=> [6, 4, 4, 2] (Doesn't work! Should be [4, 4, 4, 4].)
I am looking for either:
Unfortunately, there is no known efficient way to solve this problem optimally.
This is a variant of the Bin Packing Problem, which is Strongly NP Complete. This means there is no known efficient (polynomial or pseudo polynomial) solution to the problem.
If you really do need optimal solution, you are going to need some exponential algorithm.
Luckily, this is a well studied problem, so there is plenty of literature available about different cases of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With