Function RandLatin(Optional bVolatile As Boolean = False) As Long()
' shg 2013
' UDF only
' Requires adRandLong()
' Returns a random Latin square of size n with symbols 1 to n
' (by shuffling the symbols, then the rows, then the columns)
' to the calling range
' e.g., in A1:E5, {=RandLatin()}
' All such squares generated in this fashion are members (I think)
' of the same isotopy class, so it doesn't generate all possibilities.
Dim aiInp() As Long
Dim aiOut() As Long
Dim aiRnd() As Long
Dim n As Long
Dim i As Long
Dim j As Long
If bVolatile Then Application.Volatile
With Application.Caller
n = IIf(.Rows.Count > .Columns.Count, .Rows.Count, .Columns.Count)
End With
ReDim aiInp(1 To n, 1 To n)
ReDim aiOut(1 To n, 1 To n)
' shuffle the symbols
aiRnd = aiRandLong(1, n)
For i = 1 To n
For j = 1 To n
aiInp(i, j) = aiRnd(((i + j - 2) Mod n) + 1)
Next j
Next i
' shuffle the rows
aiRnd = aiRandLong(1, n)
For i = 1 To n
For j = 1 To n
aiOut(i, j) = aiInp(aiRnd(i), j)
Next j
Next i
aiInp = aiOut
' shuffle the columns
aiRnd = aiRandLong(1, n)
For i = 1 To n
For j = 1 To n
aiOut(j, i) = aiInp(j, aiRnd(i))
Next j
Next i
RandLatin = aiOut
End Function
Public Function aiRandLong(iMin As Long, _
iMax As Long, _
Optional ByVal n As Long = -1, _
Optional bVolatile As Boolean = False) As Long()
' shg 2008
' UDF or VBA
' Fisher-Yates shuffle
' Returns a 1-based array of n unique Longs between iMin and iMax inclusive
Dim aiSrc() As Long ' array of numbers iMin to iMax
Dim iSrc As Long ' index to aiSrc
Dim iTop As Long ' decreasing upper bound for next selection
Dim aiOut() As Long ' output array
Dim iOut As Long ' index to aiOut
If bVolatile Then Application.Volatile True
If n < 0 Then n = iMax - iMin + 1
If iMin > iMax Or n > (iMax - iMin + 1) Or n < 1 Then Exit Function
ReDim aiSrc(iMin To iMax)
ReDim aiOut(1 To n)
' init iSrc
For iSrc = iMin To iMax
aiSrc(iSrc) = iSrc
Next iSrc
iTop = iMax
For iOut = 1 To n
' Pick a number in aiSrc between 1 and iTop, copy to output,
' replace with the number at iTop, decrement iTop
iSrc = Int((iTop - iMin + 1) * Rnd) + iMin
aiOut(iOut) = aiSrc(iSrc)
aiSrc(iSrc) = aiSrc(iTop)
iTop = iTop - 1
Next iOut
aiRandLong = aiOut
End Function