Ruby: Verwenden von rand () im Code, aber Schreiben von Tests zur Überprüfung der Wahrscheinlichkeiten

7

Ich habe einen Code, der Dinge basierend auf gewichteten Zufall liefert. Dinge mit mehr Gewicht werden eher zufällig ausgewählt. Da ich jetzt ein guter Rubinist bin, möchte ich diesen ganzen Code natürlich mit Tests abdecken. Und ich möchte testen, dass die Dinge nach den richtigen Wahrscheinlichkeiten abgerufen werden.

Wie teste ich das? Das Erstellen von Tests für etwas, das zufällig sein sollte, macht es sehr schwierig, tatsächliche und erwartete Ergebnisse zu vergleichen. Ein paar Ideen, die ich habe, und warum sie nicht gut funktionieren:

  • Stub Kernel.rand in meinen Tests, um feste Werte zurückzugeben. Das ist cool, aber rand () wird mehrmals aufgerufen und ich bin mir nicht sicher, ob ich das mit genügend Kontrolle ausrüsten kann, um zu testen, was ich brauche.

  • Holen Sie ein zufälliges Objekt eine HÄUFIGE Anzahl von Malen und vergleichen Sie das tatsächliche Verhältnis mit dem erwarteten Verhältnis. Aber es sei denn, ich kann es unendlich oft laufen, das wird niemals perfekt sein und könnte zeitweilig scheitern, wenn ich im RNG etwas Pech habe.

  • Verwenden Sie einen konsistenten Zufallssatz. Das macht den Zufallszahlengenerator wiederholbar, aber es gibt mir immer noch keine Bestätigung, dass Punkt A in 80% der Fälle passieren wird (zum Beispiel).

Also, welche Art von Ansatz kann ich verwenden, um Testabdeckung für zufällige Wahrscheinlichkeiten zu schreiben?

    
Alex Wayne 20.05.2011, 21:21
quelle

4 Antworten

9

Ich denke, du solltest deine Ziele trennen. Eine besteht darin, Kernel.rand zu stubben, wie du es erwähnst. Mit rspec können Sie beispielsweise Folgendes tun:

%Vor%

Beachten Sie, dass dieser Stub nur funktioniert, wenn Sie rand mit Kernel als Empfänger aufrufen. Wenn Sie einfach "rand" aufrufen, erhält das aktuelle "self" die Nachricht und Sie erhalten tatsächlich eine Zufallszahl anstelle der test_values.

Das zweite Ziel besteht darin, einen Feldtest durchzuführen, bei dem Sie tatsächlich Zufallszahlen generieren. Sie würden dann eine gewisse Toleranz verwenden, um sicherzustellen, dass Sie dem gewünschten Prozentsatz nahe kommen. Dies wird jedoch nie perfekt sein und wird wahrscheinlich einen Menschen brauchen, um die Ergebnisse zu bewerten. Aber es ist immer noch nützlich, weil Sie vielleicht erkennen, dass ein anderer Zufallszahlengenerator besser sein könnte, wie das Lesen von / dev / random. Außerdem ist es gut, diese Art von Test zu haben, da Sie sich für eine Migration auf eine neue Art von Plattform entscheiden, deren Systembibliotheken nicht so gut dazu geeignet sind, Zufälligkeiten zu erzeugen, oder in einer bestimmten Version ein Bug vorliegt. Der Test könnte ein Warnsignal sein.

Es hängt wirklich von Ihren Zielen ab. Möchten Sie nur Ihren Gewichtungsalgorithmus oder auch die Zufälligkeit testen?

    
Kelvin 20.05.2011, 22:00
quelle
8

Am besten stürzt man Kernel.rand ab, um feste Werte zurückzugeben.

Kernel.rand ist nicht dein Code. Sie sollten davon ausgehen, dass es funktioniert, anstatt zu versuchen, Tests zu schreiben, die es anstelle Ihres Codes testen. Die Verwendung einer festen Menge von Werten, die Sie ausgewählt und explizit codiert haben, ist besser als das Hinzufügen einer Abhängigkeit davon, was rand für einen bestimmten Seed erzeugt.

    
Don Roby 20.05.2011 21:34
quelle
3

Wenn Sie die konsistente Seedroute durchgehen möchten, sehen Sie sich Kernel#srand :

an

Ссылка

Um die Dokumente zu zitieren (Hervorhebung hinzugefügt):

  

Setzt die Pseudozufallszahl   Generator auf den Wert der Zahl. Ob   Nummer ist weggelassen oder Null, sät die   Generator unter Verwendung einer Kombination der   Zeit, die Prozess-ID und eine Sequenz   Nummer. (Dies ist auch das Verhalten wenn   Kernel :: rand wird ohne aufgerufen   vorher srand anrufend, aber ohne   die Reihenfolge.) Durch Setzen des Samens   Zu einem bekannten Wert können Skripte erstellt werden   deterministisch während des Tests.   Der vorherige Startwert wird zurückgegeben. Ebenfalls   siehe Kernel :: rand.

    
Michael Kohl 20.05.2011 22:07
quelle
0

Zum Testen stub Kernel.rand mit dem folgenden einfachen aber vollkommen vernünftigen LCPRNG:

%Vor%

Sie können die Division überspringen und das Integer-Ergebnis direkt verwenden, wenn Ihr Code kompatibel ist, da dann alle Bits des Ergebnisses wiederholbar sind und nicht nur "die meisten". Dies isoliert Ihren Test von "Verbesserungen" an Kernel.rand und sollte es Ihnen ermöglichen, Ihre Verteilungskurve zu testen.

    
DigitalRoss 20.05.2011 22:06
quelle