Die Community zu .NET und Classic VB.
Menü

FAQ 0047: Wie kann ich Zufallszahlen erzeugen?

 von 

Frage 

Wie kann ich Zufallszahlen erzeugen?

Antwort  

Zufallszahlen werden mit der Funktion Rnd() erzeugt:

Wert = Int((Obergrenze - Untergrenze + 1) * Rnd) + Untergrenze

Listing 1: Zufallszahlen erzeugen

Dem geübten Beobachter fällt aber schnell auf, daß die so erzeugten Zufallszahlen nicht zufällig sind: Sie wiederholen sich. Das kann man nicht verhindern, sondern nur die Periode ausdehnen.
Dazu schreibt man den Befehl Randomize in die Methode, welches als erstes aufgerufen wird (also Sub Main oder Sub Form_Load). Durch diesen Befehl wird der Zufallsgenerator auf einen Wert gesetzt, der sich aus der Kombination der aktuellen Uhrzeit und einiger anderer Werte errechnet.
Die Periode des Zufallszahlengenerators beträgt dann je nach System 2^31 oder 2^63, d.h erst nach mindestens 2 Milliarden Zufallszahlen wiederholen sich die Werte.

Für einige Anwendungsfälle reicht dieses System aber nicht. Bei Lottozahlen z.B. sollen sich die Werte nach Möglichkeit nicht wiederholen. Zu diesem Problem bietet VB keine eigene Lösung, hier muß man selbst kreativ werden:

Sub Lotto(ByRef Result() As Long, _
          ByVal nMax As Long, _
          ByVal nBalls As Long)

' Lottozahlen ermitteln
' Autor/Copyright: K. Langbein/H. Rex, ActiveVB.de
'
' Bei der Ermittlung mehrerer Zufallszahlen aus eine gegebenen Anzahl 
' von Zahlen (6 aus 49) , verändert sich nach jeder Ziehung die 
' Wahrscheinlichkeit. D.h. wenn eine Lottozahl gezogen wurde, kann sie 
' kein zweites mal gezogen werden. 
' Die übliche (etwas dümmliche Möglichkeit) mehrere verschiedene Zahlen 
' zu ermitteln, ist, eine bereits gezogene zu markieren und solange 
' weiter zu probieren, bis eine nichtmarkierte Zahl getroffen wird. 
' Diese Methode entspricht jedoch nicht den mathematischen 
' Gegebenheiten bei einer Lottoziehung. Man muß nach jeder Ziehung die 
' Wahrscheinlichkeit verändern, so wie es in der Praxis auch geschieht: 
' D.h. man fängt mit 1 aus 49 an, dann sind es 1 aus 48, dann 1 aus 47 
' usw.. Dem wird hier Rechnung getragen, indem die gezogene Zahl nach
' jeder Ziehung aus der Liste der Möglichkeiten entfernt wird.

' Diese Methode ist auch geeignet um eine Ziffernfolge (z.B. zum Zweck 
' der Verschlüsselung) zufällig zu verändern. Hierzu verwendet man in 
' der vorliegenden Sub nMax = nBalls
'
    
    Dim List() As Long
    Dim i As Long
    Dim j As Long
    Dim maxi As Long
    Dim nBytes As Long
    
    ReDim List(1 To nMax)
    ReDim Result(1 To nBalls)
    
    ' Füllen der Liste der erlaubten Indizes'
    ' Zunächst sind alle erlaubt.
    For i = LBound(List) To UBound(List)
        List(i) = i
    Next i
    maxi = UBound(List)
    Randomize Timer
    
    'nBalls ist die Zahl der Kugeln die gezogen werden sollen
    For i = 1 To nBalls
        
        'Randomize ' Hier kann man, wenn man will ein weiteres 
        ' Randomize einfügen
        j = 1 + Int(Rnd * maxi)
        Result(i) = List(j)
        List(j) = List(maxi)  ' Umspeichern: Der gezogene Wert wird mit
                              ' dem Wert bei List(maxi) ersetzt.
        maxi = maxi - 1       ' Maximalwert erniedrigen
        
    Next i
   
End Sub

Listing 2: Lottozahlen erzeugen

Ein Beispiel zum Aufruf der Methode:

Dim Result() As Long
Dim i As Long
    
'   Sechs Lottozahlen holen
Call Lotto(Result(), 49, 6)

'   Alle Lottozahlen ausgeben    
For i = LBound(Result) To UBound(Result)
    Debug.Print Format$(Result(i), "@@@")
Next i

Listing 3: Beispiel-Aufruf

Hier stehen sich mehrere Verfahren gegenüber:
1. gezogene Zahl als ungültig erklären
2. gezogene Zahl mit der letzten zur Verfügung stehenden Zahl tauschen und das Ende dekrementieren
3. Zahlenblock nach der gezogenen Zahl um eine Position nach vorne schieben und das Ende dekrementieren

Rein mathematisch gesehen ist das dritte Verfahren das korrekte. Die Reihenfolge der zur Verfügung stehenden Zahlen wird nicht geändert, in der Zahlenfolge ist jedoch eine Lücke. Aus Gründen der Performance wird dieses Verfahren aber kaum als Algorithmus umgesetzt.

Am einfachsten scheint zunächst Verfahren 1 zu sein. Dieses Verfahren hat jedoch einen gravierenden Nachteil: Der Zufallsgenerator kann eine bereits gezogene Zahl erneut ziehen. Da diese Zahl als ungültig markiert ist, müßte man entweder die Position inkrementieren (oder, wenn man bereits am Ende der Zahlenfolge ist, dekrementieren), bis eine gültige Zahl erreicht wird. Dies ist aber (bei großen Zahlenfolgen und vielen Zufallszahlen) nicht sehr performant. Als Alternative bietet sich dabei an, erneut eine Zufallszahl zu ziehen. Dies ändert aber die Wahrscheinlichkeiten für die nachfolgenden Zufallszahlen.

Das Verfahren 2 ist eine Mischung aus den beiden anderen Verfahren. Es hat zwar den Nachteil, daß die Reihenfolge durcheinandergerät, gleicht diesen Nachteil jedoch durch seine Performance und unveränderten Wahrscheinlichkeiten aus.

Bevorzugte Zufallszahlen  

Um einen bestimmten Bereich der Zufallszahlen zu bevorzugen, kann man folgenden Code verwenden:

Function RndEx(Optional ByVal Kruemmung As Double = 1#) As Double
    '   Zufallszahl ist eine mit Rnd() bestimmte Zufallszahl
    RndEx = Zufallszahl  ^ Kruemmung
End Function

Listing 4: Bevorzugte Zufallszahlen

  • Abs(Zufallszahl) >= 1
    • Für Kruemmung < 1 sind kleine Zufallszahlen wahrscheinlicher.
    • Für Kruemmung = 1 sind die Zufallszahlen gleichmäßig über den Wertebereich verteilt.
    • Für Kruemmung > 1 sind große Zufallszahlen wahrscheinlicher.
  • -1 < Zufallszahl < 1
    • Für Kruemmung < 1 sind große Zufallszahlen wahrscheinlicher.
    • Für Kruemmung = 1 sind die Zufallszahlen gleichmäßig über den Wertebereich verteilt.
    • Für Kruemmung > 1 sind kleine Zufallszahlen wahrscheinlicher.

Ihre Meinung  

Falls Sie Fragen zu dieser FAQ haben, Ihre Erfahrung mit anderen Nutzern austauschen möchten oder auf eine Ergänzung hinweisen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.