I have a conceptual problem where I have several packages, each package contains a number of elements inside. Elements are of type A
or type B
. I want to distribute the packages in a finite number of bins in such a way that the distribution between A
and B
does not differ wildly among bins.
The problem is quite complex, hence I will try to explain it with hard constraints and a conceptual example.
Constraints
A package can only be used once
A package must be used entirely
The bins should have relatively equal distributions between `A` and `B` (max 5% deviation from the original ratio)
A package can be spread across all the bins in the given batch
I want to end up with as little as batches (size <= 3 bins) as possible
Example (Conceptual)
Plate 1: 92 * `A`
Plate 2: 92 * `A`
Plate 3: 64 * `A`
Plate 4: 42 * `A`, 50 * `B`
Plate 5: 12 * `A`, 49 * `B`
Plate 6: 92 * `B`
Total distribution as such is 302 * A
and 191 * B
yielding 493 samples in total, the resulting ratios then are 61.25% of A
and 38.75% of B
Desired result:
A minimized set of batches, where each batch contains at most 3 bins (length <= 92) with let's say between 52 and 60 of type A
and between 32 and 40 of type B
(the combined total not above 92) per bin.
Question
What algorithm or method would one suggest to tackle this problem, a simple suggested scheme will do (considering that what I have been trying so far (see below) does not get very far)
The idea behind my attempts thus far
data = ... # This is a list containg all values in a tuple format of `(unit, [(type, location)])` format
while len(data) > 0:
batch = []
counter1 = 0
counter2 = 0
for i in data:
for j in i[1]:
if j[0] == 'A':
counter1 += 1
else:
counter2 += 1
ratio1 = counter1/(counter1+counter2)
ratio2 = counter2/(counter1+counter2)
# Now we know the maximum number of A and B per batch
counter3 = 0 # This keeps track of howmany type `A` we have in current batch
counter4 = 0 # This keeps track of howmany type `B` we have in current batch
while counter3 < ratio1:
for i in data:
for j in i[1]:
if Counter(elem[0] for elem in j)['A'] < (ratio1 - counter3) and Counter(elem[0] for elem in j)['B'] < (ratio2 - counter4):
# Add this unit (from data) to the batch
batch.append(i)
counter3 += Counter(elem[0] for elem in j)['A']
counter4 += Counter(elem[0] for elem in j)['B']
# Remove the used unit from data
This is also where I am stuck, this currently does not attempt to minimize the number of bins, nor does it check the ratios. Additionally, I have the nagging idea that the way that I am trying to do this is no where near the smart way of solving such a problem.
#quick generator to rotate bin numbers
def getBin(maxBin):
number = -1
while True:
number +=1
if number >= maxBin:
number = 0
yield number
batches = []
data = ....
#calculate the minimum number of bins we need
numberOfBins = (len(data))/ 92 + 1
aBinPlacement = getBin(numberOfBins)
bBinPlacement = getBin(numberOfBins)
bins = numberOfBins * [[]]
#the ratio will be maintained because we rotate bins by type
for datum in data:
if datum[0] == 'A':
bins[aBinPlacement.next()].append(datum)
else:
bins[bBinPlacement.next()].append(datum)
batches.extend(bins)
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