Hi.
I've commented the code a little more to help you understanding it.
So, as you can see the code works on 3 separate phases.
%let EACH=%eval(&TOTAL/&SPLIT); * calculate the amount of hours per worker;
data _SCHEDULE;
length HOUR 8
array _TOTAL[&SPLIT]; * this is used to track the current hours allocated to each worker;
array SCHED[&SPLIT]; * this is the schedule for each worker, SCHED1 ... SCHEDn;
drop _:; * auxiliary vars prefixed with _ so than can be easily dropped;
_I_=0;
do HOUR=1 to &TOTAL; * cycle for each hour;
* 1. find one worker with available time and that is not working in the previous hour;
do until (_TOTAL[_I] lt &EACH and _I ne _I_);
if HOUR eq 1 then _I=&FIRST; * force first allocation to be worker 1;
else _I=ceil(rand("Uniform")*&SPLIT); * pick one worker randomly;
end;
_I_=_I; * keep track of last allocated worker for next hour;
* 2. greedly try to allocate maximum hours;
if sum(_TOTAL[_I],&MAXUNIT) gt &EACH then _NEXT=sum(&EACH,-_TOTAL[_I]);
else _NEXT=&MAXUNIT; * try max allocation;
* _NEXT holds the allocated hours;
_TOTAL[_I]=sum(_TOTAL[_I],_NEXT); * update total hours allocated for selected worker;
* 3. update the schedule;
do _J=1 to _NEXT; * cycle throught allocated hours;
do _K=1 to &SPLIT;
SCHED[_K]=0; * set 0 for workers not selected;
end;
SCHED[_I]=1; * set 1 for selected worker;
output;
HOUR+1; * adjust next;
end;
HOUR+-1; * adjust back;
end;
run;
%mend rand_schedule;
%rand_schedule(9,3,1,2); * total hours, num. of workers, first allocated, max consecutive hours;
First it tries to find an available worker that has not reached the total amount of hours for each and that is not working in the previous hour.
Then it tries to allocate the maximum consecutive hours, if it is not possible, then it will allocate the difference (maximum - current total).
Finally it will build the schedule by cycling through the number of hours allocated and setting 0 and 1 to the corresponding workers.
Hope it helps.
Daniel Santos @ www.cgd.pt
... View more