Tilfældige tal og lodtrækning med Excel VBA
En læser spurgte, om jeg ville lave et eksempel på, hvordan man genererer tilfældige tal med Excel VBA, og det vil jeg da, for opgaven er sjov.
Tilfældige tal kan fx bruges til stikprøvekontrol, kryptering eller lodtrækning, og det er en matematisk videnskab for sig.
Med regnearksfunktionen "= SLUMP()" kan man få et tilfældigt tal i en celle, og i VBA bruger man funktionen "Rnd()".
For at vende tilbage til læserens ønske, skulle der fra en tabel udtrækkes syv tilfældige og forskellige "vindertal". Det indebærer, at hvis det samme tal optræder flere gange i tabellen, ja så vil det have større sandsynlighed for at blive udtrukket - altså en form for snyd.
Herunder bringer jeg et eksempel på, hvordan det kan gøres (proceduren Lottosnyd). Bagefter viser jeg, hvordan VBA kan lave en tabel med unikke tilfældige tal (altså uden dubletter), så man kan få en fair lodtrækning.
Eksemplet viser også, hvordan man kan definere et interval for det tilfældige tal (fx 1 - 1000).
Om pseudotilfældige tal
Før jeg går videre, må jeg dog skynde mig at sige, at Excel ikke laver ægte tilfældige tal som fx kast med en terning, men såkaldt psudotilfældige tal.
VBA (og andre programmer) bruger en algoritme, som tager udgangspunkt i en "seed value". Ofte bruges computerens ur til at generere denne startværdi.
Kender man værdien, kan man regne de "tilfældige" tal ud (hvis man er skrap!), og det vil jo være ret uheldigt, hvis de fx bruges til kryptering eller lotto!
Kommandoen "Randomize" bruges i VBA til at resette talgeneratoren, så den får en ny seed value.
Det er altsammen lidt langhåret, og jeg henviser til fx Wikipedia, hvor du kan læse mere om pseudotilfældige tal.
Lodtrækning
Herunder følger et eksempel på lodtrækning fra en tabel. Eksemplet forudsætter, at der er en tabel startende i celle A1 på det første faneblad. Du kan kopiere koden ved at markere den med musen og kopiere med CTRL+C og så sætte den ind med CTRL+V i et VBA-modul i Excel.
Sub Lottosnyd()
Dim lVal As Long
Dim rInput As Range
Dim rCell As Range
Dim colTjek As Collection
Dim colVindere As Collection
On Error GoTo ErrorHandle
Set rInput = Worksheets(1).Range("A1").CurrentRegion
If rInput.Count < 8 Then
MsgBox "Der er for få celler i tabellen.", vbCritical
GoTo BeforeExit
End If
For Each rCell In rInput
If IsNumeric(rCell.Value) = False Or Len(rCell.Value) = 0 Then
MsgBox "Cellerne skal indeholde tal.", vbCritical
GoTo BeforeExit
End If
Next
On Error Resume Next
Set colTjek = New Collection
For Each rCell In rInput
With rCell
colTjek.Add Int(.Value), Str$(Int(.Value))
End With
If colTjek.Count = 8 Then Exit For
Next
If colTjek.Count < 8 Then
MsgBox "Der skal være mindst 8 forskellige tal i tabellen."
GoTo BeforeExit
End If
Set colVindere = New Collection
Randomize
For Each rCell In rInput
With rInput
lVal = Int(.Count * Rnd() + 1)
colVindere.Add Int(.Item(lVal).Value), _
Str$(Int(.Item(lVal).Value))
If colVindere.Count = 7 Then Exit For
End With
Next
On Error GoTo ErrorHandle
Set rCell = Worksheets(2).Range("A1")
rCell.Value = "Udtrukne vindertal:"
With colVindere
For lVal = 1 To .Count
rCell.Offset(lVal, 0).Value = .Item(lVal)
Next
End With
Worksheets(2).Activate
BeforeExit:
Set rCell = Nothing
Set rInput = Nothing
Set colTjek = Nothing
Set colVindere = Nothing
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Fejl i procedure Lottosnyd."
Resume BeforeExit
End Sub
Tabel med unikke tilfældige tal
Nedenstående eksempel viser, hvordan man kan lave en tabel med unikke tilfældige tal - altså uden dubletter.
Hvis man bruger denne procedure, kan man lave en fair lodtrækning med proceduren ovenover (og så bør man nok ikke kalde den "Lottosnyd"!).
Igen: Du kan markere koden med musen, kopiere med CTRL+C og indsætte den i et VBA-modul med CTRL+V.
Sub LavUnikTabel()
Dim rTabel As Range
Dim rCell As Range
Dim lMin As Long
Dim lMax As Long
Dim lVal As Long
Dim colValues As Collection
On Error GoTo ErrorHandle
Set rTabel = Worksheets(1).Range("A1", "J30")
lMin = 1
lMax = 1000
If lMax - lMin < rTabel.Count Then
MsgBox "Intervallet er for lille.", vbCritical
GoTo BeforeExit
End If
Randomize
Set colValues = New Collection
On Error Resume Next
Do Until colValues.Count = rTabel.Count
lVal = Int((lMax - lMin + 1) * Rnd() + lMin)
colValues.Add lVal, Str$(lVal)
Loop
On Error GoTo ErrorHandle
With colValues
For lVal = 1 To .Count
rTabel.Item(lVal).Value = .Item(lVal)
Next
End With
BeforeExit:
Set rCell = Nothing
Set rTabel = Nothing
Set colValues = Nothing
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Fejl i proceduren LavUnikTabel."
Resume BeforeExit
End Sub
Afslutningsvis vil jeg gøre opmærksom på, at funktionen Rnd() gerne returnerer tal med decimaler, og at jeg i mine eksempler har konverteret tallene til heltal med VBA-funktionen Int().
Relateret:
|