Numpy: Die von Numpy bereitgestellte PCG-Implementierung weist eine signifikante, gefährliche Selbstkorrelation auf

Erstellt am 20. Mai 2020  ·  104Kommentare  ·  Quelle: numpy/numpy

Der von Numpy verwendete PCG-Generator weist eine signifikante Selbstkorrelation auf. Das heißt, für jede aus einem Samen erzeugte Sequenz gibt es eine große Anzahl korrelierter, nicht überlappender Sequenzen, die von anderen Samen ausgehen. Mit "korreliert" meine ich, dass Sie durch Verschachteln zweier solcher Sequenzen und Testen des Ergebnisses Fehler erhalten, die nicht in jeder Sequenz einzeln aufgetreten sind.

Die Wahrscheinlichkeit, dass zwei Generatoren aus einem großen Satz von Terminals zwei dieser Sequenzen erhalten, ist nicht vernachlässigbar. Warum dies aus mathematischer Sicht geschieht, ist bekannt, wird hier jedoch ausführlich erläutert: http://prng.di.unimi.it/pcg.pgp (siehe "Folgen innerhalb desselben Generators").

Um dieses Problem direkt zu zeigen, habe ich dieses einfache C-Programm geschrieben, das den Numpy-Code wiederverwendet: http://prng.di.unimi.it/intpcgnumpy.c . Das Programm nimmt zwei 128-Bit-Zustände von zwei Generatoren (mit derselben LCG-Konstante oder demselben "Strom") in Form von hohen und niedrigen Bits, verschachtelt ihre Ausgabe und schreibt sie in binärer Form. Sobald wir es über PractRand senden, sollten wir keinen statistischen Fehler sehen, da die beiden Streams unabhängig sein sollten. Wenn Sie jedoch versuchen, von zwei Zuständen mit denselben 64 unteren Bits zu beginnen, erhalten Sie:

./intpcgnumpy 0x596d84dfefec2fc7 0x6b79f81ab9f3e37b 0x8d7deae980a64ab0 0x6b79f81ab9f3e37b | stdbuf -oL ~ / svn / c / xorshift / Practrand / RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded
RNG_test mit PractRand Version 0.94
RNG = RNG_stdin, seed = unbekannt
Testset = erweitert, faltbar = extra

rng=RNG_stdin, seed=unknown
length= 128 megabytes (2^27 bytes), time= 2.2 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(0+0,13-2,T)                  R= +27.6  p =  1.0e-13    FAIL
  BCFN(0+1,13-2,T)                  R= +68.0  p =  2.3e-34    FAIL !!!
  BCFN(0+2,13-3,T)                  R= +90.8  p =  8.8e-43    FAIL !!!
  BCFN(0+3,13-3,T)                  R=+120.6  p =  6.9e-57    FAIL !!!!
  DC6-6x2Bytes-1                    R=  +8.9  p =  4.0e-5   mildly suspicious
  DC6-5x4Bytes-1                    R= +15.7  p =  4.3e-9   very suspicious
  [Low1/8]BCFN(0+0,13-4,T)          R= +11.6  p =  4.9e-5   unusual
  ...and 1074 test result(s) without anomalies

Sie können sogar tiefer gehen - Sie benötigen nur die gleichen 58 unteren Bits:

./intpcgnumpy 0x596d84dfefec2fc7 0x0579f81ab9f3e37b 0x8d7deae980a64ab0 0x6b79f81ab9f3e37b | stdbuf -oL ~/svn/c/xorshift/practrand/RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded

[...]
rng=RNG_stdin, seed=unknown
length= 32 gigabytes (2^35 bytes), time= 453 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/16]FPF-14+6/32:cross        R= +11.6  p =  4.0e-10   VERY SUSPICIOUS
  [Low1/32]FPF-14+6/32:cross        R= +16.5  p =  3.2e-14    FAIL
  [Low1/32]FPF-14+6/16:cross        R= +12.8  p =  3.8e-11   VERY SUSPICIOUS
  [Low1/64]FPF-14+6/64:cross        R=  +6.8  p =  4.8e-6   mildly suspicious
  [Low1/64]FPF-14+6/32:cross        R=  +6.0  p =  1.9e-5   unusual
  [Low1/64]FPF-14+6/16:cross        R=  +5.5  p =  5.8e-5   unusual
  [Low4/32]FPF-14+6/64:all          R=  +5.8  p =  5.9e-5   unusual
  [Low4/32]FPF-14+6/32:(0,14-0)     R=  +7.7  p =  1.0e-6   unusual
  [Low4/32]FPF-14+6/32:(1,14-0)     R=  +7.7  p =  9.1e-7   unusual
  [Low4/32]FPF-14+6/32:all          R=  +6.5  p =  1.3e-5   unusual
  [Low4/64]FPF-14+6/64:all          R=  +5.9  p =  5.1e-5   unusual
  [Low4/64]FPF-14+6/64:cross        R=  +8.2  p =  3.0e-7   suspicious
  [Low4/64]FPF-14+6/32:(0,14-0)     R=  +7.6  p =  1.0e-6   unusual
  [Low8/64]FPF-14+6/64:(0,14-0)     R= +17.0  p =  2.2e-15    FAIL
  [Low8/64]FPF-14+6/64:(1,14-0)     R=  +9.1  p =  5.1e-8   mildly suspicious
  [Low8/64]FPF-14+6/64:all          R= +12.7  p =  2.1e-11   VERY SUSPICIOUS
  [Low8/64]FPF-14+6/32:(0,14-0)     R= +12.8  p =  1.7e-11   VERY SUSPICIOUS
  [Low8/64]FPF-14+6/32:all          R= +11.0  p =  9.3e-10   VERY SUSPICIOUS
  ...and 1696 test result(s) without anomalies

Beachten Sie, dass Sie nur eine halbe Million Generatoren benötigen, die zufällig beginnen (Geburtstagsparadoxon), um die 50% ige Wahrscheinlichkeit zu erhöhen, dass zwei Generatoren von zwei korrelierten Startwerten (zufällig ausgewählt) ausgehen. Und wenn Sie die Wahrscheinlichkeit berücksichtigen, dass sie nicht genau vom selben Zustand ausgehen, sondern signifikante überlappende Korrelationssequenzen aufweisen, benötigen Sie viel weniger.

Ein vernünftiger Generator aus der Literatur wird sich nicht so verhalten. Sie können zwei beliebige Startzustände von MRG32k3a, SFC64, CMWC, xoshiro256 ++ usw. kontrovers auswählen. Solange Sie nicht überlappende Sequenzen generieren, werden die oben genannten Fehler nicht angezeigt. Dies ist ein Hauptnachteil, der auftreten kann, wenn mehrere Geräte den Generator verwenden und man davon ausgeht (wie es sein sollte), dass diese Sequenzen paarweise keine Korrelation aufweisen sollten. Die Korrelation kann zu unerwünschtem Verhalten führen, das schwer zu erkennen ist.

Bitte dokumentieren Sie zumindest irgendwo, dass der Generator nicht an mehreren Terminals oder in einer stark parallelen Umgebung verwendet werden darf.

Dasselbe kann mit verschiedenen "Strömen" passieren, da die von einem LCG durch Ändern der Additivkonstante erzeugten Sequenzen alle gleich modulo sind, ein Vorzeichenwechsel und eine Additivkonstante. Eine Diskussion finden Sie hier: https://github.com/rust-random/rand/issues/907 und eine vollständige mathematische Diskussion des Problems finden Sie hier: https://arxiv.org/abs/2001.05304 .

numpy.random

Alle 104 Kommentare

@imneme , @bashtage , @rkern wären die Behörden hier, aber ich denke, wir haben dies durchgearbeitet , und deshalb haben wir die Schnittstelle SeedSequence.spawn Schnittstelle jumped vorgezogen. Zum Beispiel gab es diese Diskussion, als wir die API diskutierten. Bitte überprüfen Sie die Hinweise hier https://numpy.org/devdocs/reference/random/parallel.html und schlagen Sie bei Bedarf Verbesserungen vor.

@mattip Das hat nichts mit Springen zu tun.

Ich denke, in der Praxis ist es schwierig, umfassende Änderungen vorzunehmen, obwohl eine verbesserte Dokumentation immer eine gute Idee ist.

Ich würde wahrscheinlich AESCounter für jeden mit AES-NI oder SPECK128 für jeden ohne hochparallele Einstellungen empfehlen.

Dasselbe kann mit verschiedenen "Strömen" passieren, da die von einem LCG durch Ändern der Additivkonstante erzeugten Sequenzen alle gleich modulo sind, ein Vorzeichenwechsel und eine Additivkonstante.

Können Sie das quantifizieren? Ich kann die Fehler mit demselben Inkrement replizieren, aber wir legen sowohl das Inkrement als auch den Status fest, und ich habe noch keinen Fehler mit zwei verschiedenen zufälligen Inkrementen beobachtet. Wenn die Inkremente auch sorgfältig konstruiert werden müssen, würde dies die praktische Häufigkeit von Geburtstagskollisionen beeinflussen.

https://gist.github.com/rkern/f46552e030e59b5f1ebbd3b3ec045759

❯ ./pcg64_correlations.py --same-increment | stdbuf -oL ./RNG_test stdin64 -tf 2 -te 1 -tlmaxonly -multithreaded
0x56b35656ede2b560587e4251568a8fed
0x93526034ed105e9e587e4251568a8fed
[
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 115244779949650410574112983538102603757,
            "inc": 137507567477557873606783385380908979143
        },
        "has_uint32": 0,
        "uinteger": 0
    },
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 195824235027336627448689568147458133997,
            "inc": 137507567477557873606783385380908979143
        },
        "has_uint32": 0,
        "uinteger": 0
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0x4bf19f7b
test set = expanded, folding = extra

rng=RNG_stdin64, seed=0x4bf19f7b
length= 128 megabytes (2^27 bytes), time= 3.0 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN_FF(2+0,13-3,T)               R= +59.9  p =  3.8e-28    FAIL !!!       
  BCFN_FF(2+1):freq                 R= +89.0  p~=   6e-18     FAIL !         
  BCFN_FF(2+2):freq                 R= +39.6  p~=   6e-18     FAIL !         
  BCFN_FF(2+3):freq                 R= +14.6  p~=   6e-18     FAIL !         
  BCFN_FF(2+4):freq                 R= +10.3  p~=   5e-11   very suspicious  
  DC6-9x1Bytes-1                    R=  +7.1  p =  5.6e-4   unusual          
  DC6-6x2Bytes-1                    R= +18.9  p =  1.0e-10   VERY SUSPICIOUS 
  DC6-5x4Bytes-1                    R= +11.2  p =  1.4e-6   suspicious       
  [Low4/16]BCFN_FF(2+0):freq        R= +19.5  p~=   6e-18     FAIL !         
  [Low4/16]FPF-14+6/16:all          R=  +5.6  p =  1.0e-4   unusual          
  [Low4/16]FPF-14+6/4:all           R=  +5.9  p =  4.6e-5   unusual          
  [Low4/32]BCFN_FF(2+0):freq        R=  +6.5  p~=   2e-5    unusual          
  [Low8/32]BCFN_FF(2+0):freq        R= +15.1  p~=   6e-18     FAIL !         
  [Low8/32]FPF-14+6/32:all          R=  +8.4  p =  2.5e-7   very suspicious  
  [Low8/32]FPF-14+6/32:all2         R=  +9.0  p =  7.8e-5   unusual          
  [Low8/32]FPF-14+6/16:(0,14-0)     R= +12.4  p =  4.5e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/16:all          R= +15.5  p =  5.2e-14    FAIL           
  [Low8/32]FPF-14+6/16:all2         R= +41.4  p =  2.6e-16    FAIL !         
  [Low8/32]FPF-14+6/4:(0,14-0)      R=  +6.9  p =  5.9e-6   unusual          
  [Low8/32]FPF-14+6/4:all           R=  +7.9  p =  6.6e-7   suspicious       
  ...and 871 test result(s) without anomalies

OK, ich werde es noch einmal versuchen.

In einem LCG mit einem Potenz-2-Modul gibt es keine Mehrfachströme. Viele glaubten es in den frühen Tagen, und es gibt sogar lange alte Papiere, die behaupten, interessante Dinge mit diesen "Strömen" zu tun, aber es ist seit Jahrzehnten bekannt, dass die Umlaufbahnen, die Sie durch Ändern der Konstanten erhalten, alle das gleiche Modulo und Additiv sind konstant und möglicherweise ein Vorzeichenwechsel_. Am weitesten kann ich es verfolgen

Mark J. Durst, Verwendung linearer Kongruenzgeneratoren zur parallelen Zufallszahlengenerierung,
1989 Winter Simulation Conference Proceedings, IEEE Press, 1989, S. 462–466.

Also schrieb ich ein anderes Programm http://prng.di.unimi.it/corrpcgnumpy.c, in dem Sie Folgendes einstellen können:

  • Ein Anfangszustand für ein PRNG.
  • Ein Anfangszustand für ein anderes PRNG.
  • Eine beliebige "Stromkonstante" für das erste PRNG.
  • Eine willkürliche "Stromkonstante" für das zweite PRNG (beide sollten gerade oder beide ungerade sein; diese Einschränkung kann durch zusätzliches Fummeln aufgehoben werden).
  • Eine feste Anzahl niedrigerer Bits, die wir im zweiten PRNG kontrovers setzen werden, im Wesentlichen so, dass sie mit denselben Bits des ersten PRNG beginnen. Der Rest der Bits wird aus dem Ausgangszustand für das zweite von Ihnen bereitgestellte PRNG übernommen.

Dies ist also genau die Einstellung des ersten Programms, aber Sie können auch die Konstanten auswählen.

./corrpcgnumpy 0x596d84dfefec2fc7 0x6b79f81ab9f3e37b 0xac9c8abfcb89f65f 0xe42e8dff1c46de8b 0x8d7deae9efec2fc7 0x6b79f81ab9f3e37b 0x06e13e5e8c92c843 0xf92e8346feee7a21 56 | stdbuf -oL ~/svn/c/xorshift/practrand/RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded

rng=RNG_stdin, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 113 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]BCFN(0+0,13-1,T)          R= +27.2  p =  4.0e-14    FAIL
  [Low1/8]DC6-6x2Bytes-1            R= +10.9  p =  4.4e-6   suspicious
  [Low1/64]DC6-5x4Bytes-1           R=  -6.4  p =1-1.4e-4   unusual
  [Low8/64]FPF-14+6/64:(0,14-0)     R=  +8.4  p =  2.2e-7   mildly suspicious
  [Low8/64]FPF-14+6/64:all          R=  +8.7  p =  1.2e-7   suspicious
  [Low8/64]FPF-14+6/32:(0,14-0)     R= +10.2  p =  5.1e-9   suspicious
  [Low8/64]FPF-14+6/32:all          R=  +9.4  p =  2.7e-8   very suspicious
  [Low8/64]FPF-14+6/16:all          R=  +5.8  p =  6.4e-5   unusual
  ...and 1439 test result(s) without anomalies

Es gibt also mindestens 2 ^ 72 korrelierte Teilsequenzen, unabhängig davon, wie Sie die "Stream-Konstanten" auswählen, genau wie im Fall der gleichen Konstante.

Und wir haben eine lächerliche Menge an Spielraum für den Generator: Selbst wenn Sie anstelle des genauen Startpunkts, den ich berechne, einen Zustand vorher oder nachher verwenden würden, würde sich die Korrelation trotzdem zeigen. Sie können das Programm einfach mit einem zusätzlichen Parameter ändern, um dies zu tun.

Ich wiederhole noch einmal: Kein existierender moderner Generator aus der wissenschaftlichen Literatur hat dieses Fehlverhalten (natürlich hat ein 2-Potenz-LCG dieses Verhalten, aber um Gottes willen, das ist kein moderner Generator).

Sabastianos Kritik an PCG wird in diesem Blogbeitrag von 2018 angesprochen.

Die Kurzversion besagt, dass Sie, wenn Sie bestimmte Samen erfinden dürfen, aus so ziemlich jedem PRNG ein „schlecht aussehendes“ Verhalten zeigen können. Ungeachtet seiner Behauptung, dass PCG irgendwie einzigartig ist, ist PCG tatsächlich ziemlich konventionell - die Streams von PCG sind nicht schlechter als beispielsweise die von SplitMix, einem anderen weit verbreiteten PRNG.

Das ist völlig falsch. Um mir das Gegenteil zu beweisen, zeigen Sie zwei korrelierte nicht überlappende Sequenzen aus MRG32k3a oder xoshiro256 ++.

Ich habe nie gesagt, nicht überlappend. Zeigen Sie mir einen aktuell verfügbaren Test für xoshiro256 ++. dass zwei Samen Überlappungen vermeiden.

Im Gegensatz dazu habe ich einen PCG-Test, der zeigt, dass die von Ihnen gezeigten „Korrelationen“ im Wesentlichen eine Form der Überlappung sind.

Ich kann FUD nicht wie "im Wesentlichen" und "ein Formular" bekämpfen, aber ich habe http://prng.di.unimi.it/intpcgnumpy.c so geändert, dass es anfangs jedes PRNG 10 Milliarden Mal iteriert und mit einem Fehler beendet wird Nachricht, wenn die generierte Sequenz den Anfangszustand des anderen PRNG durchläuft. Dies garantiert, dass die ersten 160 GB Daten in Practrand aus nicht überlappenden Sequenzen stammen:

./intpcgnumpy 0x596d84dfefec2fc7 0x0579f81ab9f3e37b 0x8d7deae980a64ab0 0x6c79f81ab9f3e37b | stdbuf -oL ~/svn/c/xorshift/practrand/RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded
[...]
rng=RNG_stdin, seed=unknown
length= 64 gigabytes (2^36 bytes), time= 926 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]FPF-14+6/64:(0,14-0)      R=  +8.8  p =  8.7e-8   mildly suspicious
  [Low1/8]FPF-14+6/64:all           R=  +6.3  p =  2.1e-5   unusual          
  [Low1/16]FPF-14+6/64:(0,14-0)     R=  +7.6  p =  1.1e-6   unusual          
  [Low1/16]FPF-14+6/64:(1,14-0)     R=  +8.3  p =  2.9e-7   mildly suspicious
  [Low1/16]FPF-14+6/64:all          R=  +8.0  p =  5.8e-7   suspicious       
  [Low1/16]FPF-14+6/32:all          R=  +7.1  p =  3.9e-6   mildly suspicious
  [Low1/64]FPF-14+6/32:cross        R=  +7.1  p =  2.6e-6   mildly suspicious
  [Low4/32]FPF-14+6/64:(0,14-0)     R= +13.5  p =  4.3e-12   VERY SUSPICIOUS 
  [Low4/32]FPF-14+6/64:all          R=  +9.0  p =  5.9e-8   very suspicious  
  [Low4/64]FPF-14+6/64:(0,14-0)     R= +11.4  p =  3.8e-10  very suspicious  
  [Low4/64]FPF-14+6/64:all          R=  +8.0  p =  5.3e-7   suspicious       
  [Low4/64]FPF-14+6/32:(0,14-0)     R= +10.3  p =  3.6e-9   suspicious       
  [Low4/64]FPF-14+6/32:all          R=  +6.1  p =  3.2e-5   unusual          
  [Low8/64]FPF-14+6/64:(0,14-0)     R= +18.6  p =  8.4e-17    FAIL           
  [Low8/64]FPF-14+6/64:(1,14-0)     R= +11.4  p =  3.9e-10  very suspicious  
  [Low8/64]FPF-14+6/64:(2,14-0)     R=  +8.3  p =  2.8e-7   mildly suspicious
  [Low8/64]FPF-14+6/64:all          R= +15.3  p =  6.9e-14    FAIL           
  [Low8/64]FPF-14+6/32:(0,14-0)     R=  +7.8  p =  7.1e-7   unusual          
  [Low8/64]FPF-14+6/32:(1,14-0)     R=  +7.2  p =  2.7e-6   unusual          
  [Low8/64]FPF-14+6/32:all          R=  +5.8  p =  6.9e-5   unusual          
  ...and 1786 test result(s) without anomalies

Diese speziellen Initialisierungsdaten haben nur 56 niedrigere feste Bits, so dass man 2 ^ 72 korrelierte Sequenzen erzeugen kann, indem man die höheren Bits umdreht. Die statistischen Fehler treten bereits nach 64 GB Daten auf, was zeigt, dass Überlappungen nicht für die Korrelation verantwortlich sind. Es ist natürlich möglich, dass bei anderen spezifischen gezielten Auswahlen eine Überlappung vor 64 GB auftritt - dies ist ein spezifisches Beispiel. Es ist jetzt einfach zu überprüfen, ob Überlappungen nicht das Problem sind - der Generator weist nur eine Menge unerwünschter interner Korrelationen auf.

Bitte beachten Sie den Verhaltenskodex . Versuchen Sie, Ihre Kommentare im Einklang mit den Anweisungen zu halten, "einfühlsam, einladend, freundlich und geduldig" zu sein und "vorsichtig mit den Worten umzugehen, die wir wählen. Wir sind vorsichtig und respektvoll in unserer Kommunikation".

Ich habe nie gesagt, nicht überlappend. Zeigen Sie mir einen aktuell verfügbaren Test für xoshiro256 ++. dass zwei Samen Überlappungen vermeiden.

Nun, es ist trivial: Entscheiden Sie die Länge des Streams, iterieren Sie und stellen Sie sicher, dass die beiden Streams den Ausgangszustand nicht überschreiten. Es ist derselbe Code, den ich verwendet habe, um anzuzeigen, dass sich die korrelierten PCG-Streams im Programm http://prng.di.unimi.it/intpcgnumpy.c nicht überlappen.

Ungeachtet seiner Behauptung, dass PCG irgendwie einzigartig ist, ist PCG tatsächlich ziemlich konventionell - die Streams von PCG sind nicht schlechter als beispielsweise die von SplitMix, einem anderen weit verbreiteten PRNG.

IMHO ist die Selbstkorrelation innerhalb von PCG viel schlimmer. Es gibt kein Ergebnis für den additiven Generator, der einer SplitMix-Instanz zugrunde liegt, analog zu Dursts dramatischen Ergebnissen von 1989 über LCGs.

Die sehr milden Probleme von SplitMix sind jedoch bekannt, und JEP 356 wird eine neue Klasse von spaltbaren Generatoren, LXM, bereitstellen, um diese Probleme zu lösen. Es wäre an der Zeit, weiterzumachen und auch PCG durch etwas weniger Fehlerhaftes zu ersetzen.

Das zugrunde liegende Problem ist für beide Generatoren bekannt und auf den fehlenden Staatsmix zurückzuführen. Wenn Sie das Bit _k_ des Status eines dieser Generatoren ändern, wird die Änderung niemals unter das Bit _k_ übertragen. Dies ist bei LCGs mit Primemodul, bei F₂-Lineargeneratoren, CMWC-Generatoren usw. nicht der Fall. Alle anderen Generatoren versuchen, ihren Zustand so schnell und so weit wie möglich zu mischen.

Das Gleichsetzen der Probleme von PCG und SplitMix ist ein roter Hering. SplitMix hat einen sehr einfachen zugrunde liegenden Generator, der nur additiv ist, aber darüber hinaus gibt es eine sehr leistungsstarke Verschlüsselungsfunktion: Es ist Applebys 64-Bit-Finalizer der MurmurHash3-Hash-Funktion, die in einer Reihe von Kontexten und häufig verwendet wurde wurde von Stafford verbessert (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html). Die Konstanten der Funktion wurden so trainiert, dass sie spezifische, messbare Lawineneigenschaften aufweisen. Sogar Änderungen in einer kleinen Anzahl von Bits neigen dazu, sich über die gesamte Ausgabe zu verteilen. Mit anderen Worten, SplitMix steht auf der Schulter von Riesen.

Im Gegenteil, die LCG-zugrunde liegenden PCG-Generatoren haben die gleichen Mischungsprobleme, aber die Verschlüsselungsfunktionen sind nur eine einfache Folge von arithmetischen und logischen Operationen, die vom Autor ohne theoretische oder statistische Garantie zusammengestellt wurden. Wenn sie entwickelt worden wären, um die Tatsache zu berücksichtigen, dass alle Sequenzen des zugrunde liegenden LCG das gleiche Modulo, eine additive Konstante und möglicherweise ein Vorzeichenwechsel sind, wäre es möglich gewesen, das Problem anzugehen.

Der Autor hatte jedoch keine Ahnung, dass die Sequenzen so leicht voneinander ableitbar waren. Dies ist aus dieser Erklärung in Abschnitt 4.2.3 des technischen Berichts der PCG (https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf) leicht ersichtlich:

"Jede Wahl von _c_ führt zu einer anderen Folge von Zahlen, die keines ihrer aufeinanderfolgenden Ausgabepaare mit einer anderen Folge gemeinsam hat."

Dies wird als Beweis dafür angesehen, dass die Sequenzen unterschiedlich sind, dh dass das zugrunde liegende LCG mehrere Streams bereitstellt. Dursts negative Ergebnisse von 1989 zu diesem Thema erscheinen nirgendwo in der Zeitung. Wie ich zuvor bemerkt habe, sind nach diesen Ergebnissen alle diese Sequenzen gleich, modulo eine additive Konstante und möglicherweise ein Vorzeichenwechsel (für LCGs mit einem Potenz-2-Modul maximaler Wirksamkeit, wie es bei PCG der Fall ist).

Ich bin sicher, dass es ein wahrer Fehler ist, Dursts Ergebnisse nicht zu zitieren, aber das Problem ist, dass Sie, sobald Sie überzeugt sind, dass das zugrunde liegende LCG, das Sie verwenden, "Streams" bereitstellt, die in gewissem Sinne "unterschiedlich" sind, wenn dies nicht der Fall ist, am Ende mit einem Generator wie PCG, in dem es für jede Teilsequenz 2 ^ 72 nicht überlappende, korrelierte Teilsequenzen gibt, selbst wenn Sie den "Stream" ändern.

Vielen Dank für Ihre Eingabe. Im Moment interessiere ich mich nicht für binäre Urteile wie "PCG ist gut / schlecht". Bitte benutzen Sie Ihre eigenen Foren für solche Diskussionen. Was hier zum Thema gehört, ist, was Numpy tun wird, und dieses endgültige Urteil gehört den Numpy-Entwicklern. Wir schätzen das Fachwissen, das Sie alle in diese Diskussion einbringen, aber ich möchte es eher auf die zugrunde liegenden Fakten als auf das endgültige Urteil konzentrieren. Ich schätze besonders quantitative Aussagen, die mir eine Vorstellung davon geben, wie viel Spielraum wir haben. Wenn meine früheren Urteile falsch waren, lag es daran, dass ich zu früh zum Urteil gesprungen bin, und ich würde mich über Ihre Unterstützung freuen, um dies erneut zu vermeiden. Vielen Dank.

Beachten Sie, dass Sie nur eine halbe Million Generatoren benötigen, die zufällig beginnen (Geburtstagsparadoxon), um die 50% ige Wahrscheinlichkeit zu erhöhen, dass zwei Generatoren von zwei korrelierten Startwerten (zufällig ausgewählt) ausgehen.

@vigna Kannst du mich durch diese Berechnung führen? Die mir vertraute Berechnung der Geburtstagskollision ergibt die 50% ige Chance einer n -Bit-Kollision bei 2**(n/2) Gegenständen (geben oder nehmen Sie einen Faktor von 2). Eine halbe Million ist 2**19 , also scheinen Sie zu behaupten, dass die gefährlichen Korrelationen bei einer etwa 40-Bit-Kollision in den unteren Bits beginnen, aber ich habe keine Beweise dafür gesehen, dass dies praktisch beobachtbar ist. Ich habe ein Paar getestet, das die unteren 40 Bits teilt, und habe in PractRand 16 TiB erreicht, bevor ich den Test abgebrochen habe. Wenn Sie einen Fehler bei einer 40-Bit-Kollision beobachtet haben, wie viele TiB mussten Sie testen, um ihn zu sehen?

Ich bin überzeugt, dass das Ändern des Inkrements die Kollisionswahrscheinlichkeit nicht beeinflusst. Eine weitere Diskussion der Vorzüge von "PCG-Streams" ist nicht zum Thema. Diese Diskussion als Ausrede zu benutzen, um wiederholt auf "den Autor" zu hämmern, ist besonders unerwünscht und verstößt gegen unseren Verhaltenskodex . Das Fortbestehen bedeutet, dass wir ohne Ihre Eingabe fortfahren müssen. Vielen Dank.

@imneme Dies scheint mit den Problemen beim Springen um ein Vielfaches einer großen Potenz von 2 zu tun zu haben. Wenn ich ein Paar von PCG64 Instanzen mit demselben Inkrement konstruiere und die niedrigeren n Bits, der Abstand, den ich zwischen den beiden berechne, ist ein Vielfaches von 1 << n . Es scheint, dass Ihre stärkere Ausgabefunktion DXSM diese Manifestation ebenfalls aufzulösen scheint. Ich habe ein Paar von PCG64DXSM -Instanzen getestet, die ein Inkrement und die unteren 64-Bit-Zustände ohne Probleme auf 2 TiB teilen.

OK, das ist peinlich: Es war eine halbe Milliarde, nicht eine halbe Million. Ein einzelner Buchstabe kann einen großen Unterschied machen. Ich entschuldige mich für den Ausrutscher.

Wie ich bereits sagte, ist dies jedoch die Wahrscheinlichkeit, genau den gleichen Ausgangszustand zu erreichen, nicht die Wahrscheinlichkeit einer signifikanten Überlappung korrelierter Teilsequenzen. Persönlich bevorzuge ich die Verwendung von PRNGs ohne korrelierte Teilsequenzen, da es viele davon gibt, aber wie Sie zu Recht sagen, liegt die Entscheidung nur bei Ihnen.

Es klingt nach einer absolut vernünftigen Lösung, die Verschlüsselungsfunktion so zu fixieren, dass sie bessere Mischeigenschaften aufweist.

Mein Beitrag sollte nur die strukturellen Unterschiede zwischen PCG und SplitMix verdeutlichen, da in einem früheren Beitrag behauptet wurde, sie hätten ähnliche Probleme, und ich denke nicht, dass dies eine korrekte Aussage ist. Sie können kein Programm wie http://prng.di.unimi.it/corrpcgnumpy.c für SplitMix schreiben.

@rkern , du hast gefragt:

@imneme Dies scheint mit den Problemen beim Springen um ein Vielfaches einer großen Potenz von 2 zu tun zu haben. Wenn ich ein Paar von PCG64 Instanzen mit demselben Inkrement konstruiere und die niedrigeren n Bits, der Abstand, den ich zwischen den beiden berechne, ist ein Vielfaches von 1 << n . Es scheint, dass Ihre stärkere Ausgabefunktion DXSM diese Manifestation ebenfalls aufzulösen scheint. Ich habe ein Paar von PCG64DXSM Instanzen getestet, die ein Inkrement und die unteren 64-Bit-Zustände ohne Probleme auf 2 TiB teilen.

Vielen Dank, dass Sie den Diskussionsthread aus dem letzten Jahr gefunden und wieder verlinkt haben. Ja, wie Sebastiano in seiner Antwort feststellt,

Es klingt nach einer absolut vernünftigen Lösung, die Verschlüsselungsfunktion so zu fixieren, dass sie bessere Mischeigenschaften aufweist.

XSL-RR ist am schwächeren Ende der Dinge. Im Gegensatz dazu bewirken sowohl die ursprüngliche RXS-M-Ausgabefunktion des PCG-Papiers als auch die neue DXSM-Ausgabefunktion mehr Verwürfelung und zeigen daher keine derartigen Probleme. DXSM (im letzten Jahr in diesem Commit zur PCG-Quelle hinzugefügt) wurde speziell entwickelt, um stärker als XSL-RR zu sein, hat jedoch eine ähnliche Zeitleistung (vgl. RXS-M, das langsamer ist). Ich habe DXSM letztes Jahr ziemlich hart getestet, aber 67 Tage nach dem Start hatten wir einen längeren Stromausfall, der den Server herunterfuhr (die USV-Batterie war leer) und den Testlauf beendete, aber zu diesem Zeitpunkt hatte er sich in beiden normalen Fällen als ziemlich gut erwiesen Testen (128 TB Leistung getestet) und Sprünge von 2 ^ 48 (64 TB Leistung getestet, da dies langsamer läuft).

Wenn sich RXS-M auch ohne das Entwerfen von DXSM um das Problem gekümmert hätte, ist eine Frage, warum ich stattdessen jemals die schwächere XSL-RR-Permutation verwendet habe - warum nicht immer sehr starkes Bit-Scrambling in der Ausgabefunktion verwenden? Die Antwort ist, dass es im Grunde genommen auf Zyklen ankommt. Geschwindigkeit ist für die Menschen wichtig, deshalb versuchen Sie zu vermeiden, dass Sie viel mehr durcheinander bringen, als Sie brauchen.

Dies ist ein Thema, mit dem Sebastiano vertraut ist, weil sein Ansatz und mein Ansatz viel gemeinsam haben. Wir verfolgen jeweils einen seit langem etablierten Ansatz, bei dem moderne statistische Tests (in meinem Fall LCGs und in seinem Fall die XorShift-LFSRs von Marsaglia) nicht bestanden werden, und fügen eine Verschlüsselungsausgabefunktion hinzu, um sie einzulösen. Wir sind beide bestrebt, diese Ausgabefunktion billig zu machen, und wir sind beide ein wenig aufgefangen worden, wo sich Mängel im zugrunde liegenden Generator, die wir mit unserer Ausgabefunktion zu maskieren versuchen, dennoch bemerkbar machen. In seinem Fall haben sich Linearitätsprobleme durchgesetzt .

In einer kürzlich erschienenen Arbeit, die mich sehr gefreut hat, hat er auch gezeigt, wie viele LFSR-basierte Designs Probleme mit dem Hamming-Gewicht haben (was ein langjähriges Anliegen von mir widerspiegelt), die durch ihre Ausgabefunktionen nicht ausreichend maskiert sind. Seine eigenen Generatoren bestehen seinen Test, also ist das etwas, aber im Jahr 2018, als ich mir Hamming -Gewicht des dieses Testprogramm hervorgehoben wird ?).

In Bezug auf seine Korrelationsprogramme schrieb ich 2018, als er auf seiner Website eine Kritik an PCG veröffentlichte, die Programme enthielt, die er mit verschiedenen Themen geschrieben hatte (wie erfundenes Seeding usw.), eine Antwort , die eine Reihe ähnlicher Programme enthielt für andere seit langem etablierte PRNGs, einschließlich corrsplitmix2.c die korrelierte Streams in SplitMix erstellen. Ich bin mir nicht ganz sicher, was Sebastian meint, wenn er sagt, dass dies nicht möglich ist, aber ich gebe zu, dass ich keine Gelegenheit hatte, sein Testprogramm genau zu betrachten, um festzustellen, ob sein neues Programm sich wesentlich von dem unterscheidet, das er hat schrieb vor ein paar Jahren.

Bitte verzeihen Sie mein Unverständnis - aber könnte ich zu diesem Zeitpunkt jemanden um eine zusammenfassende Schlussfolgerung bitten? Gibt es realistische Umstände, unter denen die Standard-PCG unsicher ist? Was sind die Argumente für das Ändern der Standardeinstellung?

Der einfache Weg besteht darin, einige Dokumentationen über hochdimensionale Anwendungen und die Wahrscheinlichkeit korrelierter Sequenzen hinzuzufügen.

Ein schwierigerer Weg wäre es, die Ausgabefunktion zu ersetzen, die den Stream unterbrechen würde. Ich bin mir nicht sicher, wie stark ein Versprechen default_rng ist. Die Dokumente scheinen nicht zu warnen, dass sich dies ändern könnte, sodass wahrscheinlich ein Verfallszyklus erforderlich wäre, um sich zu ändern. Dies würde das Hinzufügen der neuen Ausgabefunktion als eigenständiger Bitgenerator (oder konfigurierbar über PCG64, was sinnvoller wäre) und das Warnen der Benutzer erfordern, dass sie sich nach dem Jahr XXXX / Release Y.ZZ ändern wird.

Der schwierigste Weg wäre, ein neues Standard-Rng zu finden. Das erste Mal war es nicht einfach und ich glaube, in den letzten 18 Monaten hat sich nichts geändert, um die Nadel in eine bestimmte Richtung zu bewegen.

Ich habe gh-16493 über die default_rng() -Änderung geöffnet. Ich glaube nicht, dass dies mit diesem Problem zusammenhängt. Ich bin mir nicht einmal sicher, ob wir darüber diskutieren müssen. Wir hatten die Regeln wahrscheinlich schon vor langer Zeit festgelegt und ich habe es einfach nicht getan. Ich erinnere mich nicht.


Ich behaupte nicht, diese Diskussion vollständig zu verstehen, aber es scheint, dass zwei Dinge herauszufinden sind:

  1. Da ich sicher bin, dass wir genug Fortschritte haben, muss ich Robert vertrauen, und im Moment klingt es für mich so, als ob es nach bestem Wissen in Ordnung sein sollte. (Dh die Wahrscheinlichkeit einer tatsächlichen Kollision ist wahrscheinlich peinlich gering, selbst in Umgebungen, die größer sind als alles, in dem NumPy verwendet werden kann. Ob dies eine Änderung des Standards in der Zukunft rechtfertigt oder nicht, ist ein anderes Problem.)
  2. Wir erklären:

    und unterstützt [...] sowie: math: 2^{127} Streams

    Was, wenn wir nicht genau wissen, woher die Zahl kommt, so klingt, als wäre es eine leichte Übertreibung von unserer Seite, und wir könnten in Betracht ziehen, sie leicht anzupassen, um vollkommen korrekt zu sein? Oder auf eine externe Ressource verlinken, die zusätzliche Details enthält?

Im Moment ist es am einfachsten, eine PCG64DXSM BitGenerator hinzuzufügen, die Variante von PCG64 mit dem "billigen Multiplikator" und einer stärkeren DXSM Ausgabefunktion. Ich denke, alle sind sich einig, dass dies ein Fortschritt gegenüber der XSL-RR-Ausgabefunktion ist, die wir jetzt in unserer Implementierung von PCG64 und die statistisch besser abschneidet, ohne die Laufzeitleistung zu beeinträchtigen. Es ist ein einfaches Upgrade in der Nische, in der PCG64 für die BitGenerator uns angebotenen PCG64 hinzufügen.

Im Übrigen bevorzuge ich, dass es sich um einen separaten Namen BitGenerator Namen PCG64 . Solche Optionen eignen sich hervorragend für randomgen , dessen Zweck es ist, viele verschiedene Algorithmen und Varianten bereitzustellen, aber für numpy möchten wir, dass unsere Auswahl als "Grab-and-Go" erfolgt so viel wir können.

Ich glaube nicht, dass wir die Richtlinien für Änderungen an den Angeboten von default_rng() wirklich festgelegt haben. Als ich es vorschlug, kam ich auf die Idee, dass einer der Gründe, warum ich es vorgezogen habe, seine Funktionalität nur in den Konstruktor Generator() , darin bestand, dass wir bei Bedarf eine Funktion mit einem anderen Namen ablehnen und zu dieser wechseln konnten. Zu diesem Zeitpunkt überlegten wir jedoch, dass default_rng() möglicherweise viele Details des zugrunde liegenden BitGenerator , die wir anschließend vermieden haben. Da PCG64DXSM dieselbe API (insbesondere .jumped() ) wie PCG64 , besteht die einzige Überlegung darin, dass die Verwendung als neuer Standard den Bitstream ändern würde. Ich denke, es wäre vernünftig für uns, die gleiche Zeitachse zu verfolgen wie jede andere Änderung des Streams, die von Generator Methoden pro NEP 19 stammt (dh bei X.Y.0 Feature-Releases). Wir können uns dafür entscheiden, etwas vorsichtiger zu sein, wenn wir wollen, und zuerst PCG64DXSM als verfügbares BitGenerator in 1.20.0 und Dokument (aber nicht warn() verfügbar machen , zu laut, um keine Auswirkung zu haben), dass default_rng() in 1.21.0 geändert wird.

Als Teil des Hinzufügens des neuen Hintergrunds wäre es gut, die Notizen auf PCG64 zu aktualisieren, um als Leitfaden zu dienen und eine Begründung für die Bevorzugung der neueren Variante zu liefern.

  1. Da ich sicher bin, dass wir genug Fortschritte haben, muss ich Robert vertrauen, und im Moment klingt es für mich so, als ob es nach bestem Wissen in Ordnung sein sollte. (Dh die Wahrscheinlichkeit einer tatsächlichen Kollision ist wahrscheinlich peinlich gering, selbst in Umgebungen, die größer sind als alles, in dem NumPy verwendet werden kann. Ob dies eine Änderung des Standards in der Zukunft rechtfertigt oder nicht, ist ein anderes Problem.)

Das ist wahrscheinlich etwas zu unangenehm. Dies hängt davon ab, wie viele Streams, wie viele Daten Sie aus jedem Stream abrufen und wie hoch Ihre Risikotoleranz für eine Geburtstagskollision ist. Ich bin noch nicht dazu gekommen, diese Mathematik in einen verständlichen Absatz zu fassen, um leicht verständliche Empfehlungen abzugeben, weshalb ich dieses Github-Problem eine Weile nicht mehr wiederholt habe. Ich denke nicht, dass es sich um ein Problem handelt, das sofort behoben werden muss.

Ich werde später etwas länger schreiben, aber wie ich es in diesem Thread sehe, haben wir den Boden, den wir letztes Jahr durchgesehen haben, runderneuert. Nichts hat sich geändert, außer dass Sebastiano herausgefunden hat, dass NumPy PCG ausgeliefert hat. Die Analyse des NumPy-Teams im letzten Jahr war eingehender und berücksichtigte plausibelere Szenarien.

Ich würde es vorziehen, den Standard so schnell wie möglich zu aktualisieren - nur um Verwirrung zu vermeiden. Ich meine, nicht auf einen Korrekturzyklus warten.

@imneme -

Die wahrscheinlich beste Explosion aus dem letzten Beitrag ist diese. Ich denke, es ist auf jeden Fall eine Lektüre wert. Sie können von dort aus im Thread nach oben scrollen, um zu sehen, wie wir über diese Probleme sprechen. Es ging um PCG 32.

Ich hatte im Kopf, was ich sagen wollte, aber wenn ich mir die Beiträge von vor einem Jahr ansehe, habe ich alles bereits gesagt, sowohl hier als auch anderswo ( mein Blog (2017), reddit , mein Blog wieder (2018) und in NumPy-Diskussionen usw.)

Über Streams und ihre Selbstähnlichkeit (für die @rkern einen Stream-Abhängigkeitstester geschrieben hat ) schrieb ich letztes Jahr:

Wie in meinem Blog-Beitrag erwähnt, haben die Streams von PCG viel mit denen von SplitMix gemeinsam.

In Bezug auf das Diagramm von @mdickinson können wir für _jedes_ PRNG, mit dem Sie den gesamten Status festlegen können, einschließlich zählerbasierter kryptografischer, Seedings erstellen, bei denen PRNGs vorhanden wären, deren Ausgaben auf irgendeine Weise korreliert wurden (der einfachste Weg, dies zu tun) ist es, PRNG-Zustände zu erstellen, die nicht weit voneinander entfernt sind, aber oft können wir andere Dinge tun, basierend auf dem Verständnis, wie sie funktionieren. Und obwohl PRNGs, die kein Seeding in vollem Zustand zulassen, dieses Problem vermeiden können, führt dies lediglich ein neues ein und bietet nur praktischen Zugang zu einem winzigen Bruchteil ihrer möglichen Zustände.

Die richtige Art, an Streams zu denken, ist nur ein zufälliger Zustand, der gesetzt werden muss. Die Verwendung kleiner Werte wie 1,2,3 ist im Allgemeinen eine schlechte Idee für Seeding-Zwecke für _jedes_ PRNG (denn wenn jeder diese Samen bevorzugt, werden die entsprechenden Anfangssequenzen überrepräsentiert sein).

Wir können uns dafür entscheiden, es überhaupt nicht als Stream zu bezeichnen und es einfach als Status zu bezeichnen. Das hat Marsaglia in XorWow getan . Wenn Sie sich den Code ansehen, interagiert die Weyl-Sequenz counter überhaupt nicht mit dem Rest des Zustands und entspricht wie LCGs und Variationen des Anfangswertes nur einer zusätzlichen Konstante.

Die Streams von SplitMix, PCG und XorWow sind sogenannte „dumme“ Streams. Sie stellen eine triviale Neuparametrisierung des Generators dar. Dies hat jedoch einen Wert. Angenommen, ohne Streams hätte unser PRNG eine interessante enge Wiederholung von 42, wobei 42 mehrmals schnell hintereinander auftaucht und dies nur für 42 und keine andere Zahl tut. Mit dummen Streams "nur ein Inkrement" oder "nur ein xor" vermeiden wir tatsächlich, die seltsame Wiederholung auf 42 fest zu verdrahten. Alle Zahlen haben einen Stream, in dem sie sich seltsam wiederholen. (Aus diesem Grund besteht die Lösung, die ich zur Behebung der Probleme mit engen Wiederholungen in Xoshiro 256 anwenden würde, darin, eine Weyl-Sequenz zu mischen.)

(Ich habe dann in diesem und in diesem Kommentar näher darauf eingegangen .)

Ich würde sagen, dass der Schlüssel zum Mitnehmen darin besteht, dass Sie für fast jedes PRNG mit ein wenig Zeit und Energie pathologische Impfungen erfinden können. PCG befindet sich in einer etwas ungewöhnlichen Position, in der es jemanden gibt, der gerne plausibel aussehende Samen für PCG arbeitet, die speziell eine handgefertigte Pathologie haben (dh Sebastiano). Als Ergebnis dieser Arbeit habe ich mich umgedreht und das Gleiche sowohl für seine PRNGs als auch für andere langjährige getan.

Im Allgemeinen möchten Sie den PRNG-Status mit etwas initialisieren, das „irgendwie zufällig“ aussieht. Das ist ziemlich universell, auch wenn die Leute etwas anderes machen wollen . Für LFSR-PRNG (XorShift-Stil, Mersenne Twister usw.) dürfen Sie es beispielsweise nicht mit einem Null-Null-Status initialisieren, da es nur dort hängen bleibt. Aber selbst Zustände, die meistens Null sind, sind für LFSRs oft problematisch (ein Phänomen, das als Nullland bekannt ist, und warum die C ++ - Leute seed_seq ). Wenn Sie das Spiel "Lass uns etwas erfundenes Seeding machen" spielen möchten, ist es nicht schwer, eine Sammlung von Initialisierungen für 100 LFSRs zu erstellen und sie alle 1K vom Erreichen des Nulllandes entfernt zu haben. Die erfundenen Initialisierungen werden alle unschuldig genug aussehen, aber sie werden alle gleichzeitig diesen seltsamen Rückgang des Hamming-Gewichts treffen.

Wenn Sie einen PCG-Generator verwenden, der mit angemessener Entropie initialisiert wurde, ist dies in Ordnung. Wenn Sie es mit Junk wie 1, 2, 3 initialisieren möchten, ist das bei jedem PRNG tatsächlich

Aber DXSM ist sicherlich stärker. Ich denke, es ist besser, oder ich hätte es nicht geschafft, aber es lohnt sich, eine Perspektive zu haben und zu erkennen, dass Benutzer in der Praxis keine Probleme mit der klassischen PCG64-Implementierung haben werden.

Ich möchte die Kritik / Verteidigung der PCG64-Streams (über das LCG-Inkrement) von der vorliegenden Diskussion trennen. Während es aufgrund der Mathematik der LCG eine gewisse Dualität gibt, ist es nicht das Kernthema, das ursprünglich hier angesprochen wurde. Außerdem sind hier mehr Details zu berücksichtigen, als in Sebastianos ursprünglicher Kritik , Ihrer Antwort oder dem alten Mega-Thread vorhanden waren. Vielleicht ist der Zusammenhang für die Experten, die mehr Zeit mit Mathematik verbracht haben, offensichtlicher, aber die praktischen Konsequenzen sind mir jetzt zumindest klarer.

Ich würde sagen, dass der Schlüssel zum Mitnehmen darin besteht, dass Sie für fast jedes PRNG mit ein wenig Zeit und Energie pathologische Impfungen erfinden können.

Zugegeben, aber es ist nicht die binäre Dose / Dose, die die Entscheidung vor mir bestimmt. Wenn ich zu viele Zahlen aus einem endlichen PRNG ziehe, wird PractRand es schließlich herausfinden. Diese binäre Tatsache macht diesen PRNG-Algorithmus nicht ungültig. Sich von dieser Binärdatei zu entfernen und das Konzept der Kopffreiheit zu etablieren, war eines der Dinge, die ich am ursprünglichen PCG-Papier sehr schätzte. Angesichts einer kontrovers erzeugten Pathologie können wir uns ansehen, wie oft diese Pathologie zufällig aus einer guten Entropie-Aussaat entstehen kann. Ich möchte das quantifizieren und daraus praktische Ratschläge für Benutzer machen.

Bei zwei Zuständen, die sich die unteren 58 Bits und das gleiche Inkrement teilen (wir setzen einen Pin ein), zeigt das Verschachteln von PCG64 XSL-RR-Instanzen aus diesen Zuständen praktisch beobachtbare Fehler in PractRand bei etwa 32 GiB. Ich denke, es ist vernünftig, das vermeiden zu wollen. Nehmen wir das als unseren Maßstab und schauen wir uns an, wie oft dies bei guter Entropie-Aussaat auftritt. Glücklicherweise ist dieses kontroverse Schema einer probabilistischen Analyse zugänglich (nicht alle sind so freundlich). Für n Instanzen beträgt die Wahrscheinlichkeit, dass 2 dieselben unteren 58 Bits teilen, n**2 / 2**58 , geben oder nehmen Sie einen Faktor von 2 für die Doppelzählung. Bei einer halben Milliarde Instanzen stehen die Chancen gut, dass es eine solche Paarung gibt, die bei Verschachtelung bei PractRand fehlschlägt. Eine halbe Milliarde ist viel! Meines Erachtens werden wir wahrscheinlich nie ein numpy -Programm sehen, das versucht, so viele PCG64 Instanzen zu erstellen. numpy wäre dann wahrscheinlich das falsche Werkzeug.

Ich denke, es ist auch vernünftig, Anfangszustände vermeiden zu wollen, deren nachfolgende Ziehungen einen der Kollisionszustände der anderen Anfangszustände mit niedrigeren 58-Bit-Werten kreuzen. Ich versuche immer noch, die Logik darüber zu überdenken, aber ich denke, die Länge beeinflusst die Wahrscheinlichkeit eher linear als quadratisch. Wenn ich recht habe und 16 GiB aus jeder Instanz ziehen möchte ( 2**28 Draws), wie viel wir aus jedem der Paare gezogen haben, die PractRand-Fehler gezeigt haben, kann ich nur mit ungefähr 2**15 Instanzen oder ungefähr 32k, bevor es sehr wahrscheinlich wird, dass eine Kreuzung beobachtet wird. Das sind immer noch ziemlich viele Instanzen für Python! Und die Gesamtmenge der generierten Daten beträgt ungefähr ein halbes Petabyte, was sehr viel ist! Aber es ist am Horizont der Praktikabilität, und wenn ich die Wahrscheinlichkeit niedrig halten will, nicht nur unter der Hälfte, muss ich auf eine davon sinken. Ich bin nicht besonders besorgt über diese Zahlen; Ich glaube nicht, dass echte numpy -Programme bei der Verwendung von PCG64 mit der XSL-RR-Ausgabefunktion auf Probleme stoßen könnten. Einige Anwendungen nähern sich jedoch möglicherweise an (z. B. große verteilte Lernläufe zur Verstärkung ).

Nehmen wir diesen Inkrement-Pin heraus und sprechen ihn an. Ich denke, es ist fair zu sagen, dass mit der XSL-RR-Ausgabefunktion auch das Entropie-Seeding des Inkrements zusätzlich zum Status diese spezielle Analyse nicht ändert. Es scheint, dass es für jedes gegebene Paar von Entropie-Inkrementen die gleiche Anzahl von praktisch kollidierenden Zuständen gibt. Das konkrete Verfahren zum absichtlichen Konstruieren dieser Zustände sieht komplizierter aus als das Bashing in denselben unteren 58 Bits, aber es scheint, dass die Anzahl der kollidierenden Zustände gleich ist, sodass die Wahrscheinlichkeitsberechnungen gleich bleiben. Dies ist dem PCG-Schema im Allgemeinen nicht eigen. Die DXSM-Ausgabefunktion scheint stark genug zu sein, dass das Ändern des Inkrements (selbst mit einem einfachen +2 ) ausreicht, um selbst dem Worst-Case-Zustand für das zugrunde liegende LCG zu widerstehen (wenn die Distanzmetrik 0 ergibt

Abschließend möchte ich noch einmal wiederholen, worüber wir uns alle einig zu sein scheinen: PCG64DXSM ist eine gute Idee. Nicht zuletzt vereinfachen die verbesserten statistischen Eigenschaften die mentalen Modelle, die ich dokumentieren muss, und alles, was bedeutet, dass ich weniger Dokumentation schreiben muss, ist in meinem Buch gut.

Streams sind immer noch etwas relevant, da das Problem nur auftritt, wenn sich die Generatoren im selben Stream befinden.

Aber unter welchen Umständen hätten sie die gleichen niedrigeren 58 Bits und wären im gleichen Stream? Gibt es einen Anwendungsfall, in dem dies passieren würde?

Der eine etwas realistische Fall, den ich kenne, ist der, über den wir letztes Jahr gesprochen haben (als wir über jumped ), und über den ich in diesem Beitrag gesprochen habe, auf den ich zuvor verlinkt habe.

Streams sind immer noch etwas relevant, da das Problem nur auftritt, wenn sich die Generatoren im selben Stream befinden.

Dies ist bei XSL-RR leider nicht der Fall. Betrachten wir zwei PCG64 XSL-RR-Instanzen. Wir entropiesäen die Inkremente willkürlich und entropiesäen einen der Zustände. Wir können 2**70 fehlerhafte Zustände für die andere PCG64 -Instanz erstellen, bei denen PractRand auf die gleiche Weise fehlschlägt wie der Fehler mit dem gleichen niedrigeren Inkrement von 58 Bit. Dies ist nur komplizierter als im Fall des gleichen Inkrements. Anstatt die unteren 58-Bits als ersten Zustand mit dem unterschiedlichen Inkrement zu teilen, werden die unteren 58-Bits des Zustands mit dem Abstand 0 (gemäß Ihrem LCG-Abstandsmaß) von der ersten Instanz geteilt, wobei die Inkremente berücksichtigt werden. Ich habe einen konstruktiven Beweis (Python-Code), aber ich muss jetzt ins Bett gehen und ihn morgen aufräumen.

@ Kern , guter Punkt. Ich gebe zu, ich habe dieses Szenario nicht getestet, um zu sehen, wie es abschneidet.

Ich würde sagen, dass der Schlüssel zum Mitnehmen darin besteht, dass Sie für fast jedes PRNG mit ein wenig Zeit und Energie pathologische Impfungen erfinden können. PCG befindet sich in einer etwas ungewöhnlichen Position, in der es jemanden gibt, der gerne plausibel aussehende Samen für PCG arbeitet, die speziell eine handgefertigte Pathologie haben (dh Sebastiano). Als Ergebnis dieser Arbeit habe ich mich umgedreht und das Gleiche sowohl für seine PRNGs als auch für andere langjährige getan.

Wie ich bereits bemerkt habe, ist dies falsch. Ich kenne kein Beispiel für ein Paar korrelierter, nicht überlappender Sequenzen, beispielsweise von xoshiro256 ++, wie Sie es in PCG leicht finden können.

PRNGs, die schnell ihren gesamten Zustand mischen, haben dieses Problem nicht. Wenn Sie ein Programm bereitstellen können, das aus xoshiro256 ++ zwei nicht überlappende Sequenzen generiert, die korreliert sind, wie in den hier veröffentlichten Beispielen, tun Sie dies bitte.

In Bezug auf seine Korrelationsprogramme schrieb ich 2018, als er auf seiner Website eine Kritik an PCG veröffentlichte, die Programme enthielt, die er mit verschiedenen Themen geschrieben hatte (wie erfundenes Seeding usw.), eine Antwort , die eine Reihe ähnlicher Programme enthielt für andere seit langem etablierte PRNGs, einschließlich corrsplitmix2.c die korrelierte Streams in SplitMix erstellen. Ich bin mir nicht ganz sicher, was Sebastian meint, wenn er sagt, dass dies nicht möglich ist, aber ich gebe zu, dass ich keine Gelegenheit hatte, sein Testprogramm genau zu betrachten, um festzustellen, ob sein neues Programm sich wesentlich von dem unterscheidet, das er hat schrieb vor ein paar Jahren.

Das oben zitierte Programm wählt die Streams aus. Es ist offensichtlich einfach, es zu schreiben.

Das hat aber nichts mit den Problemen von PCG zu tun. Das von mir bereitgestellte Programm lässt _user_ die Streams auswählen und zeigt dann die Korrelation an.

Ich lade @inmeme erneut ein, ein Programm für SplitMix bereitzustellen, in dem der Benutzer zwei verschiedene Streams beliebig auswählen kann, einen beliebigen Anfangszustand des ersten Generators, und dann findet das Programm eine korrelierte Sequenz im anderen Generator, wie http: / /prng.di.unimi.it/corrpcgnumpy.c tut dies.

Wenn der Benutzer den Stream willkürlich auswählt, zeigt sich eine viel stärkere Form der Korrelation.

Wie ich bereits bemerkt habe, ist dies falsch. Ich kenne kein Beispiel für ein Paar korrelierter, nicht überlappender Sequenzen, beispielsweise von xoshiro256 ++, wie Sie es in PCG leicht finden können.

Es scheint, als würden wir hier aneinander vorbeigehen. Ich habe nicht gesagt, dass ich korrelierte, nicht überlappende Sequenzen für jedes PRNG entwickeln könnte, ich habe gesagt, dass ich generell pathologische Keimungen entwickeln könnte, wie die verschiedenen Korrelationsprogramme zeigen, die ich zuvor geschrieben hatte, und andere wie diese als das schlechte Wiederholungsdemonstrationsprogramm für Xoshiro ** .

Auch PRNGs, die nicht ihren gesamten Zustand mischen, haben eine lange Geschichte, einschließlich XorWow, der Generatoren in numerischen Rezepten usw. Sebastianos Argument repräsentiert den Standpunkt, aber sein Argument würde sagen, dass Marsaglia XorShift irgendwie in XorWow durch Hinzufügen eines Weyls _worse_ gemacht hat Sequenz, da es eine große Anzahl ähnlicher Generatoren erstellt.

Es scheint, als würden wir hier aneinander vorbeigehen. Ich habe nicht gesagt, dass ich korrelierte, nicht überlappende Sequenzen für jedes PRNG entwickeln könnte, ich habe gesagt, dass ich generell pathologische Keimungen entwickeln könnte, wie die verschiedenen Korrelationsprogramme zeigen, die ich zuvor geschrieben hatte, und andere wie diese als das schlechte Wiederholungsdemonstrationsprogramm für Xoshiro ** .

Bitte versuchen Sie, die Diskussion auf einem technischen Niveau zu halten. "Pathologisch" hat keine mathematische Bedeutung.

Der technisch korrekte Weg, um die Selbstkorrelation zu überprüfen, besteht darin, zwei Keime zu finden, die zwei nicht überlappende Sequenzen ergeben (nicht überlappend für die Dauer des Tests - sie überlappen sich unvermeidlich, wenn Sie weit genug gehen), sie zu verschachteln und an eine Batterie weiterzuleiten von Tests.

Wenn Sie zwei Sequenzen betrachten, die sich überlappen, werden sie für jeden Generator korreliert, auch für eine Krypto-Sequenz, einfach weil die gleichen Ausgaben zweimal nach der Überlappung der Sequenzen erfolgen und jeder vernünftige Test dies aufgreift.

"Pathologisches Seeding" unter Verwendung überlappender Sequenzen ist eine triviale Aufgabe für jeden Generator (was auch immer "pathologisch" bedeutet).

Da Sie behaupten, eine PCG-ähnliche Korrelation (bei der sich die Sequenzen nicht überlappen, wie der Test zeigt) in anderen Generatoren gefunden zu haben, können Sie erneut ein Paar korrelierter, nicht überlappender Sequenzen bereitstellen, z. B. von xoshiro256 ++ oder SFC64 ?

Der technisch korrekte Weg, um die Selbstkorrelation zu überprüfen, besteht darin, zwei Keime zu finden, die zwei nicht überlappende Sequenzen ergeben (nicht überlappend für die Dauer des Tests - sie überlappen sich unvermeidlich, wenn Sie weit genug gehen), sie zu verschachteln und an eine Batterie weiterzuleiten von Tests.

Können Sie für diese Definition der Korrelation auf die Literatur verweisen, damit ich sicherstellen kann, dass ich auch diesbezüglich „technisch korrekt“ bleibe?

Sebastiano, du willst immer wieder, dass ich eine Herausforderung beantworte, die zu deinen Bedingungen gestellt wird. Was Sie darauf hinweisen, bezieht sich auf die intrinsischen Eigenschaften von LCGs, bei denen Selbstähnlichkeit besteht. Sie werden nicht das gleiche Problem bei einem chaotischen PRNG oder einem LFSR-basierten finden.

Aber für andere PRNGs wird es andere Schwachstellen geben.

LFSRs haben Nullland, schlechte Zustände, Probleme mit dem Hamming-Gewicht, Linearität und, wie wir bei Ihren Versuchen mit Xoshiro erfahren haben, manchmal andere Verrücktheiten wie seltsame Probleme mit Wiederholungen.

Chaotische PRNGs haben das Risiko von kurzen Zyklen (obwohl solche mit einem Zähler dies vermeiden - Weyl-Sequenzen FTW!) Und ihrer intrinsischen Verzerrung.

Wenn sich die Sequenzen überschneiden, wie ich geschrieben habe, schlägt der Test immer fehl. Sie brauchen keine Literatur, um zu verstehen, dass ein Test, der immer fehlschlägt, kein Test ist.

Da Sie behaupten, eine PCG-ähnliche Korrelation (bei der sich die Sequenzen nicht überlappen, wie der Test zeigt) in anderen Generatoren gefunden zu haben, können Sie erneut ein Paar korrelierter, nicht überlappender Sequenzen bereitstellen, z. B. von xoshiro256 ++ oder SFC64 ?

Sie scheinen der Frage wirklich auszuweichen. Es wäre sehr einfach für Sie, nach Ihren Behauptungen solche Beweise zu liefern, wenn Sie welche hätten.

Im Moment ist es am einfachsten, eine PCG64DXSM BitGenerator hinzuzufügen, die Variante von PCG64 mit dem "billigen Multiplikator" und einer stärkeren DXSM Ausgabefunktion. Ich denke, alle sind sich einig, dass dies ein Fortschritt gegenüber der XSL-RR-Ausgabefunktion ist, die wir jetzt in unserer Implementierung von PCG64 und die statistisch besser abschneidet, ohne die Laufzeitleistung zu beeinträchtigen. Es ist ein einfaches Upgrade in der Nische, in der PCG64 für die BitGenerator uns angebotenen PCG64 hinzufügen.

Beachten Sie, dass 64-Bit- "billige Multiplikatoren" nachweisbare Mängel aufweisen. Dies ist seit langem bekannt:

W. Hörmann und G. Derflinger, Ein tragbarer Zufallszahlengenerator, der sich gut für die
Ablehnungsmethode, ACM Trans. Mathematik. Softw. 19 (1993), Nr. 4, 489–495.

Im Allgemeinen haben Multiplikatoren, die kleiner als die Quadratwurzel des Moduls sind, inhärente Grenzen für ihre spektrale Bewertung f₂.

Das Limit kann leicht überwunden werden, indem ein 65-Bit-Multiplikator verwendet wird, den der Compiler nur in einer zusätzlichen "Add" -Operation transformiert und wahrscheinlich nicht einmal die Drehzahl des Generators ändert.

Guy Steele und ich haben ein wenig an dem Problem gearbeitet und Tabellen mit Spektralwerten für billige Multiplikatoren verschiedener Größen veröffentlicht: https://arxiv.org/pdf/2001.05304.pdf . Je größer desto besser, aber es gibt eine nachweisbare Lücke von 64 bis 65 Bit (für ein LCG mit 128 Bit Status).

Zum Beispiel erhalten Sie aus Tabelle 7 des Papiers 0x1d605bbb58c8abbfd, das eine f₂-Punktzahl von 0,9919 hat. Kein 64-Bit-Multiplikator kann über 0,9306 hinausgehen (Satz 4.1 im Papier).

Nach dem Mix und allem könnte die Verbesserung des f₂-Scores aus statistischer Sicht völlig unbemerkt bleiben. Aber angesichts der großen Verbesserung, die Sie für die relevanteste Dimension mit nur einer zusätzlichen Additionsoperation erhalten, denke ich (nun, wir denken, oder wir hätten das Papier nicht geschrieben), ist es die Mühe wert.

Sebastiano, du willst immer wieder, dass ich eine Herausforderung beantworte, die zu deinen Bedingungen gestellt wird. Was Sie darauf hinweisen, bezieht sich auf die intrinsischen Eigenschaften von LCGs, bei denen Selbstähnlichkeit besteht. Sie werden nicht das gleiche Problem bei einem chaotischen PRNG oder einem LFSR-basierten finden.

Wow, es hat eine Weile gedauert, bis wir dort waren!

LFSRs haben Nullland, schlechte Zustände, Probleme mit dem Hamming-Gewicht, Linearität und, wie wir bei Ihren Versuchen mit Xoshiro erfahren haben, manchmal andere Verrücktheiten wie seltsame Probleme mit Wiederholungen.

Ich stimme vollkommen zu - deshalb müssen Sie sie verschlüsseln. Beachten Sie, dass LFSRs und F₂-Lineargeneratoren verschiedene Dinge sind. verwandt, aber anders.

"Seltsame Probleme mit Wiederholungen" ist wie üblich ein nicht technischer Begriff, den ich nicht kommentieren kann.

Chaotische PRNGs haben das Risiko von kurzen Zyklen (obwohl solche mit einem Zähler dies vermeiden - Weyl-Sequenzen FTW!) Und ihrer intrinsischen Verzerrung.

[Update: Ich habe die Zählerbeobachtung in Klammern verpasst und aktualisiere meinen Kommentar.]

Ja, SFC64 hat solche Probleme nicht (es wird ein Zähler verwendet), daher würde ich nicht auf die gesamte Kategorie verallgemeinern. Es gibt sorgfältig entworfene chaotische Generatoren mit nachweisbarer großer kürzester Zykluslänge.

"Seltsame Probleme mit Wiederholungen" ist wie üblich ein nicht technischer Begriff, den ich nicht kommentieren kann.

Es scheint seltsam, nicht kommentieren zu können, weil ich nicht den richtigen Jargon verwendet habe. Führen Sie dieses Programm aus und klären Sie mich dann auf, wie ich das Problem am besten im richtigen Jargon beschreiben und dann den Kommentar abgeben kann, der angemessen erscheint. Ich hätte mir vorgestellt, dass Sie und David Blackman das Thema besprochen hätten, als es zum ersten Mal ans Licht kam, weil ich mit ihm darüber korrespondiert habe, aber ich habe nie gesehen, dass Sie es kommentiert haben.

Die Diskussion von PRNGs, die nicht in numpy ist nicht zum Thema. Bitte benutzen Sie Ihre eigenen Foren, um diese Diskussion fortzusetzen. Vielen Dank.

@rkern - das scheint als Kriterium etwas streng zu sein. Wenn es einen Mangel in der Numpy-Implementierung gibt, der nicht von anderen Implementierungen geteilt wird, erscheint dies vernünftig zu diskutieren.

Ich kann bestätigen, dass der Austausch, auf den ich mich beziehe, mir nicht hilft, die Entscheidungen zu treffen, die in dieser Ausgabe vor uns liegen. Bis wir Fortschritte machen, muss das Gespräch konzentriert bleiben.

Ich denke, es ist hilfreich, den breiteren Kontext zu verstehen. Sebastiano hat ein bisschen was mit PCG zu tun und schimpft seit Jahren dagegen. Ich denke, heutzutage könnten einige Leute uns beide ansehen und mit den Augen rollen und sagen: "Ihr seid beide so schlecht wie die anderen", weil ich auch seine PRNGs kritisiert habe, aber eigentlich habe ich das erst getan, nachdem er das behauptet hat Ich habe versucht, etwas zu verbergen, indem ich nie über seine Sachen gesprochen habe (in Wirklichkeit hatte ich einfach nicht die Zeit / Neigung - tatsächlich hatte ich einfach angenommen, dass es ihnen gut geht).

Seine Kritiken sind zwar nützlich, und ich freue mich, dass er so viel Zeit seines Lebens damit verbracht hat, über meine Arbeit nachzudenken, aber es ist wichtig zu erkennen, dass seine Tests widersprüchlicher Natur sind. Er nutzt das Wissen über die Struktur von PCG, um Vorkehrungen zu treffen, die in RNG-Tester eingespeist werden können, und um Tests zu bestehen.

Angesichts dessen, wie beängstigend das aussehen kann, erscheint es vernünftig zu zeigen, dass ein ähnlicher kontroverser Ansatz auch zahlreiche andere Generatoren auslösen würde und dass viele der Bedenken, die er in Bezug auf PCG äußert, auch für andere Erzeugungsschemata gelten würden, wie ich in Bezug auf Erzeugungsschemata festgestellt habe wie XorWow, und ich verwende oft SplitMix als Beispiel, wie ich weiter unten tun werde. (Keiner von uns ist auf die eine oder andere Weise besonders in SplitMix investiert, würde ich mir vorstellen.)

Wir können zum Beispiel bei SplitMix sehr beängstigend sein und zeigen, dass mit der Standard-Stream-Konstante, wenn wir jede 35185-te Ausgabe betrachten, diese in einer PRNG-Testsuite fehlschlägt. Oh nein! Dies liegt daran , intern wird es einen Zähler (Weyl Reihenfolge!) Durch Erhöhen 0x9e3779b97f4a7c15 (basierend auf φ, das goldene Verhältnis), aber 35185 * 0x9e3779b97f4a7c15 = 0x86a100000c480245 , die nur haben 14 Bits gesetzt und ein großer Schwad von nichts in der Mitte. Oder wenn wir uns jede 360998717-te Ausgabe ansehen, werden wir gleichbedeutend mit einer Addition zum internen Status von 0x48620000800401 , bei der nur 8 Bits hinzugefügt werden und die Ausgabefunktion nur schwer vollständig maskiert werden kann .

Wir könnten weiterhin Angst vor SplitMix haben und sagen, schauen Sie, was wäre, wenn ich zwei Streams hätte, einen mit der additiven Konstante 0x9e3779b97f4a7c15 und einen mit 0xdaa66d2c7ddf743f , würden wir Fehler sehen, wenn wir dies einspeisen würden eine PRNG-Testsuite !!! Aber das liegt daran, dass der zweite nur dreimal so groß ist wie der andere.

Und schließlich, wenn jemand sagte "Ich werde Ihnen beide Streams geben, machen Sie etwas Unheimliches damit!" Und sagen wir, dass ihre Streams auf π ( 0x243f6a8885a308d3 ) und _e_ ( 0xb7e151628aed2a6b basieren) ), können wir sicher sagen, lassen Sie uns etwas mehr Angst machen und jedes 6561221343-te Element aus dem Pi-Stream nehmen und es mit jedem 6663276199-ten Element aus dem E-Stream mischen und Low-and-Belhold, sie produzieren zwei identische Sequenzen. Und _worse_, ich zeige dann weiter, dass es für jeden Sprung auf Stream a einen passenden Sprung auf Stream b gibt, um die gleiche Ausgabe zu erzielen, also gibt es tatsächlich 2 ^ 64 Arten, in denen sie korrelieren !!! (Und wir können dies für zwei beliebige Streams tun, an π und _e_ war nichts Besonderes.)

Zurück zu PCG: Sebastianos Test basiert darauf, dass die beiden PCG64 XSH RR-Generatoren genau ausgerichtet sind, sodass übereinstimmende Ausgänge verschachtelt sind. Wenn wir nur eines der PRNGs um einen kleinen Betrag vorrücken und die perfekte Ausrichtung nur ein bisschen unterbrechen, wird es erheblich schwieriger, verdächtige Dinge zu erkennen.

Ein ähnlicher kontroverser Test in die andere Richtung (der Sebastiano belastet) würde darin bestehen, Ausgaben von PCG64 XSH RR bereitzustellen, die seiner Behauptung entsprechen, dass sie korreliert sind, aber wir sagen ihm nicht genau, wie sie ausgerichtet sind (sie sind gerecht) in der richtigen allgemeinen Nachbarschaft). Seine Aufgabe wäre es, die Ausrichtung zu finden, um zu zeigen, dass sie korreliert sind.

Insgesamt denke ich nicht, dass es in der Praxis ein Problem ist, dringende Brände zu löschen, aber andererseits ist die DXSM-Version besser, als sie letztes Jahr geschrieben wurde, um genau diese Art von Problemen zu mildern, und das würde ich auch tun Ich freue mich, dass Sie dazu wechseln.

PS Mit diesem Code können Sie aus Ihrer reellen Lieblingszahl magische Weyl-Additivkonstanten erstellen:

WeylConst[r_,bits_] = BitOr[Floor[(r-Floor[r])*2^bits],1]

Das ist Mathematica, ich lasse die Python-Version als Übung.

So konstruiere ich die Kollisionen mit niedrigerem Bit für verschiedene Inkremente.

Ergebnisse mit PCG64 XSL-RR und einer Kollision mit weniger als 58 Bit

❯ ./pcg64_correlations.py -m 58 | stdbuf -oL ./RNG_test stdin64 -tf 2 -te 1 -tlmaxonly -multithreaded
s0 = 0b01110010100110011101000110010010101111111001100011001011001011111001001110101010011101111101001101011000011100001111111111100001
s1 = 0b10110001011001100111100010000110101110011010101010011011010100011001011111001100010001101001001011010010110101001011101111111100
dist = 0x2eb6ec432b0ea0f4fc00000000000000
[
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 152330663589051481538402839025803132897,
            "inc": 228410650821285501905570422998802152525
        },
        "has_uint32": 0,
        "uinteger": 0
    },
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 235805414096687854712168706130903874556,
            "inc": 70910205337619270663569052684874994465
        },
        "has_uint32": 0,
        "uinteger": 0
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0x12d551b8
test set = expanded, folding = extra

rng=RNG_stdin64, seed=0x12d551b8
length= 128 megabytes (2^27 bytes), time= 2.8 seconds
  no anomalies in 891 test result(s)

rng=RNG_stdin64, seed=0x12d551b8
length= 256 megabytes (2^28 bytes), time= 9.4 seconds
  no anomalies in 938 test result(s)

rng=RNG_stdin64, seed=0x12d551b8
length= 512 megabytes (2^29 bytes), time= 18.1 seconds
  no anomalies in 985 test result(s)

rng=RNG_stdin64, seed=0x12d551b8
length= 1 gigabyte (2^30 bytes), time= 31.2 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]FPF-14+6/16:cross         R=  +4.9  p =  1.7e-4   unusual          
  [Low4/16]FPF-14+6/16:all          R=  +8.4  p =  2.3e-7   very suspicious  
  [Low4/16]FPF-14+6/16:all2         R=  +8.3  p =  8.1e-5   unusual          
  [Low8/32]FPF-14+6/32:all          R=  +6.3  p =  2.1e-5   mildly suspicious
  [Low8/32]FPF-14+6/16:all          R=  +5.7  p =  8.0e-5   unusual          
  ...and 1034 test result(s) without anomalies

rng=RNG_stdin64, seed=0x12d551b8
length= 2 gigabytes (2^31 bytes), time= 52.7 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low4/16]FPF-14+6/32:all          R=  +7.4  p =  2.0e-6   suspicious       
  [Low4/16]FPF-14+6/16:(0,14-0)     R=  +7.7  p =  9.4e-7   unusual          
  [Low4/16]FPF-14+6/16:all          R=  +8.0  p =  5.9e-7   suspicious       
  [Low4/16]FPF-14+6/16:all2         R= +12.2  p =  2.1e-6   mildly suspicious
  [Low4/16]FPF-14+6/4:(0,14-0)      R=  +7.9  p =  6.3e-7   mildly suspicious
  [Low4/16]FPF-14+6/4:all           R=  +5.8  p =  6.7e-5   unusual          
  [Low4/16]FPF-14+6/4:all2          R= +11.5  p =  3.1e-6   mildly suspicious
  [Low8/32]FPF-14+6/32:(0,14-0)     R=  +7.8  p =  8.4e-7   unusual          
  [Low8/32]FPF-14+6/32:all          R=  +7.3  p =  2.3e-6   suspicious       
  [Low8/32]FPF-14+6/32:all2         R= +14.3  p =  3.8e-7   suspicious       
  [Low8/32]FPF-14+6/16:(0,14-0)     R=  +7.7  p =  8.8e-7   unusual          
  [Low8/32]FPF-14+6/16:(1,14-0)     R=  +7.7  p =  9.3e-7   unusual          
  [Low8/32]FPF-14+6/16:all          R=  +6.9  p =  5.3e-6   mildly suspicious
  [Low8/32]FPF-14+6/16:all2         R= +18.3  p =  8.0e-9   very suspicious  
  ...and 1078 test result(s) without anomalies

rng=RNG_stdin64, seed=0x12d551b8
length= 4 gigabytes (2^32 bytes), time= 90.2 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]BCFN_FF(2+0):freq         R= +14.8  p~=   6e-18     FAIL !         
  [Low1/8]BCFN_FF(2+1):freq         R=  +7.4  p~=   1e-6    mildly suspicious
  [Low1/8]FPF-14+6/16:cross         R=  +8.4  p =  2.1e-7   very suspicious  
  [Low4/16]FPF-14+6/32:(0,14-0)     R=  +8.9  p =  8.1e-8   mildly suspicious
  [Low4/16]FPF-14+6/32:(1,14-0)     R=  +8.5  p =  1.9e-7   mildly suspicious
  [Low4/16]FPF-14+6/32:all          R=  +9.4  p =  2.4e-8   very suspicious  
  [Low4/16]FPF-14+6/32:all2         R= +23.9  p =  5.2e-11   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/16:(0,14-0)     R= +13.8  p =  2.2e-12   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/16:(1,14-0)     R= +10.0  p =  7.3e-9   suspicious       
  [Low4/16]FPF-14+6/16:all          R= +12.1  p =  8.0e-11   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/16:all2         R= +52.5  p =  1.3e-22    FAIL !!        
  [Low4/16]FPF-14+6/4:(0,14-0)      R= +12.2  p =  7.0e-11   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/4:all           R=  +7.1  p =  3.7e-6   mildly suspicious
  [Low4/16]FPF-14+6/4:all2          R= +29.8  p =  7.1e-14    FAIL           
  [Low4/16]FPF-14+6/4:cross         R=  +5.3  p =  7.8e-5   unusual          
  [Low4/32]FPF-14+6/32:(0,14-0)     R=  +7.6  p =  1.3e-6   unusual          
  [Low4/32]FPF-14+6/32:all          R=  +6.0  p =  4.4e-5   unusual          
  [Low4/32]FPF-14+6/32:all2         R=  +9.4  p =  2.9e-5   unusual          
  [Low4/32]FPF-14+6/16:(0,14-0)     R=  +7.3  p =  2.5e-6   unusual          
  [Low4/32]FPF-14+6/16:all          R=  +6.5  p =  1.4e-5   mildly suspicious
  [Low4/32]FPF-14+6/16:all2         R=  +8.2  p =  8.0e-5   unusual          
  [Low8/32]FPF-14+6/32:(0,14-0)     R= +17.2  p =  1.7e-15    FAIL           
  [Low8/32]FPF-14+6/32:(1,14-0)     R= +12.7  p =  2.3e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/32:all          R= +15.3  p =  7.9e-14    FAIL           
  [Low8/32]FPF-14+6/32:all2         R= +86.1  p =  1.2e-35    FAIL !!!       
  [Low8/32]FPF-14+6/16:(0,14-0)     R= +16.8  p =  3.5e-15    FAIL           
  [Low8/32]FPF-14+6/16:(1,14-0)     R= +12.2  p =  6.6e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/16:all          R= +13.1  p =  8.9e-12   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/16:all2         R= +82.1  p =  1.7e-34    FAIL !!!       
  [Low8/32]FPF-14+6/4:(0,14-0)      R= +12.8  p =  2.0e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/4:(1,14-0)      R=  +9.4  p =  2.5e-8   suspicious       
  [Low8/32]FPF-14+6/4:all           R= +10.5  p =  2.2e-9    VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/4:all2          R= +42.0  p =  5.8e-19    FAIL !         
  ...and 1118 test result(s) without anomalies

Ergebnisse mit PCG64 DXSM und einer Kollision mit weniger als 64 Bit (um Probleme schneller zu provozieren, obwohl ich keine sehe)

❯ ./pcg64_correlations.py -m 64 --dxsm | stdbuf -oL ./RNG_test stdin64 -tf 2 -te 1 -tlmaxonly -multithreaded
s0 = 0b10001000010110111101010101010101111100100011011111011111011111001011110101111100101101101100110101110001101101111111010101111111
s1 = 0b11000101110100011001011000001110100001001111001001100101010000101100011001010111011001100000010010011100101110001110101000011100
dist = 0x3a26b19c91e6da1d0000000000000000
[
    {
        "bit_generator": "PCG64DXSM",
        "state": {
            "state": 181251833403477538233003277050491434367,
            "inc": 46073632738916603716779705377640239269
        },
        "has_uint32": 0,
        "uinteger": 0
    },
    {
        "bit_generator": "PCG64DXSM",
        "state": {
            "state": 262946148724842088422233355148768897564,
            "inc": 125105549038853892415237434774494719583
        },
        "has_uint32": 0,
        "uinteger": 0
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0x85cea9
test set = expanded, folding = extra

rng=RNG_stdin64, seed=0x85cea9
length= 128 megabytes (2^27 bytes), time= 2.6 seconds
  no anomalies in 891 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 256 megabytes (2^28 bytes), time= 9.4 seconds
  no anomalies in 938 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 512 megabytes (2^29 bytes), time= 18.5 seconds
  no anomalies in 985 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 1 gigabyte (2^30 bytes), time= 32.3 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low4/32]BCFN_FF(2+3,13-3,T)      R=  -8.3  p =1-9.5e-5   unusual          
  ...and 1035 test result(s) without anomalies

rng=RNG_stdin64, seed=0x85cea9
length= 2 gigabytes (2^31 bytes), time= 55.8 seconds
  no anomalies in 1092 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 4 gigabytes (2^32 bytes), time= 93.1 seconds
  no anomalies in 1154 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 8 gigabytes (2^33 bytes), time= 175 seconds
  no anomalies in 1222 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 16 gigabytes (2^34 bytes), time= 326 seconds
  no anomalies in 1302 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 32 gigabytes (2^35 bytes), time= 594 seconds
  no anomalies in 1359 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 64 gigabytes (2^36 bytes), time= 1194 seconds
  no anomalies in 1434 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 128 gigabytes (2^37 bytes), time= 2334 seconds
  no anomalies in 1506 test result(s)
...

@rkern , danke, dass du deinen Code geteilt hast. Es wäre lehrreich, eine Option hinzuzufügen, um einen Versatz hinzuzufügen, um verschachtelte Ausgaben in den Tester einzuspeisen, der nicht perfekt ausgerichtet ist. Das habe ich ein wenig erforscht.

Ja, ich habe das informell gemacht, indem ich bg0.advance(N) für verschiedene N vor dem endgültigen return . Ich habe die untere 64-Bit-Kollision verwendet, um sicherzugehen, dass ich etwas sehe. Winzige Verschiebungen wie 16 ändern den Fehler nicht wesentlich, aber selbst bescheidene Verschiebungen wie 128 verlängern den Fehler auf 32 GiB.

Es sieht so aus, als sollten wir PCG64 DXSM als optionalen Bitgenerator hinzufügen und es schließlich zum Standard machen. Um haben wir eine Implementierung?

Ich denke, es ist hilfreich, den breiteren Kontext zu verstehen. Sebastiano hat ein bisschen was mit PCG zu tun und schimpft seit Jahren dagegen. Ich denke, heutzutage könnten einige Leute uns beide ansehen und mit den Augen rollen und sagen: "Ihr seid beide so schlecht wie die anderen", weil ich auch seine PRNGs kritisiert habe, aber eigentlich habe ich das erst getan, nachdem er das behauptet hat Ich habe versucht, etwas zu verbergen, indem ich nie über seine Sachen gesprochen habe (in Wirklichkeit hatte ich einfach nicht die Zeit / Neigung - tatsächlich hatte ich einfach angenommen, dass es ihnen gut geht).

Ich denke, diese Überlegungen sind völlig unangemessen.

Wenn Sie darauf bestehen, die Arbeit anderer Leute (z. B. SplitMix) ohne Verdienst und ohne Beweise anzugreifen, wird das PCG-Chaos oder der Generator von Numpy nicht besser. Stattdessen könnte ein besserer Multiplikator oder ein besser gestalteter Scrambler helfen.

Ich warte immer noch auf einen Test, der die Korrelation in SplitMix zeigt, wenn der Benutzer die Streams auswählen kann. Um ganz klar zu sein, für Numpys Generator habe ich eine Aussage über die Form bewiesen

∀c ∀d ∀x ∃y korreliert

wobei c, d Inkremente ("Ströme") sind und x, y Anfangszustände sind. Tatsächlich gibt es 2 ^ 72 Jahre. Das heißt, egal wie Sie c, d und x wählen, es gibt 2 ^ 72 ys, die Korrelation zeigen.

Der angeblich entsprechende Code, den Sie für SplitMix angegeben haben, zeigt dies

∃c ∃d ∃x ∃y korreliert

Das heißt, wenn Sie kontrovers c, d, x und y wählen, können Sie eine Korrelation zeigen.

Der Unterschied in der Stärke in den beiden Aussagen ist erstaunlich. Der Versuch, die beiden Aussagen zusammenzuführen, ist falsch.

@vigna Sie wurden jetzt zweimal vor unserem Verhaltenskodex gewarnt, von @mattip und @rkern. Die Verwendung von Sprache wie "die Arbeit anderer Leute verprügeln" und "Der Versuch, die beiden Aussagen miteinander zu verbinden, ist reine FUD" ist nicht in Ordnung. Betrachten Sie dies als Ihre letzte Warnung. Bitte ändern Sie Ihren Ton oder wir werden Sie verbieten. Technische Argumente sind weiterhin willkommen, alles andere ist derzeit nicht möglich.

Ich habe die Nachricht geändert und diese Ausdrücke durch neutrale ersetzt. Ich denke immer noch, dass es völlig unangemessen ist, einen anderen Teilnehmer der Diskussion persönlich anzugreifen ("Sebastiano hat ein bisschen was mit PCG zu tun und schimpft seit Jahren dagegen"). Ich bin sehr überrascht, ist nichts für dich.

Zum dritten und letzten Mal hilft mir die Diskussion über SplitMix in beide Richtungen nicht im geringsten. Ich kann verstehen, warum Sie denken, dass es den erforderlichen Kontext bietet oder dass Sie sich gezwungen fühlen, auf den anderen zu antworten, aber bitte vertrauen Sie darauf, dass ich Ihnen die Wahrheit sage, dass es mir keine Informationen liefert, die mir helfen, hier eine Entscheidung zu treffen. Sie haben beide Ihre eigenen Websites. Benutze sie.

Ich habe die Nachricht geändert und diese Ausdrücke durch neutrale ersetzt.

Vielen Dank.

Ich denke immer noch, dass es völlig unangemessen ist, einen anderen Teilnehmer der Diskussion persönlich anzugreifen ("Sebastiano hat ein bisschen was mit PCG zu tun und schimpft seit Jahren dagegen"). Ich bin sehr überrascht, ist nichts für dich.

Das würde ich auch lieber nicht sehen. Der Ton dieser Nachricht ist jedoch bei weitem nicht so schlecht.

Ich würde es sehr begrüßen, wenn Sie sich beide an konstruktive Tatsachenaussagen halten könnten.

IN ORDNUNG. Fangen wir bei Null an: Sie möchten einen Generator mit einer Art von LCG-basierten Streams mit einem Power-of-2-Modul für Komfort und Geschwindigkeit. Die Literatur schlägt vor, dass das Basieren von Streams auf LCG-Additivkonstanten zu Problemen führen kann (wie es jetzt passiert), aber nehmen wir an, dass dies das ist, was Sie wollen.

Nehmen Sie ein LCG mit 128-Bit-Status und einem guten Multiplikator (mindestens 65 Bit) und stören Sie die oberen Bits mithilfe der Mix-Funktion von SplitMix, die in verschiedenen Anwendungen (Hashing, PRNG usw.) intensiv getestet wurde. hervorragende Ergebnisse erzielen?

Ich bin mir ziemlich sicher, dass der Geschwindigkeitsunterschied gering sein wird. Und Sie haben eine (statistische) Garantie dafür, dass das Ergebnis von allen Bits abhängt, worum es hier geht.

Dies scheint mir eher ein "auf der Schulter der Riesen stehen" -Ansatz zu sein, als Mischfunktionen an einem Generator mit Selbstkorrelationsproblemen von Hand herzustellen.

@imneme Was ich verwenden könnte, ist ein Blog-Beitrag über DXSM, auf den man leichter verlinken kann als auf diesen Ankündigungskommentar in der alten Mega-Ausgabe. Es muss nicht viel mehr sein als das, was in diesem Kommentar steht, aber es wäre gut, den aktuellen Status der Tests anzugeben, die Sie hier erwähnt haben. Wenn Sie einen Teil der Diskussion aus dem Mega-Thema zusammenfassen möchten, das zu dieser Entwicklung geführt hat, wäre dies zwar nützlich, aber nicht unbedingt erforderlich.

@ Vigna

Nehmen Sie ein LCG mit 128-Bit-Status und einem guten Multiplikator (mindestens 65 Bit) und stören Sie die oberen Bits mithilfe der Mix-Funktion von SplitMix, die in verschiedenen Anwendungen (Hashing, PRNG usw.) intensiv getestet wurde. hervorragende Ergebnisse erzielen?

Ich entschuldige mich, wenn dies snarky klingt (obwohl es sicherlich darauf hingewiesen wird), aber es ist auch aufrichtig: Ich freue mich darauf, die Implementierung, Analyse, Benchmarks und PractRand-Ergebnisse auf Ihrer Website oder auf arXiv zu sehen. Wir sind hier (einigermaßen informierte) Praktiker, keine PRNG-Forscher, und nicht besonders gut gerüstet, um diesen Vorschlag umzusetzen. Ich kann den Sinn davon erkennen, aber angesichts der anderen Einschränkungen meiner persönlichen Zeit neige ich nicht dazu, mir die Mühe zu machen, dies vom Vorschlag zur Implementierung und Analyse zu bringen. Wenn Sie diesen Vorschlag an Numpy richten, brauchen wir PRNG-Forscher, um diese Arbeit zu erledigen. Wenn Sie diesen Vorschlag wirklich an eine andere Person richten, verwenden Sie Ihre Website.

Die zufällige Generator -> BitGenerator -> SeedSequence Architektur in NumPy soll steckbar sein. Ich denke, wir sind an dem Punkt in der Diskussion angelangt, an dem wir jemanden brauchen, der eine PR für einen BitGenerator öffnet, damit wir seine praktischen Eigenschaften mit denen vergleichen können, die derzeit in NumPy enthalten sind. Sobald es Teil des Projekts wird, können wir es weiter testen und entscheiden, es als Standard festzulegen. Ich hoffe, diese Entscheidung würde darauf beruhen

  • mangelnde Voreingenommenheit (und andere Kriterien? Ich übergebe den Experten)
  • Performance
  • Wahrscheinlichkeit verschiedener Arten von Stream-Kollisionen über die von uns beworbenen normativen Schnittstellen: Verwenden von BitGenerator.spawn und SeedSequence .

Persönlich hat mich diese Diskussion verloren, als es vermieden wurde, die Vorzüge von BitGenerators über Code zu diskutieren, der die Schnittstelle spawn verwendet. Es gibt einen Grund, warum wir es als Best Practice bewerben, und ich hoffe, dass sich die Diskussion über die zukünftige PR auf Best Practices für NumPy-Benutzer konzentrieren wird .

Vielleicht könnte eine der Schlussfolgerungen hier sein, dass wir nur spawn als Methode zulassen sollten, da die Verwendung von jumped oder advance möglicherweise gegen bewährte Praktiken verstößt. Eine neue Ausgabe oder NEP, die sich darauf konzentriert, könnte ebenfalls produktiv sein.

@mattip Die von @vigna festgestellte Geburtstagskollision mit den unteren Bits wirkt sich auch auf unsere SeedSequence.spawn() -Schnittstelle aus. Bitte seien Sie versichert, dass jeder Teil der Diskussion, an der ich teilgenommen habe, für die ordnungsgemäße Verwendung unserer APIs relevant ist.

Es ist nur erforderlich, pcg64.c mit einigen # ifdef-Blöcken etwa 8 Zeilen hinzuzufügen, um den bevorzugten Ansatz von @rkern für vollständig separate Generatoren zu verwenden. Das pyx / pxd wäre ansonsten identisch mit der PCG64-Klasse, die nur mit den richtigen Definitionen (PCG_DXSM = 1) und einer aktualisierten Dokumentzeichenfolge erstellt wird.

Ich würde wahrscheinlich expliziter darauf eingehen, insbesondere für die emulierte 128-Bit-Mathematik für die Plattformen, die sie benötigen.

https://github.com/rkern/numpy/compare/v1.17.4...rkern%3Awip/pcg64-dxsm

Es schien mir einfacher zu sein, da es einen "billigen" 64-Bit-Multiplikator verwendet. Sie können einfach einen neuen Ausgangsmischer (der unveränderlich ist) und dann ifdef um die letzte Zeile des Zufallsgenerators hinzufügen, der die Ausgabe des LCG nimmt und dann den Mischer anwendet.

https://github.com/bashtage/randomgen/commit/63e50a63f386b5fd725025f2199ff89454321f4c#diff -879bd64ee1e2b88fec97b5315cf77be1R115

Wenn man wollte, konnte man an dieser Stelle sogar Murmur Hash 3 hinzufügen, war man so geneigt.

Werden die if -Anweisungen weg kompiliert? Ich glaube nicht, dass wir ein Vielfaches davon in heißen Schleifen haben wollen.

Dies ist wiederum auf den Unterschied im Zweck zwischen randomgen und numpy . In randomgen ist es sinnvoll, parametrisierte Familien zu erstellen, aber in numpy halte ich es nicht für eine gute Idee, die Implementierungen eines Legacy- BitGenerator aus dem aktiven Standard zu verschränken BitGenerator . Wenn wir Wartungs- oder Umgestaltungsarbeiten durchführen müssen, um die Leistung auf der einen oder anderen Seite zu verbessern, wird dies die Anstrengungen eher verschlimmern als verbessern.

Stimmen Sie hier mit Robert überein. Ich habe keine Bedenken, einen neuen Bitgenerator in die Version 1.19.0 aufzunehmen. Er würde kein aktuelles Verhalten ändern.

@bashtage Beachten Sie auch, dass pcg_cm_random_r() nutzt den Pre-iterativen Zustand zur Ausgabe anstatt der Post-iterativen Zustand , so dass es sein würde nicht so einfach , den gleichen Codepfad mit halten #ifdef oder if Schalter.

Werden die if -Anweisungen weg kompiliert? Ich glaube nicht, dass wir ein Vielfaches davon in heißen Schleifen haben wollen.

Nein, in NumPy sollte das if else so etwas wie werden

#if defined(PCG_DXSM)
    pcg_output_dxsm(state.high, state.low)
#else 
   <old way>
#endif

Diese müssen in der uint128-Version und in der Fallback-Version separat definiert werden, damit das uint128 manuell auf hoch und niedrig geschaltet werden kann.

@bashtage Beachten Sie auch, dass pcg_cm_random_r() nutzt den Pre-iterativen Zustand zur Ausgabe anstatt der Post-iterativen Zustand , so dass es sein würde nicht so einfach , den gleichen Codepfad mit halten #ifdef oder if Schalter.

Hmm, ich habe gegen die @ imneme- Referenzimplementierung getestet und eine 100% ige Übereinstimmung mit 1000 Werten unter Verwendung von 2 verschiedenen Seeds erhalten:

https://github.com/bashtage/randomgen/blob/master/randomgen/src/pcg64/pcg_dxsm-test-data-gen.cpp

AFAICT (und ich kann mich irren)

https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp#L174

und

https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp#L1045

bedeutet, dass der Pfad uint_128 immer einen billigen Multiplikator verwendet.

Ich bin mir nicht sicher, was Sie dort sagen wollen.

Es ist unklar, was der kanonische PCG64 DXSM ist. In beiden Fällen verwendet die Ausgabefunktion nur 64-Bit-Operationen. Die Version, die Sie haben, verwendet einen 64-Bit-Multiplikator an einem anderen Ort, um noch schneller zu sein, und gibt vor und nicht nach zurück. setseq_dxsm_128_64 scheint die natürliche Erweiterung des vorhandenen PCG64 zu sein und ändert nur die Ausgabefunktion.

Oh, ich verstehe. Nein, Sie haben einen anderen C ++ - Generator verwendet als den, den ich in C implementiert habe. Ich habe das Äquivalent von cm_setseq_dxsm_128_64 implementiert, das den "billigen Multiplikator" in der LCG-Iteration verwendet, nicht setseq_dxsm_128_64 der immer noch verwendet wird verwendet den großen Multiplikator in der LCG-Iteration. Der "billige Multiplikator" wird innerhalb der DXSM-Ausgabefunktion wiederverwendet, aber das ist eine orthogonale Entwurfsachse.

Warum nicht lieber setseq_dxsm_128_64?

@imneme sagte, dass sie irgendwann das offizielle pcg64 in der C ++ - Version ändern würde , um auf cm_setseq_dxsm_128_64 , nicht auf setseq_dxsm_128_64 . Der billige Multiplikator gleicht einen Teil der zusätzlichen Kosten von DXSM im Vergleich zu XSL-RR aus. Und ich denke, es ist die Variante, die sie ein paar Monate lang getestet hat.

Die Ausgabe des voriterierten Zustands ist ebenfalls Teil der Leistungssteigerung .

Hier sind einige Timings:

In [4]: %timeit p.random_raw(1000000)
3.24 ms ± 4.61 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [5]: p = rg.PCG64(mode="sequence",use_dxsm=False)

In [6]: %timeit p.random_raw(1000000)
3.04 ms ± 8.47 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [7]: import numpy as np

In [8]: p = np.random.PCG64()

In [9]: %timeit p.random_raw(1000000)
3.03 ms ± 2.54 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Alles unter Ubuntu-20.04, Standard-Compiler.

6% langsamer. Scheint mir ein kleiner Unterschied zu sein. Alle Timings in NumPy / randomgen sind ziemlich weit von dem entfernt, was Sie in einer engen Schleife von nativem Code erhalten können.

Vergleichen Sie

In [10]: x = rg.Xoroshiro128(mode="sequence")

In [11]: %timeit x.random_raw(1000000)
2.59 ms ± 35.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

zu dem, was aus C-Code veröffentlicht wurde (150% langsamer ??).

Zugegeben, im Kontext von numpy spielt es keine Rolle. Im C ++ - Kontext spielt dies eine größere Rolle, was die Zuweisung von Clustermonaten zum Testen und die Nominierung als zukünftiger Standard pcg64 im offiziellen C ++ - Code vorantreibt. Dies sind die unmittelbaren Motivationen für numpy , IMO.

Der Unterschied zwischen Lager PCG64 und meinem PCG64DXSM in meinem Zweig ("billiger Multiplikator", DXSM-Ausgabefunktion, Ausgabe des voriterierten Zustands, separater Codepfad):

[practrand]
|1> s = np.random.SeedSequence()

[practrand]
|2> pcg64 = np.random.PCG64(s)

[practrand]
|3> pcg64dxsm = np.random.PCG64DXSM(s)

[practrand]
|4> %timeit pcg64.random_raw(1000000)
100 loops, best of 3: 3.46 ms per loop

[practrand]
|5> %timeit pcg64dxsm.random_raw(1000000)
100 loops, best of 3: 2.9 ms per loop

Ich sage immer noch, dass es nur wenige (mehr als ich) #ifdefs zwischen den beiden gibt, selbst mit einer speziellen Implementierung für MSVC. Plus RSN MS-Benutzer können clang # 13816 👍 verwenden.

Streiten wir über Codeduplizierung? Ich hätte lieber disjunkte Implementierungen als mir Sorgen um ein paar Codezeilen zu machen, die mit #ifdefs verschleiert sind :)

Es war größtenteils nur ein Scherz, obwohl es die Notwendigkeit einer absolut klaren Aussage zur Definition von "PCG 2.0" hervorhob (vorzugsweise irgendwo, wo es sich nicht um NumPys GitHub-Probleme handelt).

Vielen Dank, @rkern et al.

@imneme Was ich verwenden könnte, ist ein Blog-Beitrag über DXSM, auf den man leichter verlinken kann als auf diesen Ankündigungskommentar in der alten Mega-Ausgabe. Es muss nicht viel mehr sein als das, was in diesem Kommentar steht, aber es wäre gut, den aktuellen Status der Tests anzugeben, die Sie hier erwähnt haben. Wenn Sie einen Teil der Diskussion aus dem Mega-Thema zusammenfassen möchten, das zu dieser Entwicklung geführt hat, wäre dies zwar nützlich, aber nicht unbedingt erforderlich.

Haben Sie einen Zeitrahmen im Sinn? Ich habe vor, dies für einige Zeit zu tun und es durch andere Dinge herausdrücken zu lassen, also wäre es ein nützlicher Motivator für mich, tatsächlich eine vorgeschlagene Frist zu haben. Eine Woche vielleicht? Zwei?

@rkern zitierte auch @vigna , der schrieb:

Nehmen Sie ein LCG mit 128-Bit-Status und einem guten Multiplikator (mindestens 65 Bit) und stören Sie die oberen Bits mithilfe der Mix-Funktion von SplitMix, die in verschiedenen Anwendungen (Hashing, PRNG usw.) intensiv getestet wurde. hervorragende Ergebnisse erzielen?

FWIW, dieser Ansatz wurde im ursprünglichen PCG-Papier diskutiert, wobei _FastHash_ als Standard-Hash-Funktion verwendet wurde, die eine sehr ähnliche Multiplikations-Xorshift-Hash-Funktion ist. In meinen Tests war es nicht so schnell wie andere Permutationen, aber von hoher Qualität. Sebastiano erwähnte diese Idee auch in seiner PCG-Kritik von 2018 und ich diskutiere sie in diesem Abschnitt meiner Antwort auf diese Kritik.

In der Originalversion seiner PCG-Kritik schreibt er abschließend seine eigene PCG-Variante, die ich unten zitieren werde:

        #include <stdint.h>

        __uint128_t x;

        uint64_t inline next(void) {
            // Put in z the top bits of state
            uint64_t z = x >> 64;
            // Update state
            x = x * ((__uint128_t)0x2360ed051fc65da4 << 64 ^ 0x4385df649fccf645)
                  + ((__uint128_t)0x5851f42d4c957f2d << 64 ^ 0x14057b7ef767814f);
            // Compute mix
            z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
            z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
            return z ^ (z >> 31);
        }

Seitdem hat er den Code in seiner Kritik auf eine noch schnellere Version aktualisiert, die einen billigeren Multiplikator verwendet und die additive Konstante auf nur 64 Bit reduziert.

        #include <stdint.h>

        __uint128_t x;

        uint64_t inline next(void) {
            // Put in z the top bits of state
            uint64_t z = x >> 64;
            // Update state (multiplier from https://arxiv.org/abs/2001.05304)
            x = x * ((__uint128_t)1 << 64 ^ 0xd605bbb58c8abbfd) + 0x14057b7ef767814f;
            // Compute mix
            z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
            z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
            return z ^ (z >> 31);
        }

Mein Problem bei diesen beiden Varianten ist, dass die Permutation invertierbar ist, weil der abgeschnittene Zustand (die obere Hälfte) permutiert / verschlüsselt ist. Sie können ihn rückwärts ausführen und das Verwürfeln entschlüsseln, sodass Sie nur ein abgeschnittenes LCG mit allen darin enthaltenen Fehlern erhalten. Ich bevorzuge es, den gesamten Status zu permutieren / zu verschlüsseln und dann eine Kürzung davon auszugeben. (Natürlich wird das Permutieren / Verwürfeln von weniger Bits der schnellere Weg sein - wie üblich gibt es Kompromisse. Vernünftige Leute können einem nicht zustimmen, was wichtig ist.)

Aber seine Arbeit an seiner eigenen PCG-Variante lieferte sehr nützliche Inspirationen für die DXSM-Permutation, als ich das letztes Jahr schrieb.

@charris Was ist Ihr Appetit darauf, die Implementierung von PCG64DXSM in 1.19.0 verfügbar zu machen (aber noch nicht standardmäßig)? Was ist das für eine Zeitleiste? Ich sehe, wir haben bereits 1.19.0rc2 veröffentlicht, was für die Einführung einer neuen Funktion nicht besonders gut ist. Auch hier bin ich nicht in Flammen. Ich würde gerne 1.19.0 veröffentlichen, um nur unsere Richtlinien zu Änderungen an default_rng() dokumentieren und neue Inhalte in 1.20.0 einzuführen.

@rkern Der endgültige RC muss

EDIT: Vorausgesetzt natürlich, dass es keine großen Probleme mit dem neuen Code gibt und es nicht so aussieht. Ich mache mir auch keine allzu großen Sorgen um Probleme mit PCG64. Es ist unwahrscheinlich, dass jemand Probleme mit unseren empfohlenen Verfahren hat.

@imneme Eine Woche wäre toll. Zwei Wochen wären in Ordnung. Vielen Dank!

Ich habe mir eine Frage gestellt, die etwas vom Thema abweicht. Wir möchten, dass unsere Bitgeneratoren zufällige Bits erzeugen, aber AFAICT, die meisten Tests beinhalten ganze Zahlen. Wie gut können die vorhandenen Tests die Bits tatsächlich testen? Wenn jemand, der mit dem Gebiet besser vertraut ist, diese Frage beantworten könnte, wäre ich sehr dankbar.

Was getestet wird, ist der Strom von Bits. Wir teilen der Testsoftware die natürliche Wortgröße des PRNG mit, die wir ausgeben, aber nur, damit sie die besten Faltungen der Bits ausführen kann, um Fehler am effizientesten zu provozieren, die dazu neigen, in den niedrigen oder hohen Bits des PRNG aufzutauchen Wort in schlechten PRNGs. Die Software, die wir heutzutage alle verwenden, ist PractRand, und ihre Tests sind hier leicht dokumentiert . Das am besten zu lesende Papier ist wahrscheinlich das für TestU01 , die vorherige Goldstandard-Testsuite. Das Benutzerhandbuch enthält weitere Informationen zu den Tests.

Ich entschuldige mich, wenn dies snarky klingt (obwohl es sicherlich darauf hingewiesen wird), aber es ist auch aufrichtig: Ich freue mich darauf, die Implementierung, Analyse, Benchmarks und PractRand-Ergebnisse auf Ihrer Website oder auf arXiv zu sehen. Wir sind hier (einigermaßen informierte) Praktiker, keine PRNG-Forscher, und nicht besonders gut gerüstet, um diesen Vorschlag umzusetzen. Ich kann den Sinn davon erkennen, aber angesichts der anderen Einschränkungen meiner persönlichen Zeit neige ich nicht dazu, mir die Mühe zu machen, dies vom Vorschlag zur Implementierung und Analyse zu bringen. Wenn Sie diesen Vorschlag an Numpy richten, brauchen wir PRNG-Forscher, um diese Arbeit zu erledigen. Wenn Sie diesen Vorschlag wirklich an eine andere Person richten, verwenden Sie Ihre Website.

Ich kann Ihren Standpunkt perfekt verstehen. Der Code und der Benchmark befinden sich am Ende der Seite und kommentieren die Probleme von PCG (http://prng.di.unimi.it/pcg.php) seit einigen Jahren mit dem Namen LCG128Mix. Auf meiner Hardware, einer Intel (R) Core (TM) i7-7700-CPU mit 3,60 GHz, mit gcc 9.2.1 und -fno-move-loop-invariants -fno-unroll-loops sind 2,16 ns erforderlich.

Der Code ist sehr einfach - er kombiniert ein Standard-LCG mit einer Standard-Mischfunktion (Staffords verbesserter MurmurHash3-Finalizer). Ich habe es leicht modifiziert, um eine programmierbare Konstante zu haben:

    #include <stdint.h>
    __uint128_t x; // state
    __uint64_t c;  // stream constant (odd)

    uint64_t inline next(void) {
        // Put in z the top bits of state
        uint64_t z = x >> 64;
        // Update LCG state
        x = x * ((__uint128_t)1 << 64 ^ 0xd605bbb58c8abbfd) + c;
        // Compute mix
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
        z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
        return z ^ (z >> 31);
    }

Wie ich zuvor erklärt habe, ist die 65-Bit-Konstante aufgrund theoretischer Probleme von Multiplikatoren, die kleiner als die Quadratwurzel des Moduls sind, viel besser als jede 64-Bit-Konstante als Multiplikator.

Wenn Sie an einem prinzipielleren Design interessiert sind, werde ich PractRand-Tests durchführen. Sie müssen jedoch berücksichtigen, dass diese Mischfunktion einen hervorragenden Generator, SplitMix, selbst mit einem viel schwächeren zugrunde liegenden Generator (es war nur additiv) und mit einem kleineren Zustand (64 Bit) ergab. Es wird also einfach "besser" als SplitMix, das PractRand mit 32 TB besteht.

Und der zugrunde liegende Generator ist ein LCG, sodass Sie alle üblichen Schnickschnack aus den 60er Jahren haben: Sprünge, Entfernungen usw. Aber Sie haben auch eine statistische Garantie, dass jedes Bit des Ergebnisses von jedem Bit der höheren 64 Bits abhängt Zustand.

Wenn Sie an Benchmarks mit anderen Generatoren denken oder andere Compiler verwenden, lassen Sie es mich bitte wissen.

Aber bitte auf die gleiche aufrichtige Weise: Nur wenn Sie wirklich daran interessiert sind, ein Design, bei dem Riesen auf den Schultern stehen, nur mit Standardkomponenten in Betracht zu ziehen. Es gibt auch persönliche Einschränkungen für meine Zeit, und ich bin gerne bereit, einen Beitrag zu leisten, aber ich möchte vermeiden, Zeit mit einem Generator zu verbringen, der keine Chance hat, berücksichtigt zu werden.

Übrigens, um ein greifbareres Maß dafür zu geben, wie die Qualität der beteiligten Multiplikatoren verbessert werden kann, habe ich die Spektralwerte des aktuellen 64-Bit-Multiplikators von PCG DXS und einer Alternative von f₂ bis f₈ berechnet.

Spektralwerte sind die Standardmethode zur Beurteilung der Güte eines Multiplikators. 0 ist schlecht, 1 ist ausgezeichnet. Jede Partitur beschreibt, wie gut die Paare, Tripel, 4-Tupel usw. in der Ausgabe verteilt sind.

Diese sieben Zahlen können in der klassischen Kennzahl, der minimalen oder einer gewichteten Kennzahl (die erste Bewertung plus die zweite geteilt durch zwei usw., normalisiert) wieder aufgenommen werden, um die ersten Bewertungen wichtiger zu machen, wie von Knuth in TAoCP vorgeschlagen , und dies sind das Minimum und das gewichtete Maß für den aktuellen Multiplikator:

0xda942042e4dd58b  0.633  0.778

Es gibt viel bessere 64-Bit-Konstanten als diese:

0xff37f1f758180525  0.761  0.875

Wenn Sie auf 65 Bit gehen, im Wesentlichen mit der gleichen Geschwindigkeit (zumindest für LCG128Mix ist die gleiche Geschwindigkeit), erhalten Sie ein besser gewichtetes Maß:

0x1d605bbb58c8abbfd  0.761  0.899

Der Grund dafür ist, dass 64-Bit-Multiplikatoren eine intrinsische Grenze für ihren f₂-Score (≤ 0,93) haben, was, wie von Knuth festgestellt, am relevantesten ist:

0xda942042e4dd58b5  0.795
0xff37f1f758180525  0.928
0x1d605bbb58c8abbfd  0.992

Der erste Multiplikator hat also eine mittelmäßige f₂-Punktzahl. Der zweite Multiplikator kommt dem Optimum für einen 64-Bit-Multiplikator sehr nahe. Der 65-Bit-Multiplikator hat diese Einschränkungen nicht und eine Punktzahl nahe 1, die im Allgemeinen die bestmögliche ist.

Der Vollständigkeit halber hier alle Punkte:

 0xda942042e4dd58b5  0.794572 0.809219 0.911528 0.730396 0.678620 0.632688 0.639625
 0xff37f1f758180525  0.927764 0.913983 0.828210 0.864840 0.775314 0.761406 0.763689 
0x1d605bbb58c8abbfd  0.991889 0.907938 0.830964 0.837980 0.780378 0.797464 0.761493

Sie können diese Punktzahl neu berechnen oder nach Ihrem eigenen Multiplikator mit dem Code suchen, den Guy Steele und ich verteilt haben: https://github.com/vigna/CPRNG . Die besseren Multiplikatoren werden dem zugehörigen Papier entnommen.

PCG wird wahrscheinlich ein gutes Standardprogramm für Numpy sein, aber ich denke nicht, dass es den Test der Zeit bestehen wird, da es vielversprechendere, aber weniger getestete Möglichkeiten gibt, dies zu tun. Ich schlage einen im Folgenden vor.

Der halb chaotische SFC64 ist einer der schnellsten statistisch soliden Generatoren mit einer angemessen großen Mindestdauer. SFC64 hat keine Sprungfunktionen, kann jedoch ohne Geschwindigkeitsaufwand erweitert werden, um 2 ^ 63 garantierte eindeutige Streams zu unterstützen. Fügen Sie einfach eine Weyl-Sequenz mit einer vom Benutzer gewählten additiven Konstante k hinzu (muss ungerade sein), anstatt den Zähler nur um eins zu erhöhen. Jedes ungerade k erzeugt eine eindeutige volle Periode. Es sind zusätzliche 64-Bit-Status erforderlich, um das Weyl konstant zu halten:

typedef struct {uint64_t a, b, c, w, k;} sfcw64_t; // k = stream

static inline uint64_t sfcw64_next(sfcw64_t* s) {
    enum {LROT = 24, RSHIFT = 11, LSHIFT = 3};
    const uint64_t out = s->a + s->b + (s->w += s->k);
    s->a = s->b ^ (s->b >> RSHIFT);
    s->b = s->c + (s->c << LSHIFT);
    s->c = ((s->c << LROT) | (s->c >> (64 - LROT))) + out;
    return out;
}

Ein 320-Bit-Zustand ist manchmal unerwünscht, daher habe ich versucht, ihn wieder auf 256 Bit zu reduzieren. Beachten Sie auch die geänderte Ausgabefunktion, die die Weyl-Sequenz besser zum Bitmischen nutzt. Es verwendet einen chaotischen / strukturierten Zustand von 128/128 Bit, der eine gute Balance zu finden scheint:
/ EDIT: rotl64 () aus der Ausgabefunktion entfernt + Bereinigung, 6. August:

typedef struct {uint64_t a, b, w, k;} tylo64_t;

static inline uint64_t tylo64_next(tylo64_t* s) {
    enum {LROT = 24, RSHIFT = 11, LSHIFT = 3};
    const uint64_t b = s->b, out = s->a ^ (s->w += s->k);
    s->a = (b + (b << LSHIFT)) ^ (b >> RSHIFT);
    s->b = ((b << LROT) | (b >> (64 - LROT))) + out;
    return out;
}

Dies hat derzeit 4 TB in PractRand-Tests ohne Anomalien bestanden, und ich habe den Hamming-Gewichtstest von Vigna bislang ohne Probleme durchgeführt (obwohl das Bestehen dieser Tests keine Garantie für eine nahezu echte Zufallsausgabe ist, sondern eher ein Test, ob das Prng fehlerhaft ist oder nicht ).

Hinweis: Es ist angeblich statistisch von Vorteil, eine (eindeutige) zufällige Weyl-Konstante mit ungefähr 50% der gesetzten Bits zu verwenden, aber nur weitere Tests oder Analysen werden zeigen, wie wichtig dies ist.

/ Änderungen: Aufräumarbeiten.

@ tylo-work SFC64 ist bereits in NumPy enthalten, zusammen mit Philox handelt es sich um den Standardgenerator.

Ok, ich wusste nicht genau, welche implementiert wurden, also geht es nur darum, die am besten geeigneten aus diesen auszuwählen? Fair genug und danke für die Klarstellung.

Ich werde versuchen, meinen vorgeschlagenen Generator ausgiebig zu testen, um zu sehen, wie er sich mit anderen schlägt. Bisher sieht er in Bezug auf Geschwindigkeit, Ausgabequalität, Einfachheit / Größe / Portabilität und massive massive Nutzung sehr gut aus. Aber ich würde mich freuen, wenn andere es auch testen würden.

Ich glaube nicht, dass wir die Diskussion über das Standard-PRNG von Grund auf neu eröffnen. Wir haben ein sehr spezifisches Problem mit unserem aktuellen PRNG und suchen nach verfügbaren, eng verwandten Varianten, die dieses spezifische Problem angehen. Eines unserer Probleme ist, dass das aktuelle Standard-PRNG bestimmte Funktionen des PRNG wie die Sprungbarkeit verfügbar macht, die die Variante, die es ersetzt, weiterhin verfügbar machen muss. SFC64 (entweder unser oder Ihr) verfügt nicht über diese Funktion.

Es ist möglich, dass @bashtage bereit ist, eine PR für randomgen zu

@ tylo-work Wenn Sie an einer parallelen Ausführung interessiert sind, sollten Sie sich die SeedSequence-Implementierung von NumPy ansehen.

Ich glaube nicht, dass wir die Diskussion über das Standard-PRNG von Grund auf neu eröffnen. Wir haben ein sehr spezifisches Problem mit unserem aktuellen PRNG und suchen nach verfügbaren, eng verwandten Varianten, die dieses spezifische Problem angehen.

Angenommen, Sie möchten etwas PCG-DXS-ähnliches, gibt es weitere Verbesserungen, die Sie mit nur besseren Konstanten (und einer sehr geringfügigen Verlangsamung) erzielen können. Zum Beispiel wird PCG-DXS bald zwei verschiedene Arten von Tests für zwei verschachtelte, korrelierte Teilsequenzen mit denselben unteren 112 Zustandsbits nicht bestehen:

rng=PCGDXS_int112, seed=0x4d198651
length= 128 gigabytes (2^37 bytes), time= 5700 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/64]TMFn(0+2):wl             R= +57.3  p~=   2e-27     FAIL !!
  [Low8/64]FPF-14+6/64:(1,14-0)     R= +17.5  p =  8.0e-16    FAIL
  [other failures in the same tests]
  ...and 1893 test result(s) without anomalies

Beachten Sie, dass es sich nur um 65536 korrelierte Sequenzen handelt - nichts, vor dem Sie Angst haben müssen.

Sie können den Generator jedoch verbessern, indem Sie einen besseren Multiplikator wie 0x1d605bbb58c8abbfd und einen besseren Mixer wie 0x9e3779b97f4a7c15 auswählen. Die erste Zahl ist ein 65-Bit-Multiplikator mit viel besseren Spektralwerten. Die zweite Zahl ist nur der goldene Schnitt in einer 64-Bit-Fixpunktdarstellung, und es ist bekannt, dass diese gute Mischeigenschaften aufweist (siehe Knuth TAoCP zum multiplikativen Hashing). Beispielsweise wird es von der Eclipse Collections-Bibliothek zum Mischen von Hash-Codes verwendet.

Infolgedessen scheitern Sie nur an FPF für dieselbe Datenmenge:

rng=PCG65-DXSϕ_int112, seed=0x4d198651
length= 128 gigabytes (2^37 bytes), time= 5014 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low8/64]FPF-14+6/64:(0,14-0)     R= +16.1  p =  1.5e-14    FAIL
  [other failures in the same test]
  ...and 1892 test result(s) without anomalies

In der Tat, wenn wir bei 2 TB weiter gehen, schlägt PCG-DXS drei Arten von Tests für die gleichen verschachtelten, korrelierten Teilsequenzen fehl:

rng=PCGDXS_int112, seed=0x4d198651
length= 2 terabytes (2^41 bytes), time= 53962 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/32]TMFn(0+0):wl             R= +50.2  p~=   4e-23     FAIL !!
  [Low8/64]FPF-14+6/64:(1,14-0)     R=+291.1  p =  4.7e-269   FAIL !!!!!!
  [Low8/64]Gap-16:B                 R= +19.5  p =  1.4e-16    FAIL !
  [other failures in the same tests]
  ...and 2153 test result(s) without anomalies

während PCG65-DXSϕ immer noch nur FPF ausfällt:

rng=PCGDXS65ϕ_int112, seed=0x4d198651
length= 2 terabytes (2^41 bytes), time= 55280 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low8/64]FPF-14+6/64:(0,14-0)     R=+232.1  p =  2.0e-214   FAIL !!!!!!
  [other failures in the same test]
  ...and 2153 test result(s) without anomalies

Früher oder später wird natürlich auch PCG65-DXSϕ Gap und TMFn versagen. Sie müssen jedoch viel mehr Ausgabe sehen als mit PCG-DXS.

Dies ist der vollständige Code für PCG65-DXSϕ, bei dem es sich nur um PCG-DXS mit besseren Konstanten handelt:

#include <stdint.h>

__uint128_t x; // State
uint64_t c; // Additive constant

static inline uint64_t output(__uint128_t internal) {
    uint64_t hi = internal >> 64;
    uint64_t lo = internal;

    lo |= 1;
    hi ^= hi >> 32;
    hi *= 0x9e3779b97f4a7c15;
    hi ^= hi >> 48;
    hi *= lo;
    return hi;
}

static uint64_t inline next(void) {
    __uint128_t old_x = x;
    x = x *  ((__uint128_t)1 << 64 ^ 0xd605bbb58c8abbfd) + c;
    return output(old_x);
}

Die geringfügige Verlangsamung ist auf einen Additionsbefehl (verursacht durch den 65-Bit-Multiplikator) und das Laden von zwei 64-Bit-Konstanten zurückzuführen.

Ich befürworte Generatoren dieser Art im Allgemeinen nicht, aber PCG65-DXSϕ ist messbar besser als PCG-DXS, wenn es darum geht, Korrelationen zu verbergen.

@Vigna , FYI, ich habe auch einige Interleaving-Tests durchgeführt und festgestellt, dass xoshiro256 ** beim Erstellen von 128 verschachtelten Streams oder mehr ziemlich schnell fehlgeschlagen ist. Mit 256 schlug es schnell fehl. Der Zweck des Tests besteht darin, zu überprüfen, wie gut sich die PRNGs verhalten, wenn jeder Stream mit einigen linearen Abhängigkeiten initialisiert wurde. Im Wesentlichen wird der Status auf s[0]=s[1]=s[2]=s[3] = k1 + stream*k2 initialisiert. Dann werden 12 Ausgänge übersprungen, was im Grunde die Initialisierung von sfc64 ist.

Mir ist klar, dass dies nicht die empfohlene Initialisierung für xoshiro ist, aber es ist immer noch interessant - und ein wenig besorgniserregend -, dass die Tests für xoshiro mit wenigen verschachtelten Streams in Ordnung schienen, aber bei vielen fehlgeschlagen sind.

seed: 1591888413
RNG_test using PractRand version 0.95
RNG = RNG_stdin64, seed = unknown
test set = core, folding = standard (64 bit)
...
rng=RNG_stdin64, seed=unknown
length= 2 gigabytes (2^31 bytes), time= 29.6 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/64]FPF-14+6/16:(1,14-1)     R=  +7.2  p =  3.7e-6   unusual
  [Low1/64]FPF-14+6/16:all          R=  +9.6  p =  1.8e-8   very suspicious
  ...and 261 test result(s) without anomalies

rng=RNG_stdin64, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 55.5 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/64]FPF-14+6/16:(0,14-0)     R= +13.4  p =  4.7e-12   VERY SUSPICIOUS
  [Low1/64]FPF-14+6/16:(1,14-0)     R=  +9.4  p =  2.6e-8   suspicious
  [Low1/64]FPF-14+6/16:(2,14-1)     R=  +7.7  p =  1.3e-6   unusual
  [Low1/64]FPF-14+6/16:all          R= +17.4  p =  8.8e-16    FAIL !
  ...and 275 test result(s) without anomalies

Ich habe auch versucht, die Initialisierung für SFC64 und TYLO64 zu schwächen, um nur 2 Ausgänge zu überspringen, aber sie schienen immer noch in Ordnung zu sein.
Leistung: xoshiro256 ** läuft auf meinem Computer 33% langsamer als die beiden anderen. TYLO64 aktualisiert nur 196 Bit Statusvariablen. Hier ist das Testprogramm:

int main()
{
    //FILE* f = freopen(NULL, "wb", stdout);  // Only necessary on Windows, but harmless.
    enum {THREADS = 256};
    uint64_t seed = 1591888413; // <- e.g. this fails. // (uint64_t) time(NULL); 
    fprintf(stderr, "seed: %lu\n", seed);

    static tylo64_t tyl[THREADS];
    static sfc64_t sfc[THREADS];
    static uint64_t xo[THREADS][4];

    for (size_t i = 0; i < THREADS; ++i) {
    tyl[i] = tylo64_seed(seed + (12839732 * i), 19287319823 * i);
    sfc[i] = sfc64_seed(seed + (12839732 * i));
    xo[i][0] = xo[i][1] = xo[i][2] = xo[i][3] = seed + (12839732 * i);
    for (int j=0; j<12; ++j) xoshiro256starstar_rand(xo[i]);
    }
    static uint64_t buffer[THREADS];
    size_t n = 1024 * 1024 * 256 / THREADS;

    while (1/*n--*/) {
        for (int i=0; i<THREADS; ++i) {
        //buffer[i] = tylo64_rand(&tyl[i]);
        //buffer[i] = sfc64_rand(&sfc[i]);
            buffer[i] = xoshiro256starstar_rand(xo[i]);
        }
        fwrite((void*) buffer, sizeof(buffer[0]), THREADS, stdout);
    }
    return 0;
}

Ich werde einen relevanten Header-Code einfügen:

typedef struct {uint64_t a, b, w, k;} tylo64_t; // k = stream

static inline uint64_t tylo64_rand(tylo64_t* s) {
    enum {LROT = 24, RSHIFT = 11, LSHIFT = 3};
    const uint64_t b = s->b, w = s->w, out = (s->a + w) ^ (s->w += s->k);
    s->a = (b + (b << LSHIFT)) ^ (b >> RSHIFT);
    s->b = ((b << LROT) | (b >> (64 - LROT))) + out;
    return out;
}

/* stream in range [0, 2^63) */
static inline tylo64_t tylo64_seed(const uint64_t seed, const uint64_t stream) {
    tylo64_t state = {seed, seed, seed, (stream << 1) | 1};
    for (int i = 0; i < 12; ++i) tylo64_rand(&state);
    return state;
}

static inline uint64_t rotl(const uint64_t x, int k) {
    return (x << k) | (x >> (64 - k));
}
static inline uint64_t xoshiro256starstar_rand(uint64_t* s) {
    const uint64_t result = rotl(s[1] * 5, 7) * 9;
    const uint64_t t = s[1] << 17;
    s[2] ^= s[0];
    s[3] ^= s[1];
    s[1] ^= s[2];
    s[0] ^= s[3];
    s[2] ^= t;
    s[3] = rotl(s[3], 45);
    return result;
}

@ tylo-work Ich schätze die Analyse, aber ich brauche dieses Problem wirklich, um konzentriert zu bleiben. Wenn Sie diese Diskussion fortsetzen möchten, empfehle ich Ihnen, Ihre Arbeit in Ihrem eigenen Github-Repo zu veröffentlichen und hier einen weiteren Beitrag zu verfassen, in dem die Leute hier dazu eingeladen werden. Alle anderen, bitte antworten Sie dort. Danke für Ihre Kooperation.

@imneme @rkern Die Zeit für die Version 1.19 läuft ab.

@rkern Es sieht so aus, als würde PCG64DXSM es nicht in 1.19.0 schaffen. Ich werde dieses Wochenende veröffentlichen. Wenn Sie den oben erwähnten Hinweis zu unseren Änderungsrichtlinien / bevorstehenden Änderungen schreiben könnten, wäre ich Ihnen dankbar.

Entschuldigung, ich habe mich mit einigen anderen Angelegenheiten befasst, die nichts miteinander zu tun haben. Aufgrund unserer Diskussion halte ich eine kleine Verzögerung nicht für ein großes Problem, da PCG64DXSM als alternative Option geplant war und nicht als neue Standardeinstellung (zumindest vorerst).

Ist es nach dem Start von 1.20 an der Zeit, dies erneut zu überprüfen und zu DXSM zu wechseln?

Wir hätten noch etwas Zeit, um den Umzug vor der Verzweigung durchzuführen, aber es könnte gut sein, innerhalb der nächsten Woche oder so damit zu beginnen. @bashtage Ich denke, Sie haben das PCG64DXSM fertig und dies erfordert hauptsächlich die Entscheidung, den Schalter auf den Standard-Stream zu schalten?

Nach dem, was es sieht, klang es so, als ob wir dies nur für 1,20 tun sollten, wenn wir es sofort verfügbar haben.

IIRC, wir haben auf eine Referenz gewartet, die verknüpft werden könnte. Aber wenn die Leute mit der Zufallszahl mit der Änderung zufrieden sind, sollten wir sie verwenden. Benötigen wir einen speziellen Code für Windows?

Es ist nur eine andere Konstante und eine andere Verschlüsselungsfunktion. Nichts ist neuartiger als das, was @rkern für die ursprüngliche PCG64-Implementierung unter Windows geschrieben hat. Ich denke, die Entscheidung war, ein vollständig eigenständiges PCG64DXSM zu haben, anstatt Code (für die Leistung) gemeinsam zu nutzen.

Es wäre wahrscheinlich sinnvoll gewesen, von der WIP-Verzweigung von

Ich sagte, ich würde einen Blog-Beitrag darüber schreiben, den @rkern meiner Meinung nach gekümmert und es ist noch nicht geschehen (sorry). In der Zwischenzeit wurde die DXSM-Permutation im Test abgeschliffen und scheint weiterhin eine Verbesserung gegenüber dem Original zu sein. Aus den Anmerkungen weiter oben im Thread geht hervor, dass @rkern eine noch stärkere Ausgabepermutation gefallen hat, dies jedoch entweder Geschwindigkeit kostet oder (wenn Sie Ecken abschneiden, um Geschwindigkeit zu gewinnen) eine triviale Vorhersagbarkeit hinzufügt.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen