Select the range of interest, paste
=RandLatin()-1
in the formula bar, press and hold the Ctrl and Shift keys, then press Enter.
0 |
3 |
2 |
1 |
4 |
A1:E5: {=RandLatin()-1} |
2 |
4 |
1 |
3 |
0 |
|
4 |
1 |
0 |
2 |
3 |
|
3 |
2 |
4 |
0 |
1 |
|
1 |
0 |
3 |
4 |
2 |
|
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
' Returns a 1-based array of n unique Longs between iMin and iMax inclusive
Dim ai() As Long ' array of numbers iMin to iMax
Dim i As Long ' index to ai
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 ai(iMin To iMax)
For i = iMin To iMax
ai(i) = i
Next i
FYShuffle ai
If n > -1 Then ReDim Preserve ai(iMin To iMin + n - 1)
aiRandLong = ai
End Function
Sub FYShuffle(av As Variant)
' In-situ Fisher-Yates shuffle of 1D array av
' VBA only
Dim iLB As Long
Dim iTop As Long
Dim vTmp As Variant
Dim iRnd As Long
iLB = LBound(av)
iTop = UBound(av) - iLB + 1
Do While iTop
iRnd = Int(Rnd * iTop)
iTop = iTop - 1
vTmp = av(iTop + iLB)
av(iTop + iLB) = av(iRnd + iLB)
av(iRnd + iLB) = vTmp
Loop
End Sub
Bookmarks