Warum scheint C ++ rand () nur Zahlen derselben Größenordnung zu generieren?

146

In einer kleinen Anwendung, die in C / C ++ geschrieben wurde, habe ich ein Problem mit der randFunktion und möglicherweise dem Startwert:

Ich möchte eine Folge von Zufallszahlen erzeugen, die unterschiedliche Ordnungen haben, dh unterschiedliche Logarithmuswerte (Basis 2). Es scheint jedoch, dass alle produzierten Zahlen in derselben Größenordnung liegen und nur zwischen 2 ^ 25 und 2 ^ 30 schwanken.

Liegt es daran, dass rand()Unix-Zeit verwendet wird, die mittlerweile eine relativ große Zahl ist? Was vergesse ich? Ich säe rand()nur einmal am Anfang des main().

Tallaron Mathias
quelle
7
FWIW also, ist es C oder C ++? Wenn mit C / C ++ gemeint ist, dass Sie tatsächlich C ++ verwenden können und die Erwähnung von C nur zufällig war, kann dies möglicherweise hilfreich sein.
R. Martinho Fernandes
9
Leider haben Sie auf das falsche Pferd gewettet. Samen sollte nicht dein Problem sein. Ihr Problem war falsch erwartete Verteilung. Da ein unvoreingenommener Programmierer erwarten würde rand(), gleichmäßig verteilte Zahlen zurückzugeben (Dokumentation mit hohem Google-Ranking sagt dies ausdrücklich aus), halte ich diese Frage für zukünftige Leser nicht für nützlich. Deshalb stimmen Sie ab, aber lassen Sie sich nicht davon abhalten, SO zu verwenden.
Kaiser Orionii
12
@ doug65536 "... wo nie eine Zahl wiederholt wird" - das ist kein Zufall! Ich könnte meinen Ruhestand am Craps-Tisch finanzieren, wenn meine rand () - Würfel nie zweimal dieselbe Zahl zurückgeben würden, bis jede mögliche Zahl zurückgegeben worden wäre.
Chris Gregg
6
@GalacticCowboy Verwechseln Sie Periodizität nicht mit einer Wiederholung einzelner Zahlen. In dem von Ihnen zitierten Wikipedia-Artikel heißt es: "Ein wiederholtes Ergebnis bedeutet nicht, dass das Ende des Zeitraums erreicht wurde, da sein interner Zustand möglicherweise größer ist als seine Ausgabe." Es wäre sehr, sehr schlecht, wenn ein PRNG einen Wert erzeugen würde und dann garantiert würde, diesen Wert nicht wieder zu produzieren, bis alle Werte zurückgegeben wurden.
Chris Gregg
12
Doug65536, niemand kämpft. Sie sagen nur richtig, dass Sie falsch liegen. Ein PRNG könnte ziemlich glücklich Folgendes produzieren, wenn ich einen RAND zwischen 1 und 10 wollte: 2 4 7 2 8 1 5 9 7 3 Das wäre trotz der mehrfachen 2er und 7er völlig gültig. Ich denke, Sie verwechseln das PRNG mit der Shuffle-Funktion auf Ihrem iPhone.
Entspannen in Zypern

Antworten:

479

Es gibt nur 3% der Zahlen zwischen 1 und 2 30, die NICHT zwischen 2 25 und 2 30 liegen . Das klingt also ziemlich normal :)

Da 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%

C4stor
quelle
36
Ja, guter Punkt! Es gibt 31 mal mehr Zahlen zwischen 2 ^ 25 und 2 ^ 30 als zwischen 1 und 2 ^ 25 :) Danke für die schnelle Antwort. Ich muss dann das Programm überdenken. Frage beantwortet.
Tallaron Mathias
1
@TallaronMathias Erwägen Sie, die Zahl durch >>Bitverschiebung abzuschneiden - dies gibt Ihnen kleinere Zahlen. (Oder nehmen Sie einen Modul mit %.)
Sean Allred
13
Ich würde erwarten, dass dies für die meisten Programmierer offensichtlich ist: Jede vorzeichenlose Ganzzahl kleiner als 2 ^ 25 muss ihre ersten 7 Bits haben 0- und wenn jedes Bit zufällig ist ...
BlueRaja - Danny Pflughoeft
118
@ BlueRaja-DannyPflughoeft - Wenn die Wahrscheinlichkeiten offensichtlich wären, wären die Casinos aus dem Geschäft.
Brett Hale
26
@BrettHale - Ich glaube nicht, dass Programmierer die Zielgruppe eines Casinos sind.
EkoostikMartin
272

Das hellere Grün ist der Bereich zwischen 0 und 2 25 ; Das dunklere Grün ist der Bereich zwischen 2 25 und 2 30 . Die Zecken sind Potenzen von 2.

Verteilung

Casey Chu
quelle
42

Sie müssen genauer sein: Sie möchten unterschiedliche Logarithmuswerte für Basis 2, aber welche Verteilung möchten Sie dafür? Die Standardfunktionen von rand () erzeugen eine gleichmäßige Verteilung. Sie müssen diese Ausgabe mithilfe der Quantilfunktion transformieren, die der gewünschten Verteilung zugeordnet ist.

Wenn Sie uns die Verteilung mitteilen, können wir Ihnen die quantileFunktion mitteilen, die Sie benötigen.

Bathseba
quelle
13
+1, Verteilung ist der entscheidende Begriff. Es ist nicht wirklich sinnvoll, über Zufallszahlen zu sprechen, wenn nichts über die Verteilung bekannt ist. Uniform ist nur ein Sonderfall, wenn auch ein wichtiger. Könnte ein guter Ort sein, um auf verschiedene Distributionen aus der C ++ 11-Standardbibliothek hinzuweisen.
links um den
18

Wenn Sie unterschiedliche Größenordnungen wünschen, warum nicht einfach versuchen pow(2, rand())? Oder wählen Sie die Reihenfolge direkt als rand (), wie Harold vorgeschlagen hat?

aspiring_sarge
quelle
3
Gute Idee, aber Sie sollten Ihre Antwort mit pow anstelle von ^ korrigieren (dies ist der logische xor-Operator, nicht power, in C-Sprache).
kriss
6
Da rand()kann bis zu gehen RAND_MAX, müssen Sie wirklich Ihre Zufallszahl skalieren, damit das Ergebnis nicht überläuft ...
Floris
@Floris: Aber wenn Sie einen kleinen zählbaren Bereich auf einen sehr großen Bereich skalieren, werden Sie VIELE Löcher haben, was OP wahrscheinlich nicht erwartet.
André Caron
13

@ C4stor machte einen tollen Punkt. Für einen allgemeineren Fall, der für den Menschen leichter zu verstehen ist (Basis 10): Für den Bereich von 1 bis 10 ^ n liegen ~ 90% der Zahlen daher zwischen 10 ^ (n-1) und 10 ^ n ~ 99% der Zahlen gehen von 10 ^ (n-2) bis 10 ^ n. Fügen Sie so viele Dezimalstellen hinzu, wie Sie möchten.

Lustige Mathematik, wenn Sie dies für n fortsetzen, können Sie sehen, dass von 1 bis 10 ^ n, 99,9999 ...% = 100% der Zahlen mit dieser Methode von 10 ^ 0 bis 10 ^ n sind.

Wenn Sie nun eine Zufallszahl mit zufälligen Größenordnungen von 0 bis 10 ^ n für den Code wünschen, können Sie Folgendes tun:

  1. Generieren Sie eine kleine Zufallszahl von 0 bis n

  2. Wenn Sie den Bereich kennen, den n hat, generieren Sie eine große Zufallszahl der Ordnung 10 ^ k, wobei k> max {n} ist.

  3. Schneiden Sie die längere Zufallszahl, um die n Ziffern dieser großen Zufallszahl zu erhalten.

Francisco Presencia
quelle
46
Sie haben völlig Recht, aber für eine WIRKLICH leicht verständliche Antwort sollte sich das OP fragen, warum 90% der Zufallszahlen zwischen 1 und 100 zweistellig sind.
Fragen Sie nach Monica
13

Die grundlegende (und richtige) Antwort wurde bereits oben gegeben und akzeptiert: Es gibt 10 Zahlen zwischen 0 und 9, 90 Zahlen zwischen 10 und 99, 900 zwischen 100 und 999 usw.

Um eine rechnerisch effiziente Methode zum Erhalten einer Verteilung mit ungefähr logarithmischer Verteilung zu erhalten, möchten Sie Ihre Zufallszahl um eine Zufallszahl nach rechts verschieben:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Es ist nicht perfekt, aber viel schneller als das Rechnen pow(2, rand()*scalefactor). Es wird in dem Sinne "klumpig" sein, dass die Verteilung für Zahlen innerhalb eines Faktors 2 gleichmäßig ist (einheitlich für 128 bis 255, die halbe Dichte für 256 bis 1023 usw.).

Hier ist ein Histogramm der Häufigkeit der Zahlen 0 bis 31 (in 1M-Stichproben):

Geben Sie hier die Bildbeschreibung ein

Floris
quelle
Nitpick: Dies fördert sehr kleine Zahlen mehr als man erwarten könnte. Die Wahrscheinlichkeit, eine Null zu bekommen, ist signifikant höher als eine 10.
Mooing Duck
Nun - der springende Punkt dabei ist, kleine Zahlen zu fördern, also bin ich froh, dass es funktioniert! Ich habe eine Monte-Carlo-Simulation durchgeführt, und dies führt zu einem Rückgang der Wahrscheinlichkeit um den Faktor 2, da sich die Zahlen verdoppeln - ähnlich wie bei einer Protokollverteilung. Aktualisierte Antwort mit einem Bild.
Floris
Nein, ich meine, mit rand()>>(rand()&31);würde man intuitiv erwarten, dass 1/32 der Zahlen 32 Bit und 1/32 der Zahlen 31 Bit und 1/32 der Zahlen 30 Bit usw. haben. Aber das ist es Nicht die Ergebnisse, die Sie erhalten, nur etwa 1/64 der Zahlen würden 32 Bit ergeben, während fast die Hälfte 0 sein sollte. Da meine mentale Mathematik nicht mit Ihren Messungen übereinstimmt, muss ich meine eigenen Messungen durchführen, um sie zu ermitteln das raus.
Mooing Duck
2
Ich will damit nicht sagen, dass Ihr Code falsch ist. Es ist wahrscheinlich das, was ich tun würde. Es verdient nur eine Warnung , dass die Ergebnisse nicht ganz verteilt , wie man erwarten könnte.
Mooing Duck
1
Ich denke, das Problem liegt darin, dass man 0 als 1-Bit-Zahl betrachtet ... das ist die Art von Rätsel, auf die man stößt, wenn man ganze Zahlen und Logarithmen mischt. Es war eine gute Übung und du hast mir etwas zum Nachdenken gegeben. "Testen Sie die Grenzen Ihres Algorithmus" - er wird nie alt.
Floris
5

Es gibt genau die gleiche Anzahl von Zahlen zwischen 0 und 2 ^ 29 und 2 ^ 29 und 2 ^ 30.

Eine andere Sichtweise auf das Problem: Betrachten Sie die binäre Darstellung der von Ihnen erzeugten Zufallszahl, die Wahrscheinlichkeit, dass das höchste Bit 1 ist, entspricht 1/2, und daher erhalten Sie in halben Fällen die Ordnung 29. Was Sie wollen, ist eine Zahl zu sehen, die unter 2 ^ 25 liegt, aber das bedeutet, dass 5 höchste Bits alle Null sind, was mit einer geringen Wahrscheinlichkeit von 1/32 geschieht. Es besteht die Möglichkeit, dass selbst wenn Sie es längere Zeit ausführen, die Reihenfolge unter 15 überhaupt nicht angezeigt wird (die Wahrscheinlichkeit ist ungefähr 6 bis 6 Mal hintereinander zu würfeln).

Nun der Teil Ihrer Frage zum Samen. Nein, der Startwert kann möglicherweise nicht den Bereich bestimmen, aus dem die Zahlen generiert werden, sondern nur das erste Anfangselement. Stellen Sie sich rand () als eine Folge aller möglichen Zahlen im Bereich vor (vorgegebene Permutation). Der Startwert bestimmt, wo Sie mit dem Zeichnen von Zahlen aus der Sequenz beginnen. Wenn Sie (Pseudo-) Zufälligkeit wünschen, verwenden Sie daher die aktuelle Zeit, um die Sequenz zu initialisieren: Es ist Ihnen egal, dass die Position, von der Sie ausgehen, nicht gleichmäßig verteilt ist. Alles, was zählt, ist, dass Sie nie von derselben Position aus starten.

Vadim
quelle
2

Verwenden pow(2,rand()) Sie es, um die Antworten in der Reihenfolge der gewünschten Größe zu geben!

Shivendra
quelle
2

Wenn Sie Zufallszahlen aus einem Onlinedienst verwenden möchten, für den Sie wget verwenden können, möchten Sie möglicherweise sehen, dass Sie auch Dienste wie random.org für Ihre Zufallszahlengenerierung verwenden können. Sie können sie mit wget abfangen und dann die Zahlen von lesen die heruntergeladene Datei

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html

Namit Sinha
quelle
Willkommen bei SO. Bitte veröffentlichen Sie keine Links als Antworten. Sie können eine detaillierte Skizze einer Antwort bereitstellen, wobei die Details über Links gelesen werden können.
Shai