C++11-PRNGs
-
asfdlol schrieb:
Und auch wenn es eine effiziente Version gibt, dann lässt sich die auch ohne Probleme im eine freie Funktion implementieren.
Eh... nö. Wie kommst du darauf? Als friend vielleicht, aber dass Memberfunktionen hier hübscher als friend gehacke sind sollte wohl klar sein.
asfdlol schrieb:
Das was PRND schon gepostet hat.
Also da habe ich jetzt nichts zur kryptographischen Sicherheit gefunden?
asfdlol schrieb:
Schade. Wie macht ihr das denn?
Es gibt die Möglichkeit den Seed und wie oft man discard()en muss zu speichern. Wenn das zu langsam ist muss man wohl in den sauren Apfel beißen und es selbst implementieren, aber ich meine dann scheint man ja eh recht spezielle Anforderungen an die Performance zu haben.
Ansonsten wegen der Engine, also wenn die die mit <16> instanziiere und eine uniform_int_distribution(0, 32) 50000 benutze kommt da bei mir (VS 2013) folgendes bei raus:
0: 1432 1: 1324 2: 1364 3: 1382 4: 1334 5: 1329 6: 1438 7: 1622 8: 1641 9: 1598 10: 1570 11: 1630 12: 1629 13: 1667 14: 1585 15: 1600 16: 1655 17: 1447 18: 1415 19: 1322 20: 1355 21: 1382 22: 1376 23: 1368 24: 1668 25: 1551 26: 1651 27: 1650 28: 1608 29: 1602 30: 1636 31: 1662 32: 1507Sind zwar ein paar "Ausschläge" bei die nicht ganz sauber aussehen, aber die würde ich dann mal frech auf das etwas eigenartige Verhalten deiner Engine schieben. (Innerhalb einer Periode kann eine Zahl nur genau ein mal vor kommen.)
-
cooky451 schrieb:
asfdlol schrieb:
Und auch wenn es eine effiziente Version gibt, dann lässt sich die auch ohne Probleme im eine freie Funktion implementieren.
Eh... nö. Wie kommst du darauf? Als friend vielleicht, aber dass Memberfunktionen hier hübscher als friend gehacke sind sollte wohl klar sein.
Oder die Engine, für welche dies der Fall ist, bietet eine Methode an, die dann von der entsprechenden Spezialisierung der freien Funktion aufgerufen wird. Ist aber auch egal nun, ich bin das Boilerplate-
discard()mit CRTP los geworden.cooky451 schrieb:
asfdlol schrieb:
Das was PRND schon gepostet hat.
Also da habe ich jetzt nichts zur kryptographischen Sicherheit gefunden?
Okay, ich habe gerade dieses Paper hier gefunden, in welchem steht, dass KISS doch nicht kryptographisch sicher ist. Schade. Nichtsdestotrotz produziert KISS laut dem BigCrush-Test bessere Zufallszahlen wie der Mersenne-Twister.
cooky451 schrieb:
[...]
Oha, deine Ergebnisse sind ja um Welten besser als die meinen. Ich hab meinen Code des Tests leider wieder weggeworfen und weiss die exakten Testparameter nicht mehr...
Vielleicht finde ich sie wieder heraus. Vielleicht habe ich aber auch bei der Auswertung gestern Quatsch gemacht, denn meine Ergebnisse sind schon so absonderlich, dass sie kaum glaubwürdig waren / sind. Könnte jedoch wetten, dass die Auswerung gestimmt hat.
-
asfdlol schrieb:
std::uniform_int_distributiongenerieren lassen. Diese sind zu meiner Enttäuschung äusserst schlecht verteilt. Seht selbst:0: 0 1: 0 2: 0 3: 0 4: 0 5: 0 6: 96774 7: 0 8: 0 9: 0 10: 0 11: 0 12: 0 13: 0 14: 0 15: 0 16: 96774 17: 0 18: 0 19: 0 20: 0 21: 0 22: 0 23: 0 24: 0 25: 0 26: 96774 27: 0 28: 0 29: 0 30: 0 31: 0 32: 0 33: 0 34: 0 35: 0 36: 96774 37: 0 38: 0 39: 0 40: 0 41: 0 42: 0 43: 0 44: 0 45: 0 46: 96774 47: 0 48: 0 49: 0 50: 0 51: 0 52: 0 53: 0 54: 0 55: 0 56: 96774 57: 0 58: 0 59: 0 60: 0 61: 0 62: 0 63: 0 64: 0 65: 0 66: 129033 67: 0 68: 0 69: 0 70: 0 71: 0 72: 0 73: 0 74: 0 75: 0 76: 96774 77: 0 78: 0 79: 0 80: 0 81: 0 82: 0 83: 0 84: 0 85: 0 86: 96774 87: 0 88: 0 89: 0 90: 0 91: 0 92: 0 93: 0 94: 0 95: 0 96: 96775 97: 0 98: 0 99: 0Wieso ist das so?
Haste die std::uniform_int_distribution lange leben lassen oder für jede Zahl eine neue genaut, wie es so oft hier im Form vorgeschlagen wird?
-
Darf die
std::uniform_int_distributiondenn lügen wenn man dauernt neue Instanzen erzeugt?
-
Wieso lügen?
Woher soll denn die nächste Instanz den Zustand ihrer Vorgängerin kennen?
-
hustbaer schrieb:
Darf die
std::uniform_int_distributiondenn lügen wenn man dauernt neue Instanzen erzeugt?So Sachen wie "Produces random integer values i, uniformly distributed on the closed interval [a, b]" habe ich immer nur so verstanden, daß daß die Ausgabefolge eines Objekts auf lange Sicht gleichverteilt ist. Keine Aussage drüber, was kurzfristig passiert.
-
@Caligulaminus
Ich würde mal behaupten dass manstd::uniform_int_distributionwunderbar stateless implementieren kann.Bei anderen Verteilungen mag das schwieriger sein, keine Ahnung.
Wobei ich mir grad schwer tue mir vorzustellen wo eine "stateful" Implementierung Sinn machen würde.@volkard
Ja klar auf lange Sicht. Die Frage ist nur ob man "stateful" Distributions erlauben sollte.
Vorberechnete Konstanten/Lookup-Tables/... sind klarerweise OK. Mutable State in einer Distribution ... weiss nicht ob das so gut ist.
-
hustbaer schrieb:
@Caligulaminus
Ich würde mal behaupten dass manstd::uniform_int_distributionwunderbar stateless implementieren kann.Das denke ich auch, aber erstens ist stateful sicher effizienter und zweitens ist die std::uniform_int_distribution nunmal stateful implementiert (zumindest bei meinem MSVC).
-
volkard schrieb:
hustbaer schrieb:
Darf die
std::uniform_int_distributiondenn lügen wenn man dauernt neue Instanzen erzeugt?So Sachen wie "Produces random integer values i, uniformly distributed on the closed interval [a, b]" habe ich immer nur so verstanden, daß daß die Ausgabefolge eines Objekts auf lange Sicht gleichverteilt ist. Keine Aussage drüber, was kurzfristig passiert.
Die einzigen Member von uniform_int_distribution beim GCC sind min und max, das Ziehen könnte hier theoretisch const sein.
Allerdings ist das nicht bei jeder Distribution so, std::normal_distribution hat einen State.
Die Verteilheit sollte aber auch gewährleistet sein, wenn man jedesmal eine neue uniform_int_distribution erstellt, nur halt vielleicht etwas langsamer.
-
Caligulaminus schrieb:
stateful implementiert (zumindest bei meinem MSVC).
Also bei mir nicht wirklich, wenn ich da mal kurz drüber schaue finde ich nur min/max, und wenn ich die distribution jedes mal neu erstelle bekomme ich auch immer noch exakt die selben Ergebnisse.
-
cooky451 schrieb:
Also bei mir nicht wirklich
Ich glaube, ich muß mich korrigieren, meine std::uniform_int_distribution scheint doch nicht stateful zu sein.
Morgen muß ich noch mal genauer schauen...
-
volkard schrieb:
Haste die std::uniform_int_distribution lange leben lassen oder für jede Zahl eine neue genaut, wie es so oft hier im Form vorgeschlagen wird?
Ich hab eine Distribution für alle Werte genommen. Ehrlich gesagt habe ich nie Diesbezügliches im Forum gelesen.
Also bei meinem Visual C++ 2013 Express ist
std::uniform_int_distributionauch zustandslos implementiert.
-
Mal außen vor, dass der Generator keine vereinfachte std::shuffle_order_engine und natürlich nicht gleichverteilt ist (wie hoch ist die Wahrscheinlichkeit, mit ihm dreimal hintereinander die gleiche Zahl zu ziehen?) und eine uniform_int_distribution keinerlei Verpflichtung hat, damit sinnvoll klarzukommen -- irgendwie sieht mir das Ergebnis so aus, als sei eine PermutationEngine<10> als Generator für eine uniform_int_distribution(0, 100) verwendet worden. Das könnte natürlich nicht gutgehen.
@asfdlol: Zeig doch mal den kompletten Code, der das Verhalten erzeugt.
-
seldon schrieb:
Mal außen vor, dass der Generator keine vereinfachte std::shuffle_order_engine und natürlich nicht gleichverteilt ist (wie hoch ist die Wahrscheinlichkeit, mit ihm dreimal hintereinander die gleiche Zahl zu ziehen?)
Es sind keine guten Zufallszahlen wenn man nicht 3x hintereinander die selbe Zahl ziehen kann. Aber inwiefern soll das die (1 dimensionale) Gleichverteilung stören? Die ist ja gerade perfekt bei dem Generator.
und eine uniform_int_distribution keinerlei Verpflichtung hat, damit sinnvoll klarzukommen -- irgendwie sieht mir das Ergebnis so aus, als sei eine PermutationEngine<10> als Generator für eine uniform_int_distribution(0, 100) verwendet worden. Das könnte natürlich nicht gutgehen.
Keine Ahnung, ist das so?
Was sind denn die Anforderungen die eine Distribution an den übergebenen Generator stellen darf?
-
hustbaer schrieb:
Was sind denn die Anforderungen die eine Distribution an den übergebenen Generator stellen darf?
26.5.1.3 Uniform random number generator requirements
A uniform random number generator g of type G is a function object returning unsigned integer values such
that each value in the range of possible results has (ideally) equal probability of being returned. [ Note: The
degree to which g’s results approximate the ideal is often determined statistically. — end note ]Die Anforderungen werden hier also tatsächlich erfüllt. Es gibt, wie du schon anmerkst, keinen mathematischen Grund es anders zu verlangen. Die Zufallszahlen sind zwar schlecht (sowohl vor als auch nach dem man sie durch die Verteilungsfunktion gejagt hat), aber ihre Verteilung sollte richtig sein.
P.S.: Die Distributionen verlangen dann auch einen eben solchen uniform random number generator (§ 26.5.1.6, 2f). Nur um das nochmal klar zu stellen, falls es nicht offensichtlich war.
-
Danke

Und was sind die Anforderungen an min/max?
-
hustbaer schrieb:
Und was sind die Anforderungen an min/max?
min < max

Zumindest beim GCC klappt das auch wie man erwartet, d.h. es wird gewartet, bis genügend Information gesammelt wurde:
#include <random> #include <iostream> #include <functional> #include <map> struct poor_mans_device { std::mt19937 gen; std::uniform_int_distribution<int> dis = std::uniform_int_distribution<int>(1, 6); typedef int result_type; static constexpr result_type min() {return 1;} static constexpr result_type max() {return 6;} result_type operator()() { return dis(gen); } }; int main() { poor_mans_device gen; std::uniform_int_distribution<> dis(10, 36); std::map<int, int> map; for (int n=0; n<1000000; ++n) ++map[dis(gen)]; for(int i = 10; i < 37; ++i) std::cout << i << ' ' << map[i] << '\n'; }Sollte Gleichverteilung ergeben.
Oder, um es noch extremer zu machen:
#include <random> #include <iostream> #include <functional> #include <map> struct poor_mans_device { int min_, max_; std::mt19937 gen; std::uniform_int_distribution<int> dis; typedef int result_type; result_type min() const {return min_;} result_type max() const {return max_;} result_type operator()() { return dis(gen); } poor_mans_device(int min, int max): min_(min), max_(max), dis(min_, max_) {} }; int main() { poor_mans_device poor(0,1); poor_mans_device intermediate(0,1000); poor_mans_device good(0,1000000); std::uniform_int_distribution<> dis(10, 36); std::map<int, int> map_poor, map_intermediate, map_good, map_mixed; for (int n=0; n<1000000; ++n) { for(int i = 1; i < 3; ++i) { ++map_poor[dis(poor)]; ++map_intermediate[dis(intermediate)]; ++map_good[dis(good)]; } ++map_mixed[dis(poor)]; ++map_mixed[dis(intermediate)]; ++map_mixed[dis(good)]; } for(int i = 10; i < 37; ++i) std::cout << i << '\t' << map_poor[i] << '\t' << map_intermediate[i] << '\t' << map_good[i] << '\t' << map_mixed[i] << '\n'; }Die Distribution muss also dynamisch mit sich ändernden Generatoren auskommen.
P.S.: Ein bisschen Overengineered ist das schon :p . Wenn Geschwindigkeit eine kritische Rolle spielen würde, würde ich mir den Einsatz nochmal überlegen. Da ist mindestens eine Abfrage drin, ob min und max vom Generator ausreichen oder ob mehrere Ziehungen nötig sind, die man in 99.9% aller Fälle nicht braucht und die immer die gleiche Antwort hat.
-
seldon schrieb:
@asfdlol: Zeig doch mal den kompletten Code, der das Verhalten erzeugt.
Den habe ich leider nicht mehr (es erschliesst sich jedoch alles aus dem OP bis auf den Bereich vom Generator). Ich habe es auch nicht hinbekommen es zu reproduzieren, sondern bekomme stattdessen Verteilungen wie sie auch cooky451 etwa erhalten hat. Wie gesagt, vielleicht hatte ich irgendwo einen kleinen Fehler drin
seldon schrieb:
Mal außen vor, dass der Generator keine vereinfachte std::shuffle_order_engine
Sicher? Meine Engine nimmt doch immer einen
Range-grossen Block von einer hypothetischen Engine, die konsequtive Werte moduloRangeliefert, und gibt diese durchgemischt wieder.seldon schrieb:
natürlich nicht gleichverteilt ist (wie hoch ist die Wahrscheinlichkeit, mit ihm dreimal hintereinander die gleiche Zahl zu ziehen?)
Der von dir genannte Fall ist kein Kriterium für Gleichverteilung. Für meinen Generator gilt die Laplace-Formel, womit er gleichverteilt ist.
-
Nein, die PermutationEngine ist nicht gleichverteilt. Die erste gezogene Zahl kommt noch mit gleicher Wahrscheinlichkeit für alle Werte daher, die zweite schon nicht mehr -- der eben gezogene Wert kann nicht wieder kommen, und die anderen Werte werden entsprechend wahrscheinlicher. Die Laplace-Bedingung ist dann auch nicht erfüllt.
Eine shuffle_order_engine funktioniert so, dass aus einem bestehenden Array von Zufallszahlen zufällig eine ausgewählt wird (die ist dann der Rückgabewert) und ihr Slot mit einer aus einem gewrappten Zufallszahlengenerator neu gezogenen Zahl überschrieben wird. Sowohl kann aus dem gewrappten Generator die selbe Zahl erneut gezogen werden als auch der selbe Slot zweimal hintereinander ausgewählt. Die shuffle_order_engine ist gleichverteilt, wenn der gewrappte Zufallszahlengenerator es ist, aber den formellen Nachweis dafür müsste ich herleiten oder im Knuth nachschlagen.
Bei der uniform_int_distribution habe ich mich geirrt; die muss anscheinend tatsächlich auch hochskalieren können. Gut, die formalen Bedingungen für den Generator, den die uniform_int_distribution verlangt, erfüllt die PermutationEngine nicht, aber in diesem speziellen Anwendungsfall sollte eine zustandslose Implementierung von uniform_int_distribution Werte liefern, die so aussehen, als sei alles in Ordnung. Meine Vermutung mit der PermutationEngine<10> ist damit hinfällig.
-
seldon schrieb:
Nein, die PermutationEngine ist nicht gleichverteilt. Die erste gezogene Zahl kommt noch mit gleicher Wahrscheinlichkeit für alle Werte daher, die zweite schon nicht mehr -- der eben gezogene Wert kann nicht wieder kommen, und die anderen Werte werden entsprechend wahrscheinlicher. Die Laplace-Bedingung ist dann auch nicht erfüllt.
Wenn du forderst dass die Zahl die bei einer Ziehung rauskommen kann unabhängig von allen Zahlen davor ist, dann erfüllt das kein einziger PRNG.
Natürlich ist diese Eigenschaft - bzw. eine gute Annäherung daran - für viele Dinge wichtig. Deswegen verwendet man ja auch für viele Dinge Generatoren die u.A. in möglichst vielen Dimensionen gleichverteilt sind.
Ich kann aus den Artikeln zum Thema Gleichverteilung auch keine solche Forderung rauslesen. Da steht die Wahrscheinlichkeit muss für alle Zahlen gleich sein. Und das ist sie bei dem hier diskutierten Generator ja auch, wenn man kein Wissen über die davor gezogenen Zahlen hat. Und wenn man es hat, siehe oben, dann erfüllt das kein einziger PRNG.