OK, so let's do it in a simple to follow (not the quickest method, but less than a second is probably anyway acceptable.
Most of the tesks is really done in a worksheet where in columns J ... main procedure works. The key column is M where for a given date from column J a semi-random number is obtained from formula:
Formula:
=IF(COUNTIF($O$1:$O$4,K1)>0,100,0)+L1+RAND()
(stored in code in R1C1 form):
Formula:
=IF(COUNTIF(R1C15:R4C15,RC[-2])>0,100,0)+RC[-1]+RAND()
so it is a random number + counter of persons who are already assigned for that day + 100 if the person has already a date from this week (as noted in O1:O4).
then lowest value is promoted to very top by sorting
HAve a look how it works in the attachment. To observe it better you can comment-out a line with application.screenupdating and set a breakpoint in a
next j instruction (take next person) and run the code with either <F5> or "play" button - see the screanshot
requirements - one of dates for the mionth to be prepared in A1, list of persons in column A (A3 down) , their number of times to be tested in given month in column B.
The whole code:
Sub test()
Dim i As Integer, j As Integer, t As Double, mycalculation As Integer
Dim month_start As Date, days_in_month As Integer, times_a_month As Integer
'setting stage
't = Timer
month_start = Range("A1") - Day(Range("A1")) + 1
days_in_month = DateSerial(Year(month_start), Month(month_start) + 1, 1) - month_start
Columns("D:O").ClearContents
Application.ScreenUpdating = False
mycalculation = Application.Calculation
Application.Calculation = xlCalculationManual
For i = 1 To days_in_month
Cells(i, "J").Value = month_start + i - 1
Cells(i, "K").Value = Format(month_start + i - 1, "ww")
Cells(i, "L").Value = 0
Cells(i, "M").FormulaR1C1 = "=IF(COUNTIF(R1C15:R4C15,RC[-2])>0,100,0)+RC[-1]+RAND()"
Next i
' main loop - for each person
For j = 3 To Cells(Rows.Count, "A").End(xlUp).Row
' empty used for this person weeks list
For i = 1 To 4: Cells(i, "O").Value = "": Next i
' select rarely used date
For i = 1 To Cells(j, "B")
'sort using current values in column M
Range("M1:M" & days_in_month).Calculate
Range("J1:M" & days_in_month).Sort key1:=Range("M1"), order1:=xlAscending, Orientation:=xlSortColumns, Header:=xlNo
' write it to columns D:G, increase date counter, store used week number
Cells(j, i + 3) = Range("J1")
Cells(j, i + 3).NumberFormat = "m/d/yyyy"
Range("L1") = Range("L1") + 1
Cells(i, "O") = Range("K1")
Next i
Cells(j, 4).Resize(1, 4).Sort key1:=Cells(j, 4), order1:=xlAscending, Header:=xlNo, Orientation:=xlSortRows
Next j
Columns("J:O").ClearContents
'MsgBox Timer - t
Application.Calculation = mycalculation
End Sub
Bookmarks