Zufallszahlen generieren und verwenden

Ein Würfelspiel in Access realisiert Oder ein Roulette-Tisch in einem Formular Das wären denkbare Einsatzgebiete für generierte Zufallszahlen. Aber gibt es über solche Späße hinaus sinnvolle Verwendung für Zufälle in Datenbanken Beleuchten wir in diesem Beitrag einmal, wie Zufallszahlen erzeugt und wofür sie zum Einsatz kommen können.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1410_Zufallszahlen.mdb.

Zufallszahlen erzeugen

Rein mathematisch gesehen ist das Berechnen von Zufallszahlen eine Wissenschaft für sich, und selbst die Definition des Ausdrucks Zufall ist Thema philosophischer Abhandlungen. Ein Prozessor kennt im Prinzip nun mal nur exakte Berechnungen und kann sich nicht einfach etwas ausdenken.

Tatsächlich finden Sie nicht einmal im Windows-API eine Funktion für Zufallszahlen. High-Level-Sprachen, wie VBA, müssen sich daher etwas einfallen lassen. Gerne werden aktuelles Datum und Uhrzeit als zufällige Werte angenommen und davon Zahlen abgeleitet.

Der Systemzeitgeber spielt also häufig eine Rolle für zufällige Werte. Lange Rede, kurzer Sinn: Access oder die Datenbank-Engine selbst enthalten keine Zufallszahlengeneratoren, wohl aber VBA mit der einzigen Methode Rnd().

Die VBA-Funktion Rnd()

Wollten Sie in der Variablen x einen Zufallswert haben, so geschähe das in der kürzesten Form mit dieser Zeile:

Dim x As Double
x = Rnd()

Rnd erzeugt einen zufälligen Double-Wert im Bereich von 0 bis 1. Die Funktion basiert, wie schon angedeutet, auf dem Systemzeitgeber von Windows. Wenn Sie es schaffen, auf zwei Rechnern zur gleichen Zeit diese Funktion aufzurufen, dann wird das Ergebnis der „Zufallszahl“ dasselbe sein. Versuchen Sie es aber lieber erst gar nicht, denn hier werden sogar Nanosekunden ausgewertet.

Für ein Würfelspiel reicht dieser Wertebereich nicht aus. Um aus ihm den Bereich 1 bis 6 zu erzeugen, reicht eine simple Multiplikation aus:

Dim x As Integer
x = 1 + 5 * Rnd()

Der Mindestwert von 1 muss addiert werden, sonst kämen auch Würfel ohne Punkt heraus. Aber auch die angegebene Zeile führt zu gezinkten Würfeln, denn die 6 käme seltener zum Vorschein, als ihr gebührt. Dasselbe gilt für die 1.

Der Knackpunkt nämlich ist die Zuweisung eines Double-Werts zu einer Integer-Variablen. VBA muss hier runden: Double-Werte größer als 0.5 führen zu einer 1, die anderen zu 0. Folglich erscheinen mit der Code-Zeile die Werte 1 und 6 genau halb so oft, wie die Werte 2, 3, 4, 5. Wegen dieses Rundungsverhaltens wäre die Zeile dergestalt zu korrigieren:

Dim x As Integer
x = 0.5 + 6 * Rnd()

Die 1 erscheint nun bei Zufallswerten von etwa 0.51 bis 1.5, die 6 bei Werten von 5.51 bis 6.5.

Diesen Umstand sollten Sie immer im Auge behalten, wenn Sie die Rückgabe von Rnd in Integer-Werte konvertieren.

Eigentlich wäre nun bereits alles gesagt, würde Rnd nicht noch mit einem Parameter aufwarten, der die Steuerung der Rückgabewerte ermöglicht.

Startwerte von Rnd()

Der optionale Parameter von Rnd kann unterschiedliche Bedeutungen einnehmen, je nachdem, wie groß er ist. Ein möglicher Wert ist die Null:

Dim x As Double
x = Rnd(0)

Wenn Sie diese Zeile ausführen, erhalten Sie als Ergebnis jedes Mal den gleichen Wert. Um das zu erläutern, muss etwas weiter ausgeholt werden, wie Rnd arbeitet. Beim allerersten Aufruf der Funktion nämlich initialisiert VBA den Zufallsgenerator mit einem Startwert, der sich vom Zeitgeber ableitet. Bei allen weiteren Aufrufen aber spielt dieser keine Rolle mehr. Er wird nicht mehr abgefragt, sondern die weiteren Zufallszahlen einer Serie ausgegeben, die VBA intern anlegt beziehungsweise berechnet.

Sie können sich das wie eine Tabelle vorstellen, in der zufällige Zahlen gespeichert sind. Beim ersten Aufruf wird der Datensatzzeiger auf einen zufälligen Datensatz gesetzt und anschließend alle weiteren Datensätze ab diesem ausgegeben. Rnd(0) entspricht nun genau dem Datensatz mit dem ersten Zeiger. Ein weiterer Aufruf von Rnd ohne Parameter ändert diesen Zeiger aber wieder auf Basis des Systemzeitgebers. Rnd(0) gibt also immer den zuletzt generierten Wert zurück.

Ein anderer Parameterwert wäre eine Zahl (Single) größer als 0:

Dim x As Double
x = Rnd(0.3)
x = Rnd(99.5)

Obwohl Sie hier beliebige Single-Zahlen angeben können, spielt ihr Wert für das Ergebnis keine wesentliche Rolle. Die Zahl setzt den Zeiger auf die Zufallsserie lediglich um mehr, als eine Position, nach vorn. Eine Zufallszahl kann aber nicht zufälliger sein, als eine andere! Die Verwendung dieses Parameters kommt damit dem Aufruf der Funktion ohne Parameter gleich. Sie können Parameterwerte größer, als 0, demnach ignorieren.

Anders sieht es aus, wenn ein Parameterwert kleiner 0 gesetzt wird:

Dim x As Double
x = Rnd(-14)

x wird damit immer mit derselben Zufallszahl gefüllt. Setzen Sie statt der -14 eine -13 ein, so ändert sich die zurückgelieferte Zahl.

Der Parameter kommt damit der absoluten Positionierung des Zeigers auf die Zufallsserie gleich, während die positiven Parameterwerte relative Positionierung bedeuteten.

Sie können Zufallszahlen so immer wiederholen. Wofür ist das gut Tja, außer einem manipulierten Casinospiel fällt uns dazu auch nicht viel ein. Sie werden dieses Verhalten sehr selten benötigen.

Randomize

Eng verbündet mit der Rnd-Funktion ist die VBA-Anweisung Randomize. VBA erzeugt ja, ausgehend von einem auf dem Zeitgeber basierenden Ausgangspunkt, die Zufallsserie immer nach dem gleichen Schema, einem Algorithmus, der keinen wirklichen Zufall mehr enthält. Auf Rechner A sähe die Serie exakt gleich aus, wie auf Rechner B, so der erste Aufruf von Rnd zum selben Zeitpunkt erfolgte. Sie können die Zufälligkeit gewissermaßen erhöhen, indem Sie Randomize aufrufen:

Dim x As Double
x = Rnd()
Randomize
x = Rnd()

Randomize zwingt den Zufallsgenerator erneut den Systemzeitgeber abzufragen, einen neuen Startwert zu setzen, und damit die Serie neu zu belegen. Auch hier stellt sich wieder die philosophische Frage nach der Zufälligkeit eines Zufalls. Streuen Sie einfach Randomize gelegentlich ein, wenn sie lange Serien von Zufallszahlen erzeugen, um Ihr Gewissen zu beruhigen.

Verteilung des Zufalls

Machen wir uns an die statistische Auswertung der von Rnd() generierten Zufallszahlen. Man könnte erwarten, dass bei einer großen Menge von Zufallszahlen alle genau gleich häufig auftreten. Um das zu überprüfen, rufen Sie die Prozedur FillTableRandom in der Beispieldatenbank, Modul mdlZufall, auf. Sie füllt die Tabelle tblZufall mit 20.000 zufälligen Werten im Bereich von 0 bis 100 (siehe Listing 1).

Sub FillTableRandom()
     Dim rs As DAO.Recordset
     Dim i As Long
     Dim n As Long
     CurrentDb.Execute "DELETE FROM tblZufall"
     Set rs = CurrentDb.OpenRecordset("tblZufall", dbOpenDynaset)
     n = 20000
     For i = 0 To n
         If (i Mod 200) = 0 Then Randomize
         rs.AddNew
'        rs!Zahl.Value = 100 * Rnd    'Falsch!
         rs!Zahl.Value = 101 * Rnd - 0.5
         rs.Update
     Next i
     rs.Close
End Sub

Listing 1: Code zum Befüllen der Tabelle tblZufall mit 20000 Zufallszahlen

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins Access [basics]:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 400 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar