Generieren Sie Zufallszahlen gleichmäßig über einen gesamten Bereich

91

Ich muss innerhalb eines bestimmten Intervalls [max; min] Zufallszahlen generieren.

Außerdem sollten die Zufallszahlen gleichmäßig über das Intervall verteilt sein und sich nicht an einem bestimmten Punkt befinden.

Derzeit generiere ich als:

for(int i=0; i<6; i++)
{
    DWORD random = rand()%(max-min+1) + min;
}

Aus meinen Tests werden Zufallszahlen nur um einen Punkt generiert.

Example
min = 3604607;
max = 7654607;

Zufallszahlen generiert:

3631594
3609293
3630000
3628441
3636376
3621404

Aus den Antworten unten: OK, RAND_MAX ist 32767. Ich bin auf einer C ++ Windows-Plattform. Gibt es eine andere Methode, um Zufallszahlen mit einer gleichmäßigen Verteilung zu generieren?

anand
quelle
2
Erstellen Sie eine Dice-O-Matic: gamesbyemail.com/News/DiceOMatic
Jarett Millard
1
Ich hatte keine Ahnung, dass C ++ rand()einheitlich ist. Welche Bibliothek benutzt du? cstdlib.h's rand()ist NICHT einheitlich: cplusplus.com/reference/cstdlib/rand
Mike Warren
3
Nein, rand () ist einheitlich (außer in einigen frühen fehlerhaften Implementierungen). Was nicht einheitlich ist, ist die Verwendung des Moduloperators '%', um den Bereich einzuschränken. Siehe stackoverflow.com/questions/2999075/... für eine angemessene Lösung, oder wenn Sie ‚arc4random_uniform‘ zur Verfügung haben , dann können Sie verwenden , sowohl direkt als auch.
John Meacham
@ Alien01: Würden Sie in Betracht ziehen, die akzeptierte Antwort in die von "Schuh" zu ändern ("Warum ist Rand eine schlechte Idee" usw.)? Meine Antwort ist wirklich veraltet und jedes Mal, wenn ich eine Gegenstimme dafür bekomme, habe ich das Gefühl, dass jemand den falschen Gang hinunterläuft.
Peterchen
Nettes Whitepaper über Zufall in C ++ 11.
Pupsik

Antworten:

149

Warum randist eine schlechte Idee

Die meisten Antworten, die Sie hier erhalten haben, verwenden die randFunktion und den Moduloperator. Diese Methode generiert möglicherweise keine einheitlichen Zahlen (dies hängt vom Bereich und dem Wert von ab RAND_MAX) und wird daher nicht empfohlen.

C ++ 11 und Generierung über einen Bereich

Mit C ++ 11 sind mehrere andere Optionen gestiegen. Eine davon entspricht Ihren Anforderungen, um eine Zufallszahl in einem Bereich zu generieren std::uniform_int_distribution. Hier ist ein Beispiel:

const int range_from  = 0;
const int range_to    = 10;
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<int>  distr(range_from, range_to);

std::cout << distr(generator) << '\n';

Und hier ist das laufende Beispiel.

Andere Zufallsgeneratoren

Der <random>Header bietet unzählige andere Zufallszahlengeneratoren mit unterschiedlichen Verteilungen, einschließlich Bernoulli, Poisson und Normal.

Wie kann ich einen Container mischen?

Der Standard sieht vor std::shuffle, dass wie folgt verwendet werden kann:

std::vector<int> vec = {4, 8, 15, 16, 23, 42};

std::random_device random_dev;
std::mt19937       generator(random_dev());

std::shuffle(vec.begin(), vec.end(), generator);

Der Algorithmus ordnet die Elemente zufällig mit einer linearen Komplexität neu.

Boost.Random

Eine andere Alternative, falls Sie keinen Zugriff auf einen C ++ 11 + -Compiler haben, ist die Verwendung von Boost.Random . Die Benutzeroberfläche ist der von C ++ 11 sehr ähnlich.

Schuh
quelle
21
Achten Sie auf diese Antwort, da sie weitaus moderner ist.
Gsamaras
Das ist die richtige Antwort. Vielen Dank! Trotzdem würde ich gerne eine ausführlichere Beschreibung jedes Schritts dieses Codes sehen. ZB was ist ein mt19937Typ?
Apollo
@Apollo In der Dokumentation steht "32-Bit-Mersenne Twister von Matsumoto und Nishimura, 1998". Ich gehe davon aus, dass es ein Algorithmus ist, um Pseudozufallszahlen zu generieren.
Schuh
@Shoe generiert für einen bestimmten Bereich Zahlen in derselben Reihenfolge 1 9 6 2 8 7 1 4 7 7. Wie können Sie dies jedes Mal randomisieren, wenn wir das Programm ausführen?
1
@ Richard Was ist die Alternative?
Schuh
59

[Bearbeiten] Warnung: Nicht rand()für Statistiken, Simulationen, Kryptografie oder andere schwerwiegende Zwecke verwenden .

Es ist gut genug, um Zahlen für einen typischen Menschen in Eile zufällig aussehen zu lassen , nicht mehr.

Siehe @ Jefffreys Antwort für bessere Optionen oder diese Antwort für kryptosichere Zufallszahlen.


Im Allgemeinen zeigen die hohen Bits eine bessere Verteilung als die niedrigen Bits. Daher wird empfohlen, Zufallszahlen eines Bereichs für einfache Zwecke zu generieren:

((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Hinweis : Stellen Sie sicher, dass RAND_MAX + 1 nicht überläuft (danke Demi)!

Die Division erzeugt eine Zufallszahl im Intervall [0, 1); "dehnen" Sie dies auf den gewünschten Bereich. Nur wenn sich max-min + 1 RAND_MAX nähert, benötigen Sie eine "BigRand ()" - Funktion, wie sie von Mark Ransom veröffentlicht wurde.

Dies vermeidet auch einige Schnittprobleme aufgrund des Modulos, die Ihre Zahlen noch weiter verschlechtern können.


Es ist nicht garantiert, dass der eingebaute Zufallszahlengenerator die für statistische Simulationen erforderliche Qualität aufweist. Es ist in Ordnung, dass Zahlen für einen Menschen "zufällig" aussehen, aber für eine ernsthafte Anwendung sollten Sie etwas Besseres nehmen - oder zumindest seine Eigenschaften überprüfen (eine gleichmäßige Verteilung ist normalerweise gut, aber die Werte korrelieren tendenziell und die Reihenfolge ist deterministisch ). Knuth hat eine ausgezeichnete (wenn auch schwer zu lesende) Abhandlung über Zufallszahlengeneratoren, und ich fand kürzlich, dass LFSR ausgezeichnet und verdammt einfach zu implementieren ist, da seine Eigenschaften für Sie in Ordnung sind.

Peterchen
quelle
4
BigRand kann bessere Ergebnisse liefern, selbst wenn der gewünschte Bereich RAND_MAX nicht überschreitet. Überlegen Sie, wann RAND_MAX 32767 ist und Sie 32767 mögliche Werte wünschen - zwei dieser 32768-Zufallszahlen (einschließlich Null) werden derselben Ausgabe zugeordnet und treten doppelt so häufig auf wie die anderen. Kaum eine ideale zufällige Eigenschaft!
Mark Ransom
7
(RAND_MAX + 1) ist eine schlechte Idee. Dies kann zu einem Überschlag führen und einen negativen Wert ergeben. Besser etwas zu tun wie: ((doppelt) RAND_MAX) + 1.0
Demi
3
@ Peterchen: Ich denke du hast falsch verstanden was Demi gesagt hat. Sie meinte dies: ( rand() / ((double)RAND_MAX+1)) * (max-min+1) + min Verschieben Sie die Konvertierung einfach auf doppelt und vermeiden Sie das Problem.
Mooing Duck
3
Außerdem ändert dies lediglich die Verteilung von den unteren 32767-Werten im Bereich zu gleichmäßig verteilten 32767-Werten im Bereich, und die verbleibenden 4017233-Werte werden von diesem Algorithmus niemals ausgewählt.
Mooing Duck
1
Die gegebene Antwort ist um 1 versetzt. Die richtige Gleichung lautet: ((doppelt) rand () / (RAND_MAX + 1.0)) * (max-min) + min Das "max-min + 1" wird verwendet, wenn% nicht verwendet wird * . Sie werden sehen, warum, wenn Sie min = 0, max = 1 tun. Könnte Peterchen oder @ Peter-Mortensen es ändern.
Davepc
17

Ich möchte die hervorragenden Antworten von Angry Shoe und Peterchen mit einem kurzen Überblick über den Stand der Technik im Jahr 2015 ergänzen:

Einige gute Entscheidungen

randutils

Die randutilsBibliothek (Präsentation) ist eine interessante Neuheit, die eine einfache Oberfläche und (deklarierte) robuste Zufallsfunktionen bietet. Es hat die Nachteile, dass es eine Abhängigkeit von Ihrem Projekt hinzufügt, und da es neu ist, wurde es nicht ausführlich getestet. Da ich kostenlos (MIT-Lizenz) und nur als Header bin, denke ich, dass es einen Versuch wert ist.

Minimale Probe: ein Würfelwurf

#include <iostream>
#include "randutils.hpp"
int main() {
    randutils::mt19937_rng rng;
    std::cout << rng.uniform(1,6) << "\n";
}

Auch wenn man sich nicht für die Bibliothek interessiert, bietet die Website ( http://www.pcg-random.org/ ) viele interessante Artikel zum Thema Zufallszahlengenerierung im Allgemeinen und zur C ++ - Bibliothek im Besonderen.

Boost.Random

Boost.Random (Dokumentation) ist die Bibliothek , die inspiriert C++11s‘ <random>, mit denen Aktien viel von der Schnittstelle. Während es theoretisch auch eine externe Abhängigkeit ist, Boosthat es mittlerweile den Status einer "Quasi-Standard" -Bibliothek, und sein RandomModul könnte als die klassische Wahl für die Erzeugung von Zufallszahlen guter Qualität angesehen werden. Es bietet zwei Vorteile in Bezug auf die C++11Lösung:

  • Es ist portabler und benötigt nur Compiler-Unterstützung für C ++ 03
  • Es random_deviceverwendet systemspezifische Methoden, um eine Aussaat von guter Qualität zu ermöglichen

Der einzige kleine Fehler ist, dass das Modulangebot random_devicenicht nur Header ist, sondern kompiliert und verknüpft werden muss boost_random.

Minimale Probe: ein Würfelwurf

#include <iostream>
#include <boost/random.hpp>
#include <boost/nondet_random.hpp>

int main() {
    boost::random::random_device                  rand_dev;
    boost::random::mt19937                        generator(rand_dev());
    boost::random::uniform_int_distribution<>     distr(1, 6);

    std::cout << distr(generator) << '\n';
}

Während die minimale Stichprobe gut funktioniert, sollten echte Programme zwei Verbesserungen verwenden:

  • make mt19937a thread_local: Der Generator ist ziemlich prall (> 2 KB) und wird besser nicht auf dem Stapel zugeordnet
  • Samen mt19937mit mehr als einer ganzen Zahl: Der Mersenne Twister hat einen großen Zustand und kann während der Initialisierung von mehr Entropie profitieren

Einige nicht so gute Entscheidungen

Die C ++ 11-Bibliothek

Die <random>Bibliothek ist zwar die idiomatischste Lösung, bietet aber nicht viel im Austausch für die Komplexität ihrer Schnittstelle, selbst für die Grundbedürfnisse. Der Fehler liegt darin, dass std::random_deviceder Standard keine minimale Qualität für seine Ausgabe vorschreibt (solange entropy()zurückgegeben wird 0), und ab 2015 druckt MinGW (nicht der am häufigsten verwendete Compiler, aber kaum eine esoterische Wahl) immer 4auf dem minimalen Sample.

Minimale Probe: ein Würfelwurf

#include <iostream>
#include <random>
int main() {
    std::random_device                  rand_dev;
    std::mt19937                        generator(rand_dev());
    std::uniform_int_distribution<int>  distr(1, 6);

    std::cout << distr(generator) << '\n';
}

Wenn die Implementierung nicht schlecht ist, sollte diese Lösung der Boost-Lösung entsprechen, und es gelten dieselben Vorschläge.

Godots Lösung

Minimale Probe: ein Würfelwurf

#include <iostream>
#include <random>

int main() {
    std::cout << std::randint(1,6);
}

Dies ist eine einfache, effektive und saubere Lösung. Nur ein Fehler, die Kompilierung wird eine Weile dauern - ungefähr zwei Jahre, vorausgesetzt, C ++ 17 wird rechtzeitig veröffentlicht und die experimentelle randintFunktion wird in den neuen Standard aufgenommen. Vielleicht verbessern sich bis dahin auch die Garantien für die Aussaatqualität.

Die schlechter-ist-besser- Lösung

Minimale Probe: ein Würfelwurf

#include <cstdlib>
#include <ctime>
#include <iostream>

int main() {
    std::srand(std::time(nullptr));
    std::cout << (std::rand() % 6 + 1);
}

Die alte C-Lösung wird aus guten Gründen als schädlich angesehen (siehe die anderen Antworten hier oder diese detaillierte Analyse ). Dennoch hat es seine Vorteile: Es ist einfach, tragbar, schnell und ehrlich, in dem Sinne, dass bekannt ist, dass die Zufallszahlen, die man erhält, kaum anständig sind und man daher nicht versucht ist, sie für ernsthafte Zwecke zu verwenden.

Die Buchhaltungstrollösung

Minimale Probe: ein Würfelwurf

#include <iostream>

int main() {
    std::cout << 9;   // http://dilbert.com/strip/2001-10-25
}

Während 9 ein etwas ungewöhnliches Ergebnis für einen normalen Würfelwurf ist, muss man die hervorragende Kombination guter Eigenschaften in dieser Lösung bewundern, die es schafft, die schnellste, einfachste, cachefreundlichste und tragbarste zu sein. Wenn man 9 durch 4 ersetzt, erhält man einen perfekten Generator für jede Art von Dungeons and Dragons-Würfel, wobei die symbolbeladenen Werte 1, 2 und 3 vermieden werden. Der einzige kleine Fehler ist, dass aufgrund der schlechten Laune von Dilberts Buchhaltungstrollen Dieses Programm erzeugt tatsächlich undefiniertes Verhalten.

Alberto M.
quelle
Die randutilsBibliothek heißt jetzt PCG.
tay10r
11

Wenn RAND_MAX32767 ist, können Sie die Anzahl der Bits leicht verdoppeln.

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}
Mark Ransom
quelle
Ich denke nicht, dass das funktioniert. Pseudozufallszahlengeneratoren sind typischerweise deterministisch. Wenn zum Beispiel der erste randAnruf zurückkehrt 0x1234und der zweite 0x5678, erhalten Sie 0x12345678. Dies ist die einzige Nummer, mit der Sie beginnen können 0x1234, da die nächste Nummer immer sein wird 0x5678. Sie erhalten 32-Bit-Ergebnisse, haben aber nur 32768 mögliche Zahlen.
user694733
@ user694733 Ein guter Zufallszahlengenerator hat eine Periode, die größer ist als die Anzahl der Ausgänge, die er generieren kann, sodass auf 0x1234 nicht immer 0x5678 folgt.
Mark Ransom
9

Wenn Sie in der Lage sind, verwenden Sie Boost . Ich hatte viel Glück mit ihrer zufälligen Bibliothek .

uniform_int sollte tun was du willst.

Jeff Thomas
quelle
Ich habe einige Arbeiten an uniform_int mit einem Merseinne-Twister durchgeführt und leider sind die von uniform_int zurückgegebenen Werte für bestimmte Bereiche nicht so einheitlich, wie ich es erwarten würde. Zum Beispiel neigt uniform_int <> (0, 3) dazu, mehr
Nullen
@ ScaryAardvark, das klingt nach einer schlechten Implementierung von uniform_intdamals. Es ist ziemlich einfach, eine unvoreingenommene Ausgabe zu generieren. Hier gab es mehrere Fragen, die die Methode demonstrieren.
Mark Ransom
@ Mark Lösegeld. Ja, ich würde vollkommen zustimmen.
ScaryAardvark
8

Wenn Sie sich Gedanken über Zufälligkeit und nicht über Geschwindigkeit machen, sollten Sie eine sichere Methode zur Generierung von Zufallszahlen verwenden. Es gibt verschiedene Möglichkeiten, dies zu tun ... Die einfachste ist die Verwendung des Zufallszahlengenerators von OpenSSL .

Sie können auch Ihre eigenen mit einem Verschlüsselungsalgorithmus (wie AES ) schreiben . Durch Auswahl eines Seeds und einer IV und anschließende kontinuierliche Verschlüsselung der Ausgabe der Verschlüsselungsfunktion. Die Verwendung von OpenSSL ist einfacher, aber weniger männlich.

Seifenkiste
quelle
Ich kann keine Bibliothek von Drittanbietern verwenden. Ich bin nur auf C ++ beschränkt.
13.
Gehen Sie dann den männlichen Weg, implementieren Sie AES oder einen anderen Verschlüsselungsalgorithmus.
SoapBox
2
RC4 ist trivial zu codieren und für alle praktischen Zwecke zufällig genug (außer WEP, aber das ist nicht ganz RC4s Fehler). Ich meine es ernst, es ist unglaublich trivialer Code. Wie 20 Zeilen oder so. Der Wikipedia-Eintrag enthält Pseudocode.
Steve Jessop
4
Warum können Sie keinen Code von Drittanbietern verwenden? Wenn dies eine Hausaufgabenfrage ist, sollten Sie dies sagen, da viele Leute in diesem Fall lieber hilfreiche Hinweise geben würden, als vollständige Lösungen bereitzustellen. Wenn es keine Hausaufgabe ist, treten Sie den Typen, der "kein Code von Drittanbietern" sagt, weil er ein Idiot ist.
DevSolar
Direkterer Link zu den OpenSSL rand () - Funktionsdokumenten: openssl.org/docs/crypto/rand.html#
DevSolar
5

Sie sollten nach RAND_MAXIhrem speziellen Compiler / Ihrer Umgebung suchen . Ich denke, Sie würden diese Ergebnisse sehen, wenn Sie rand()eine zufällige 16-Bit-Zahl erzeugen. (Sie scheinen davon auszugehen, dass es sich um eine 32-Bit-Zahl handelt).

Ich kann nicht versprechen, dass dies die Antwort ist, aber bitte geben Sie Ihren Wert RAND_MAXund ein wenig mehr Details zu Ihrer Umgebung an.

abelenky
quelle
3

Überprüfen Sie, was RAND_MAXsich auf Ihrem System befindet - ich vermute, es sind nur 16 Bit, und Ihre Reichweite ist zu groß dafür.

Darüber hinaus finden Sie in dieser Diskussion Folgendes : Generieren zufälliger Ganzzahlen innerhalb eines gewünschten Bereichs und Hinweise zur Verwendung (oder nicht) der Funktion C rand () .

Rob Walker
quelle
Ok, RAND_MAX ist 32767. Ich bin auf einer C ++ - Windows-Plattform. Gibt es eine andere Methode, um Zufallszahlen mit gleichmäßiger Verteilung zu generieren?
13.
2

Dies ist nicht der Code, aber diese Logik kann Ihnen helfen.

static double rnd(void)
{
   return (1.0 / (RAND_MAX + 1.0) * ((double)(rand())) );
}

static void InitBetterRnd(unsigned int seed)
{
    register int i;
    srand( seed );
    for( i = 0; i < POOLSIZE; i++){
        pool[i] = rnd();
    }
}

 // This function returns a number between 0 and 1
 static double rnd0_1(void)
 {
    static int i = POOLSIZE-1;
    double r;

    i = (int)(POOLSIZE*pool[i]);
    r = pool[i];
    pool[i] = rnd();
    return (r);
}
user3503711
quelle
2

Wenn Sie möchten, dass die Zahlen gleichmäßig über den Bereich verteilt sind, sollten Sie Ihren Bereich in mehrere gleiche Abschnitte aufteilen, die die Anzahl der benötigten Punkte darstellen. Dann erhalten Sie eine Zufallszahl mit einem Min / Max für jeden Abschnitt.

Als weitere Anmerkung sollten Sie wahrscheinlich nicht verwenden, rand()da es nicht sehr gut ist, Zufallszahlen zu generieren. Ich weiß nicht, auf welcher Plattform Sie laufen, aber es gibt wahrscheinlich eine bessere Funktion, die Sie aufrufen können random().

Jason Coco
quelle
1

Dies sollte eine gleichmäßige Verteilung über den Bereich [low, high)ohne Verwendung von Floats ermöglichen, solange der Gesamtbereich kleiner als RAND_MAX ist.

uint32_t rand_range_low(uint32_t low, uint32_t high)
{
    uint32_t val;
    // only for 0 < range <= RAND_MAX
    assert(low < high);
    assert(high - low <= RAND_MAX);

    uint32_t range = high-low;
    uint32_t scale = RAND_MAX/range;
    do {
        val = rand();
    } while (val >= scale * range); // since scale is truncated, pick a new val until it's lower than scale*range
    return val/scale + low;
}

und für Werte größer als RAND_MAX möchten Sie so etwas wie

uint32_t rand_range(uint32_t low, uint32_t high)
{
    assert(high>low);
    uint32_t val;
    uint32_t range = high-low;
    if (range < RAND_MAX)
        return rand_range_low(low, high);
    uint32_t scale = range/RAND_MAX;
    do {
        val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval
    } while (val >= range);
    return val + low;
}

Dies ist ungefähr die Vorgehensweise von std :: uniform_int_distribution.

benf
quelle
0

Eine kleine Stichprobe von Zufallszahlen muss naturgemäß nicht gleichmäßig verteilt sein. Sie sind schließlich zufällig. Ich bin damit einverstanden, dass, wenn ein Zufallszahlengenerator Zahlen generiert, die konsistent gruppiert zu sein scheinen, wahrscheinlich etwas nicht stimmt.

Aber denken Sie daran, dass Zufälligkeit nicht unbedingt einheitlich ist.

Bearbeiten: Ich habe "kleine Probe" hinzugefügt, um dies zu verdeutlichen.

Kluge
quelle
"gleichmäßig verteilt" hat eine genau definierte Bedeutung, und die Standard-Zufallsgeneratoren kommen normalerweise nahe.
Peterchen
Ja, Sie haben Recht, Zufallszahlengeneratoren sollten eine Ausgabe erzeugen, deren Verteilung im Laufe der Zeit im Allgemeinen gleichmäßig ist. Ich denke, mein Punkt ist, dass über eine kleine Anzahl von Instanzen (6, wie im Beispiel gezeigt) die Ausgabe nicht immer einheitlich ist.
Kluge
Kluge hat recht. Eine gleichmäßige Verteilung in einer kleinen Stichprobe zeigt an, dass die Stichprobe definitiv nicht zufällig ist.
Bill the Lizard
1
Bill, es zeigt nichts dergleichen an. Kleine Stichproben sind meistens bedeutungslos, aber wenn das RNG einheitlich sein soll und die Ausgabe einheitlich ist, warum ist das schlechter als eine ungleichmäßige kleine Stichprobe?
Dan Dyer
2
Signifikante Verteilungen in beiden Richtungen weisen auf Nicht-Zufälligkeit hin: Ich denke, Bill bedeutet nur, dass 6 Ergebnisse mit gleichem Abstand ebenfalls verdächtig wären. Im OP liegen 6 Werte in einem Bereich von 32 k / 4 M oder <1% des gewünschten Bereichs. Die Wahrscheinlichkeit, dass dies falsch positiv ist, ist zu gering, um darüber zu streiten.
Steve Jessop
0

Die von Mann 3 Rand gegebene Lösung für eine Zahl zwischen 1 und einschließlich 10 lautet:

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

In Ihrem Fall wäre es:

j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));

Natürlich ist dies keine perfekte Zufälligkeit oder Einheitlichkeit, wie einige andere Nachrichten hervorheben, aber dies reicht für die meisten Fälle aus.

Calandoa
quelle
1
Dies ordnet lediglich die Verteilung so um, dass sie gleichmäßiger erscheint , aber es ist nicht einmal mehr für große Bereiche (wie im Fall des OP)
Mooing Duck
0

@Lösung ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Warnung : Vergessen Sie nicht, dass Sie aufgrund von Dehnungen und möglichen Präzisionsfehlern (selbst wenn RAND_MAX groß genug wäre) nur gleichmäßig verteilte "Bins" und nicht alle Zahlen in [min, max] generieren können.


@ Lösung: Bigrand

Warnung : Beachten Sie, dass dies die Bits verdoppelt, aber im Allgemeinen immer noch nicht alle Zahlen in Ihrem Bereich generieren kann, dh es ist nicht unbedingt richtig, dass BigRand () alle Zahlen in seinem Bereich generiert.


Info : Ihr Ansatz (Modulo) ist "in Ordnung", solange der Bereich von rand () Ihren Intervallbereich überschreitet und rand () "einheitlich" ist. Der Fehler für höchstens die ersten max - min Zahlen ist 1 / (RAND_MAX +1).

Außerdem schlage ich vor, auch in C ++ 11 auf das neue Zufallspaket e umzusteigen, das bessere und vielfältigere Implementierungen bietet als rand ().

Ritualmeister
quelle
0

Dies ist die Lösung, die ich mir ausgedacht habe:

#include "<stdlib.h>"

int32_t RandomRange(int32_t min, int32_t max) {
    return (rand() * (max - min + 1) / (RAND_MAX + 1)) + min;
}

Dies ist eine Bucket-Lösung, die konzeptionell den Lösungen ähnelt, rand() / RAND_MAXmit denen ein Gleitkommabereich zwischen 0 und 1 ermittelt und dann in einen Bucket gerundet wird. Es wird jedoch eine rein ganzzahlige Mathematik verwendet und der ganzzahlige Teilungsboden verwendet, um den Wert auf den nächsten Bucket abzurunden.

Es werden einige Annahmen getroffen. Erstens wird davon ausgegangen, dass dies RAND_MAX * (max - min + 1)immer in eine passt int32_t. Wenn RAND_MAX32767 und 32-Bit-Int-Berechnungen verwendet werden, können Sie maximal 32767 verwenden. Wenn Ihre Implementierung einen viel größeren RAND_MAX hat, können Sie dies überwinden, indem Sie eine größere Ganzzahl (wie int64_t) für die Berechnung verwenden. Zweitens, wenn verwendet int64_twird, aber RAND_MAXimmer noch 32767 ist, werden in Bereichen, die größer sind als RAND_MAXSie, "Löcher" in den möglichen Ausgangsnummern bekommen. Dies ist wahrscheinlich das größte Problem bei Lösungen, die aus der Skalierung abgeleitet werden rand().

Das Testen über eine große Anzahl von Iterationen zeigt jedoch, dass diese Methode für kleine Bereiche sehr einheitlich ist. Es ist jedoch möglich (und wahrscheinlich), dass dies mathematisch gesehen eine geringe Verzerrung aufweist und möglicherweise Probleme entwickelt, wenn sich der Bereich nähert RAND_MAX. Testen Sie es selbst und entscheiden Sie, ob es Ihren Anforderungen entspricht.

Ryan
quelle
-1

Natürlich gibt Ihnen der folgende Code keine Zufallszahlen, sondern Pseudozufallszahlen. Verwenden Sie den folgenden Code

#define QUICK_RAND(m,n) m + ( std::rand() % ( (n) - (m) + 1 ) )

Beispielsweise:

int myRand = QUICK_RAND(10, 20);

Sie müssen anrufen

srand(time(0));  // Initialize random number generator.

Andernfalls sind die Zahlen nicht nahezu zufällig.

Michael Haephrati
quelle
1
Die Frage ist nach einer gleichmäßigen Verteilung. Diese vorgeschlagene Lösung führt nicht zu einer gleichmäßigen Verteilung. Die Standard C ++ Library verfügt über Funktionen zur Erzeugung von Pseudozufallszahlen . Diejenigen , tun eine gleichmäßige Verteilung, wenn gewünscht.
Unsichtbarer
-3

Ich habe das gerade im Internet gefunden. Das sollte funktionieren:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));
anand
quelle
Bitte klären Sie, wofür Sie sie benötigen. Es gibt unzählige Algorithmen für PRNGs. Es wäre auch einfacher, wenn Sie Ihre Hauptfrage bearbeiten, anstatt Antworten zu veröffentlichen.
Peterchen
Das funktioniert am besten für mich ... Ich bin in der Lage, besser verteilte Zufallszahlen mit dieser Formel zu erhalten ..
14.
4
Wenn Ihr Bereich RAND_MAX überschreitet, sind die Ergebnisse möglicherweise nicht einheitlich. Das heißt, es gibt Werte im Bereich, die nicht dargestellt werden, egal wie oft Ihre Funktion aufgerufen wird.
dmckee --- Ex-Moderator Kätzchen
4
Wenn sowohl max als auch min int ohne Vorzeichen sind und min 0 und max MAX_UINT ist, ist ((max) - (min) +1) 0 und das Ergebnis ist immer 0. Achten Sie bei dieser Art von Mathematik auf einen Überlauf! Wie von dmckee festgestellt, wird die Verteilung über den Zielbereich gestreckt, es werden jedoch nicht mehr als eindeutige RAND_MAX-Werte garantiert.
Jesus