I have an optimisation issue. It's about a product that contains 20 parts (the order of producing doesn't matter). I've got 3 similar machine that can produce all 20 parts.
I've got the 20 parts represented in minutes (ie. it takes 3min to produce the first part and 75min to produce the second part, etc)
ItemTime<-c(3,75,55,12,45,55,11,8,21,16,65,28,84,3,58,46,5,84,8,48)
So to produce 1 product it takes 730 min.
sum(ItemTime)
The aim is to minimise the production of one product by allocating the good item to the three machines.
sum(ItemTime/3)
So actually I need to be as close as 243.333 min (730/3)
The amount of possibility is huge 3^20
I guess there are many different optimal solutions. I would like that R give me all of them. I don't need only to know total time that will need machine 1 2 and 3 : I also need to know which items to give to machine 1, to machine 2 and to manchine 3.
Alternatively, if it's too long I would like to choose a sample without repetition that is as reasonable as possible...
Can I solve my issue with R language?
Edit: Obviously this problem is slightly different to the one I remember, because as @han shows, this algorithm is not optimal, merely an approximation (although there is a guarantee that the "makespan" from this algorithm is never more than 4/3 * optimal makespan in general and 11/9 * optimal for 3 machines.).
The link @han provided linked to the multiprocessor scheduling article, which is precisely equivalent to this problem. The article tells us that the problem is actually NP-hard. Which means there is no polynomial time algorithm to compute the optimal answer (much less a
O(n log n)
like we have here).You can just use a greedy algorithm: go through the list and assign the job that takes the longest to the machine that currently has the least work allocated to it.
As an example, consider just having
c(5,3,6,1,2)
as your part manufacturing times. First you sort this into decreasing order:c(6,5,3,2,1)
, and then go through it (in order) assigning tasks.So machine 1 makes the part that takes 6 minutes, machine 2 makes the ones that take 1 and 5 minutes and machine 3 makes the one that takes 3 and 2.
You can see that in step 4, the machine with the shortest total time is machine 3 so that is why we assigned the
2
to it.From memory, this algorithm is actually optimal; although I don't have a link for that claim. I also don't know if you can adapt it to get all possible optimal results.If you define a function that takes one job and a list of the machines with their current jobs, you can use
Reduce
to assign all the jobs. The single job assignment might look something like:(I don't like the
machines[[which.machines]]
line... I'm sure there is a better way to modify a list at a specific index.)And then the assignment could be like:
(I don't like the line that starts
machines <-
: I'm sure there is a neater way of creating a list of lengthn
, but I can't find it.)On your example, this gives:
The final step is working out which job corresponds to which time: this can either be done by working backwards after assigning the times (this can be done in approximately linear time, since there is a relatively simple mapping from times to job and vice versa) or by modifying
allocate
andassign.job
to keep track of the indices of the jobs (if you are going to do this then theorder
function will be more useful thansort
, and using dataframes instead of vectors, to store times in one column and indices in another).(It should be noted that this solution is several times faster than the other, since the other answer is using higher powered algorithms, which are
possiblynot overkill for this problem..."possibly" because I'm still not 100% sure this algorithm is optimal.)I believe your problem is a close variant of the Multiple Knapsack Problem (MKP) which is, a priori, not a piece of cake...
However, your dimension is small enough that the problem can be solved as a Mixed-Integer Programming (MIP). I solved it below using
Rglpk
; it took the solver about four minutes. If you are lucky enough to have access to CPLEX, I would highly recommend you useRcplex
instead, it will smoke it.Problem formulation:
Now let's solve it:
As noted in other answers this is related to the bin packing problem. A simple bin packing algorithm is already implemented in the BBmisc package; we can apply this existing function here for a simple and fast solution:
In this case, it instantly produces an optimal solution using 3 bins. We can confirm the solution's bin sizes:
If
binPack
produces an initial solution using too many bins, it can be placed in a for-loop that increments the binsize and only returns a solution when there are no more than 3 bins in the output ofbinPack
.Interestingly, binPack returns a solution with the same bin sizes as flodel's answer above, but with different assignments in bins 2 and 3:
While
binPack
provides a fast and simple way to solve this problem, its description notes that this algorithm is simple and may not return the optimal solution: