tinyrand
Leichte RNG -Spezifikation und mehrere ultraschnelle Implementierungen in Rost. tinyrand
ist no_std
und verwendet keinen Heap -Allocator.
tinyrand
?std
, was bedeutet, dass es eingebettet ist-es läuft auf Mikrocontrollern und bloßen Metall (No OS) Umgebungen.Mock
zum Testen von Code, der von Zufallszahlen abhängt. Das heißt, wenn Sie sich für die Codeabdeckung interessieren.Nachfolgend finden Sie einen Vergleich mehrerer bemerkenswerter PRNGs.
Prng | Algorithmus | Bandbreite (GB/s) | |
---|---|---|---|
rand | Chacha12 | 2.4 | |
tinyrand | Splitmix | 6.5 | |
tinyrand | XorShift | 6.7 | |
fastrand | Wyrand | 7.5 | |
tinyrand | Wyrand | 14.6 |
TL; DR: tinyrand
ist 2x schneller als fastrand
und 6x schneller als rand
.
Es ist unmöglich, sicher zu sagen, ob eine bestimmte PRNG gut ist. Die Antwort ist wahrscheinlich. Alle drei Algorithmen stehen gut gegen die Erkrankung von Tests, aber Wyrand und Splitmix sind etwas besser als XorShift. (Getestet auf 30,8 Milliarden Proben.) Dies bedeutet, dass tinyrand
Zahlen erzeugt, die ausreichend zufällig erscheinen und in den meisten Anwendungen wahrscheinlich für die Verwendung geeignet sind.
tinyrand
-Algorithmen sind nicht kryptografisch sicher, was bedeutet, dass es möglich ist, die nächste Zufallszahl durch Beobachten einer Abfolge von Zahlen zu erraten. (Oder die vorhergehenden Zahlen.) Wenn Sie einen robusten CSPRNG benötigen, wird nachdrücklich vorgeschlagen, dass Sie mit rand
gehen. CSPRNGs sind im Allgemeinen viel langsamer und die meisten Leute brauchen keine.
cargo add tinyrand
Eine Rand
-Instanz ist erforderlich, um Zahlen zu generieren. Hier verwenden wir StdRand
, ein Alias für den Standard/empfohlenen RNG. (Derzeit auf Wyrand
eingestellt, kann sich aber in Zukunft ändern.)
use tinyrand :: { Rand , StdRand } ;
let mut rand = StdRand :: default ( ) ;
for _ in 0 .. 10 {
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ;
}
In ähnlicher Weise können wir eine Anzahl anderer Typen erzeugen:
use tinyrand :: { Rand , StdRand } ;
let mut rand = StdRand :: default ( ) ;
let num = rand . next_u128 ( ) ;
println ! ( "generated wider {num}" ) ;
Die next_uXX
-Methoden generieren Zahlen im gesamten nicht signierten Bereich des angegebenen Typs. Oft wollen wir eine Zahl in einem bestimmten Bereich:
use tinyrand :: { Rand , StdRand , RandRange } ;
let mut rand = StdRand :: default ( ) ;
let tasks = vec ! [ "went to market" , "stayed home" , "had roast beef" , "had none" ] ;
let random_index = rand . next_range ( 0 ..tasks . len ( ) ) ;
let random_task = tasks [ random_index ] ;
println ! ( "This little piggy {random_task}" ) ;
Ein weiterer häufiger Anwendungsfall ist die Erzeugung von bool
s. Möglicherweise möchten wir den binären Ergebnissen auch eine Gewichtung zuweisen:
use tinyrand :: { Rand , StdRand , Probability } ;
let mut rand = StdRand :: default ( ) ;
let p = Probability :: new ( 0.55 ) ; // a slightly weighted coin
for _ in 0 .. 10 {
if rand . next_bool ( p ) {
// expect to see more heads in the (sufficiently) long run
println ! ( "heads" ) ;
} else {
println ! ( "tails" ) ;
}
}
Es gibt Zeiten, in denen wir unseren Faden für eine Weile schlafen müssen und auf einen Zustand warten. Wenn viele Themen schlafen, wird im Allgemeinen empfohlen, sich zufällig zurückzuziehen, um einen Ansturm zu vermeiden.
use tinyrand :: { Rand , StdRand , RandRange } ;
use core :: time :: Duration ;
use std :: thread ;
use tinyrand_examples :: SomeSpecialCondition ;
let mut rand = StdRand :: default ( ) ;
let condition = SomeSpecialCondition :: default ( ) ;
let base_sleep_micros = 10 ;
let mut waits = 0 ;
while !condition . has_happened ( ) {
let min_wait = Duration :: ZERO ;
let max_wait = Duration :: from_micros ( base_sleep_micros * 2u64 . pow ( waits ) ) ;
let random_duration = rand . next_range ( min_wait..max_wait ) ;
println ! ( "backing off for {random_duration:?}" ) ;
thread :: sleep ( random_duration ) ;
waits += 1 ;
}
Aufrufen Default::default()
auf einem Rand
initialisiert es mit einem konstanten Samen. Dies ist großartig für die Wiederholbarkeit, führt jedoch zu dem gleichen Lauf von "zufälligen" Zahlen, was nicht das ist, was die meisten Leute brauchen.
tinyrand
ist eine no_std
-Kiste und leider gibt es keine gute, tragbare Möglichkeit, Entropie zu erzeugen, wenn man keine Annahmen über die zugrunde liegende Plattform treffen kann. In den meisten Anwendungen könnte man eine Uhr, aber etwas so trivial wie SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
ist möglicherweise nicht immer verfügbar.
Wenn Sie eine Entropiequelle zur Verfügung haben, können Sie einen Rrnd
als SO säen:
use tinyrand :: { Rand , StdRand , Seeded } ;
let seed = tinyrand_examples :: get_seed_from_somewhere ( ) ; // some source of entropy
let mut rand = StdRand :: seed ( seed ) ;
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ;
Sie können auch in Betracht ziehen, getrandom
zu verwenden, eine plattformübergreifende Methode zum Abrufen von Entropiedaten.
Wenn man sich nicht um no_std
kümmert, sollten sie nicht an seine Grenzen gebunden sein. Um aus der Systemuhr zu säen, können Sie sich für std
entscheiden:
cargo add tinyrand-std
Jetzt haben wir eine ClockSeed
zur Verfügung, die auch das Rand
-Merkmal implementiert. ClockSeed
leitet einen u64
ab, indem Sie die oberen 64 Bit des Nanosekundenzeitstempels (aus SystemTime
) mit den unteren 64 Bits. Es ist nicht für den kryptografischen Gebrauch geeignet, wird jedoch für die meisten allgemeinen Anwendungen ausreichen.
use tinyrand :: { Rand , StdRand , Seeded } ;
use tinyrand_std :: clock_seed :: ClockSeed ;
let seed = ClockSeed :: default ( ) . next_u64 ( ) ;
println ! ( "seeding with {seed}" ) ;
let mut rand = StdRand :: seed ( seed ) ;
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ;
Die tinyrand-std
Kiste enthält auch eine säugige, faden lokale Rand
Implementierung:
use tinyrand :: Rand ;
use tinyrand_std :: thread_rand ;
let mut rand = thread_rand ( ) ;
let num = rand . next_u64 ( ) ;
println ! ( "generated {num}" ) ;
Eine gute Testabdeckung kann manchmal schwer zu erreichen sein. Doppelte, wenn Anwendungen von Zufälligkeit oder anderen Nichtdeterminismus abhängen. tinyrand
verfügt über eine Schein-RNG, die eine feinkörnige Kontrolle über die Ausführung Ihres Codes bietet.
Das Schein verwendet die alloc
-Kiste, da die Haufenszuweisung von Schließungen erforderlich ist. Daher wird das Mock als Opt-In-Paket verteilt:
cargo add tinyrand-alloc
Auf Basisebene ist Mock
mit einer Handvoll Delegierten strukturiert. Ein Delegierter ist ein Verschluss, der vom Mock aufgerufen wird, wenn eine bestimmte Merkmalsmethode vom zu testenden System aufgerufen wird. Das Mock behält auch einen internen Aufrufstatus bei, der den Überblick über die Anzahl der Ausübung eines bestimmten Delegiertens verfolgt. Sie können also nicht nur das Verhalten des Rand
-Merkmals verspotten, sondern auch die Anzahl der Typen überprüfen, die eine bestimmte Gruppe verwandter Merkmalsmethoden aufgerufen wurde.
Die Delegierten werden im Testfall angegeben, während die Mock -Instanz als Rand
-Implementierung an das zu testete System übergeben wird. Derzeit werden drei Delegierten unterstützt:
FnMut(&State) -> u128
-aufgerufen, wenn eine der next_uXX()
methodien auf dem Mock aufgerufen wird. ( uXX
ist einer von u16
, u32
, u64
, u128
oder usize
.) Der Delegierte gibt die nächste "zufällige" Nummer zurück, die bis zu 128 Bits breit ist. Die Breite ist für u128
ausgelegt - den breitesten Typ, der von Rand
unterstützt wird. Wenn einer der schmaleren Typen angefordert wird, gibt das Schein einfach die unteren Teile zurück. (ZB für einen u32
wird der verspottete Wert unter Verwendung as u32
unter der Motorhaube abgeschnitten.)FnMut(Surrogate, Probability) -> bool
-aufgerufen, wenn die nächste Methode der next_bool(Probability)
aufgerufen wird.FnMut(Surrogate, u128) -> u128
-Wenn entweder next_lim
oder next_range
aufgerufen wird. Beginnen Sie mit den absoluten Grundlagen, lassen Sie uns von next_uXX()
eine Konstante zurückgeben. Wir werden dann überprüfen, wie oft unser Mock angerufen wurde.
use tinyrand :: Rand ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_u128 ( |_| 42 ) ;
for _ in 0 .. 10 {
assert_eq ! ( 42 , rand.next_usize ( ) ) ; // always 42
}
assert_eq ! ( 10 , rand.state ( ) .next_u128_invocations ( ) ) ;
Obwohl dieses Szenario peinlich einfach ist, ist es tatsächlich weit verbreitet. Gleiches kann mit der fixed(uXX)
Funktion erreicht werden.
use tinyrand :: Rand ;
use tinyrand_alloc :: { Mock , fixed } ;
let mut rand = Mock :: default ( ) . with_next_u128 ( fixed ( 42 ) ) ;
assert_eq ! ( 42 , rand.next_usize ( ) ) ; // always 42
Da Delegierte regelmäßige Schließungen sind, können wir uns an Variablen im umschließenden Bereich binden. Dies gibt uns eine fast unbegrenzte Kontrolle über das Verhalten unseres Scheins.
use tinyrand :: Rand ;
use tinyrand_alloc :: Mock ;
use core :: cell :: RefCell ;
let val = RefCell :: new ( 3 ) ;
let mut rand = Mock :: default ( ) . with_next_u128 ( |_| * val . borrow ( ) ) ;
assert_eq ! ( 3 , rand.next_usize ( ) ) ;
// ... later ...
* val . borrow_mut ( ) = 17 ;
assert_eq ! ( 17 , rand.next_usize ( ) ) ;
Der Delegierte kann zu jedem Zeitpunkt neu zugewiesen werden, auch nachdem das Schein erstellt und ausgeübt wurde:
use tinyrand :: Rand ;
use tinyrand_alloc :: { Mock , fixed } ;
let mut rand = Mock :: default ( ) . with_next_u128 ( fixed ( 42 ) ) ;
assert_eq ! ( 42 , rand.next_usize ( ) ) ;
rand = rand . with_next_u128 ( fixed ( 88 ) ) ; // the mock's behaviour is now altered
assert_eq ! ( 88 , rand.next_usize ( ) ) ;
Die Signatur des nächsten Delegiertens next_u128
nimmt eine State
Referenz an, die die Häufigkeit des Mocks erfasst. (Die Anzahl wird erst nach Abschluss des Aufrufs erhöht.) Schreiben wir ein Schein, das eine "zufällige" Nummer zurückgibt, die aus dem Aufrufstatus abgeleitet ist.
use tinyrand :: Rand ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_u128 ( |state| {
// return number of completed invocations
state . next_u128_invocations ( ) as u128
} ) ;
assert_eq ! ( 0 , rand.next_usize ( ) ) ;
assert_eq ! ( 1 , rand.next_usize ( ) ) ;
assert_eq ! ( 2 , rand.next_usize ( ) ) ;
Dies ist nützlich, wenn wir erwarten, dass das Schein mehrmals aufgerufen wird und jeder Aufruf ein anderes Ergebnis zurückgeben sollte. Ein ähnliches Ergebnis kann mit der counter(Range)
erzielt werden, die durch einen bestimmten Zahlenbereich rennt und bequem an die Grenze einpackt:
use tinyrand :: Rand ;
use tinyrand_alloc :: { Mock , counter } ;
let mut rand = Mock :: default ( ) . with_next_u128 ( counter ( 5 .. 8 ) ) ;
assert_eq ! ( 5 , rand.next_usize ( ) ) ;
assert_eq ! ( 6 , rand.next_usize ( ) ) ;
assert_eq ! ( 7 , rand.next_usize ( ) ) ;
assert_eq ! ( 5 , rand.next_usize ( ) ) ; // start again
Indem wir nur den nächsten Delegierten next_u128
liefern, können wir das Ergebnis jeder anderen Methode im Rand
-Merkmal beeinflussen, da sie alle aus derselben Quelle der Zufälligkeit herabgehen und schließlich unseren Delegierten unter der Motorhaube nennen werden ... theoretisch! In der Praxis sind die Dinge viel komplizierter.
Abgeleitete Rand
-Methoden wie next_bool(Probability)
, next_lim(uXX)
und next_range(Range)
werden durch verschiedene Wahrscheinlichkeitsverteilungen gesichert. next_bool
zeichnet beispielsweise aus der Bernoulli -Verteilung, während next_lim
und next_range
eine skalierte einheitliche Verteilung mit einer zusätzlichen Debiasing -Schicht verwenden. Darüber hinaus ist die Zuordnung zwischen den verschiedenen Verteilungen ein internes Implementierungsdetail, das sich ändern kann. Die Debiasing -Schicht allein hat mehrere Implementierungen, die für Arten unterschiedlicher Breiten optimiert sind. Mit anderen Worten, die Zuordnungen von next_u128
bis next_bool
, next_lim
und next_range
und Nichttrivial; Es ist nicht etwas, das Sie ohne Taschenrechner und einige Kenntnisse der modularen Arithmetik verspotten möchten.
Zum Glück lässt Rand
uns diese Mapping -Funktionen "umgehen". Hier kommen die beiden anderen Delegierten ins Spiel. Im folgenden Beispiel verspotten wir das Ergebnis von next_bool
.
use tinyrand :: { Rand , Probability } ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_bool ( |_ , _| false ) ;
if rand . next_bool ( Probability :: new ( 0.999999 ) ) {
println ! ( "very likely" ) ;
} else {
// we can cover this branch thanks to the magic of mocking
println ! ( "very unlikely" ) ;
}
Der next_bool
-Delegate erhält eine Surrogate
, die sowohl eine Rand
-Implementierung als auch einen Keeper des Aufrufstatus ist. Mit dem Ersatz können wir bool
S als SO ableiten:
use tinyrand :: { Rand , Probability } ;
use tinyrand_alloc :: Mock ;
let mut rand = Mock :: default ( ) . with_next_bool ( |surrogate , _| {
surrogate . state ( ) . next_bool_invocations ( ) % 2 == 0
} ) ;
assert_eq ! ( true , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
assert_eq ! ( false , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
assert_eq ! ( true , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
assert_eq ! ( false , rand.next_bool ( Probability ::new ( 0.5 ) ) ) ;
Der Ersatz lässt den Delegierten auch die verspotteten Methoden aus dem Schein rufen.
Der letzte Delegierte wird verwendet, um aufgrund ihres Isomorphismus sowohl next_lim
als auch next_range
-Methoden zu verspotten. Unter der Motorhaube stellt next_range
zu next_lim
ein, so dass für alle Grenzgrenzen ( M
, N
) M
< N
, next_range(M..N)
= M
+ next_lim(N - M)
. So ist alles in der Praxis verspottet:
use tinyrand :: { Rand , RandRange } ;
use tinyrand_alloc :: Mock ;
enum Day {
Mon , Tue , Wed , Thu , Fri , Sat , Sun
}
const DAYS : [ Day ; 7 ] = [ Day :: Mon , Day :: Tue , Day :: Wed , Day :: Thu , Day :: Fri , Day :: Sat , Day :: Sun ] ;
let mut rand = Mock :: default ( ) . with_next_lim_u128 ( |_ , _| 6 ) ;
let day = & DAYS [ rand . next_range ( 0 .. DAYS . len ( ) ) ] ;
assert ! ( matches! ( day, Day :: Sun ) ) ; // always a Sunday
assert ! ( matches! ( day, Day :: Sun ) ) ; // yes!!!
tinyrand
getestet? Dieser Abschnitt beschreibt kurz den tinyrand
-Testansatz. Es richtet sich an diejenigen, die -
Der tinyrand
-Testprozess ist in vier Ebenen aufgeteilt:
tinyrand
zu behaupten. Mit anderen Worten, jede Codezeile wird mindestens einmal ausgeübt, grundlegende Erwartungen werden bestätigt und es gibt wahrscheinlich keine trivialen Defekte.tinyrand
besteht. Dies sind formale Hypothesentests, bei denen angenommen wird, dass die Quelle zufällig ist (die Nullhypothese) und nach Beweisen suchen, um diese Annahme zu zerstreuen (die alternative Hypothese).Die Unit -Tests zielen nicht darauf ab, numerische Qualitäten zu gründen. Sie sind rein funktionaler Natur. Ziele sind - -
tinyrand
basiert auf der Philosophie, dass eine Codezeile, die nicht nachdenklich ausgeübt wird, entfernt werden sollte. Es gibt keine Ausnahmen von dieser Regel.true
Ergebnisses gegenüber false
in der Erzeugung von bool
s. Die Funktionen für die Zuordnung von der einheitlichen Verteilung auf eine benutzerdefinierte Kartierung sind nicht trivial und erfordern eine Debiasing -Schicht. tinyrand
verwendet je nach Wortbreite unterschiedliche Debiasing -Methoden. Der Zweck der Domain -Transformationstests besteht darin, zu überprüfen, ob diese Funktionalität wie erwartet funktioniert und Ableitungsstichproben stattfindet. Es überprüft jedoch nicht die numerischen Eigenschaften des Debiasings. Die synthetischen Benchmarks werden verwendet, um die heißen Pfade der tinyrand
-PRNGs auszuüben und die Ergebnisse mit Peer -Bibliotheken zu vergleichen. Die Benchmarks testen die Erzeugung von Zahlen mit verschiedenen Wortlängen, Transformationen/Debien und die Erzeugung gewichteter bool
. Eine Untergruppe dieser Benchmarks ist auch in den CI -Tests enthalten, sodass es ein wenig einfacher ist, die Leistung von tinyrand
über Ausschaltungsversionen hinweg zu vergleichen.
tinyrand
wird mit einer integrierten statistischen Testsuite gebündelt, die von Dehard, Dieharder und NIST SP 800-22 inspiriert ist. Die tinyrand
-Suite ist zugegebenermaßen viel kleiner als jeder dieser Tests; Es ist beabsichtigt, die bereits wesentliche und leicht zugängliche Arbeit in diesem Bereich zu replizieren, sondern ein Sicherheitsnetz zu schaffen, das sowohl bei jedem Commiting sehr effektiv zu erkennen und schnell genug zu erkennen, und schnell genug, um bei jedem Commit betrieben zu werden.
Die folgenden Tests sind enthalten.
Rand
-Instanz durch, indem Sie den Wert eines einzelnen Bits maskieren und überprüfen, ob das Bit -Bit auf 1 im erwarteten Bereich liegt. Für jeden nachfolgenden Versuch wird die Maske nach links verschoben und die Hypothese erneut getestet. Der Test erfolgt über mehrere Zyklen; Jeder Zyklus umfasst 64 Bernoulli -Versuche (eines für jedes Stück u64
).bool
mit einer ausgewählten Wahrscheinlichkeit aus einem 64-Bit-Wort zu erhalten. Der Test umfasst eine Reihe von Bernoulli -Studien mit einer anderen (zufällig ausgewählten) Gewichtung in jedem Versuch und simuliert einen Lauf von Münzflips. In jedem Versuch behauptet H0, dass die Quelle zufällig ist. (Dh die Anzahl der 'Köpfe' fällt in ein statistisch akzeptabler Intervall.)u64
S in separaten Versuchen aufgenommen werden. In jedem Versuch gehen wir davon aus, dass die Werte einzelner Bits mit einer Wahrscheinlichkeit von 0,5 IID sind, was bestätigt, dass die Anzahl der auf 1 festgelegten Häufigkeit innerhalb des erwarteten Bereichs liegt. Für eine zufällige Quelle folgt die Anzahl der 1s (und 0s) einem Bernoulli -Prozess. Jede der Tests von tinyrand
wird nicht nur gegen seine eigenen PRNGs, sondern auch gegen absichtliche fehlerhafte Implementierungen ausgeübt, die zur Überprüfung der Wirksamkeit des Tests verwendet werden. Die Tests müssen konsequent nicht H0 für die richtigen PRNGs ablehnen und H1 für die fehlerhaften akzeptieren.
Die statistischen Tests werden selbst aus zufälligen Werten ausgesät. Zufälligkeit wird verwendet, um die zu testenden PRNGs zu säen (jeder Versuch ist unabhängig voneinander ausgesät), den Bernoulli -Experimenten Gewichtsmittel zuzuweisen, ausgewählte Integer -Bereiche zum Testen von Transformationsfunktionen und Debiasing, Kontrollwerte für das Testen von Kollisionen usw. Wir verwenden das rand
-Paket als Steuerungspaket, damit ein Defekt in tinyrand
einen Test nicht versehentlich auf eine Weise untergraben kann, die sich selbst maskiert. Die Tests werden so ausgesät, dass ihre Wahl der Parameter völlig deterministisch und somit wiederholbar ist, obwohl sie durch den Parameterraum einen zufälligen Ausflug zu haben scheinen. Dies ist aufgrund der Möglichkeit des Typ -I -Fehlers (fälschlicherweise die Nullhypothese abzulehnen), die nicht zeitweise auftreten darf, insbesondere in CI -Umgebungen. Mit anderen Worten, das Testen der Zufälligkeit kann nicht dem Zufall überlassen werden .
Eine Möglichkeit true
die Zufälligkeitshypothese zu testen, besteht darin, einen Satz von Parametern N
(z M
. Die Begründung ist, dass je größer die Stichprobe ist, desto wahrscheinlicher wird eine nachweisbare Anomalie. Dies ist im Allgemeinen nicht sehr effektiv, um bestimmte Arten von Anomalien zu erkennen, die PRNGs nur unter sehr bestimmten Bedingungen beeinflussen können. Zum Beispiel kann eine schlecht geschriebene Debiasing -Funktion für die meisten kleinen Ganzzahlbereiche und sogar einige große (diejenigen, die nahezu zwei Kräften nahe stehen) gut ab. Wenn der Test Parameter ungünstig enthält, kann es keine Anomalien finden, egal wie umfassend er diese Parameter testet.
Eine viel bessere Möglichkeit, PRNG zu testen, besteht darin, die Vielfalt in das Testregime einzuführen und eine große Anzahl kleiner Versuche mit unterschiedlichen Parametern und nicht mit einer sehr großen Versuch durchzuführen. Genau das machen die statistischen tinyrand
-Tests - führen Sie mehrere Studien mit zufällig (aber deterministisch) ausgewählten Parametern durch. Dies enthüllt sofort das Problem der Mehrfachvergleiche. Betrachten Sie eine a priori ideal prng. Es wird häufig Zahlen erzeugt, die gemäß einigen vereinbarten Maßnahmen "zufällig" erscheinen. Gelegentlich erzeugt es jedoch Ausgang, die durch das gleiche Maß nicht randomiert werden. Sogar eine ideale Quelle erzeugt beispielsweise sehr langfristig oder Nullen. In der Tat würde es auch nicht gerendert werden, dies zu tun. Leider wird dies einen p-Wert erzeugen, der selbst den entspannendsten Test ausfällt ... irgendwann. Dies ist ein Problem für einzelne Hypothesentests, aber in mehreren Hypothesentests proportional verschärft.
Die tinyrand
-integrierten Tests beheben dieses Problem mit der meth-bonferroni-Korrekturmethode. Die Holm-Bonferroni-Korrektur unterdrückt Typ I-Fehler und behält eine gute statistische Leistung bei-Unterdrückung der Fehler vom Typ II. Es scheint gut für die Bedürfnisse von tinyrand
zu funktionieren, insbesondere zu erkennen, dass die Zahlenversuche im Allgemeinen im Bereich 100-1000 gehalten werden. ( tinyrand
-Tests sind sehr schnell ausgelegt, wodurch die Anzahl der Versuche praktisch gefesselt ist. Im Idealfall sollten alle statistischen Tests innerhalb weniger Sekunden abgeschlossen werden, damit sie als Teil des Routine -Entwicklungsflusss vorgeschrieben werden.)
Die Dieharder Test Suite erweitert Marsaglias ursprüngliche Diehard -Batterie von Tests. Es ist mit einer großen Anzahl von Tests gebündelt und dauert lange (~ 1 Stunde). tinyrand
verfügt über ein Dienstprogramm zum Pumpen von Zufallsausgang zu Dieharder, das normalerweise auf Ad -hoc -Basis ausgeführt wird. Die Dieharder -Batterie sollte ausgeführt werden, wenn ein PRNG eine materielle Änderung erfährt, was selten ist - sobald ein PRNG -Algorithmus implementiert ist, bleibt er im Allgemeinen unberührt, es sei denn, er wird entweder neu oder ein Defekt gefunden. Dieharder ist wohl nützlicher, um experimentelle PRNGs mit tinyrand
zu bauen und zu testen. Die anderen drei Teststufen reichen für die Wartung des tinyrand
-Pakets aus.
tinyrand
gegen Dieharder zu laufen:
cargo run --release --bin random -- wyrand 42 binary 1T | dieharder -g 200 -a
Der obige Befehl verwendet das mit der Nummer 42 ausgesetzte Wyrand PRNG, wodurch eine binäre Ausgabe über 1 Billion 64-Bit-Wörter erzeugt wird. Es ist stdout
an dieharder
gepumpt. (In der Praxis wird Dieharder unter 31 Milliarden Zahlen konsumieren.)
Ein Wort der Vorsicht: Dieharder hat keinen Mechanismus zum Umgang mit Fehlern von Typ I bei mehreren Hypothesentests - teilweise, weil sich die Tests nicht nur in Parametern unterscheiden. Dieharder begrenzt die Hypothesen -Tests auf den Umfang eines individuellen Tests; Es gibt keine übergeordnete Hypothese, die eine PRNG entweder als Anpassung oder nicht auf der Grundlage der Anzahl der bestandenen Tests einstuft oder das Konfidenzniveau auf andere Weise für Fehler vom Typ I anpasst.