/* Rostering problem in Picat. Assign persons to different tasks. Problem inspired by Jean-Charles Regin "Modeling Problems in Constraint Programming" ( http://www.constraint-programming.com/people/regin/papers/modelincp.pdf ) page 19ff The objective is to minimize the differences of workload points. Here is an (optimal) rostering for 14 days, 5 workers and the workload of [4,3,2,1]. num_days = 14 num_workers = 5 workload = [4,3,2,1] Workload: [4,3,2,1] Day 1: [1,5,4,3] Day 2: [1,2,3,5] Day 3: [3,4,5,2] Day 4: [4,5,1,2] Day 5: [1,2,5,4] Day 6: [1,4,5,2] Day 7: [1,2,5,3] Day 8: [2,3,5,1] Day 9: [5,2,3,1] Day 10: [1,3,5,2] Day 11: [4,3,2,5] Day 12: [4,2,3,5] Day 13: [3,4,2,5] Day 14: [4,3,5,2] num_workdays = [9,13,11,9,14] workload_points = [28,28,28,28,28] z = 0 This program was created by Hakan Kjellerstrand, hakank@gmail.com See also my Picat page: http://www.hakank.org/picat/ */ % import cp. import sat. main => go. go => nolog, NumDays = 14, Workload = [4,3,2,1], NumWorkers = 5, rostering(NumWorkers,Workload,NumDays, OnDuty,NumWorkdays,WorkloadPoints,Z), println(num_days=NumDays), println(num_workers=NumWorkers), println(workload=Workload), printf("Workload: %w\n", Workload), foreach(Day in 1..NumDays) printf("Day %2d: %w\n", Day, OnDuty[Day].to_list) end, println(num_workdays=NumWorkdays), println(workload_points=WorkloadPoints), println(z=Z), nl, % fail, nl. rostering(NumWorkers,Workload,NumDays, OnDuty,NumWorkdays,WorkloadPoints,Z) => NumShifts = Workload.len, OnDuty = new_array(NumDays,NumShifts), OnDuty :: 1..NumWorkers, NumWorkdays = new_list(NumWorkers), NumWorkdays :: 0..NumDays, WorkloadPoints = new_list(NumWorkers), WorkloadPoints :: 0..NumDays*max(Workload), % Difference in workloads. To minimize Z #= sum([abs(WorkloadPoints[W1]-WorkloadPoints[W2]) : W1 in 1..NumWorkers, W2 in W1+1..NumWorkers]), % Make the schedule as fair as possible, i.e. approx. the same % number of days per week. % at most one shift a day foreach(I in 1..NumDays) foreach(J in 1..NumShifts, K in J+1..NumShifts) OnDuty[I,J] #!= OnDuty[I,K] end end, % all must work at least x shift per week foreach(W in 1..NumWorkers) NumWorkdays[W] #= sum([OnDuty[J,K] #= W : J in 1..NumDays, K in 1..NumShifts]), % approx. equal number of days NumWorkdays[W] #>= (NumShifts*7) div NumWorkers, % different shift has different work load WorkloadPoints[W] #= sum([Workload[K]*(OnDuty[J,K] #= W) : J in 1..NumDays, K in 1..NumShifts]) end, Vars = WorkloadPoints ++ OnDuty.vars ++ NumWorkdays, solve($[min(Z),report(printf("Z:%d\n", Z))],Vars).