it-swarm.com.de

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

Ich muss Zufallszahlen innerhalb eines festgelegten Intervalls ([max; min]) generieren.

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

Aktuell 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

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

81
anand

Warum ist Rand eine schlechte Idee

Die meisten Antworten, die Sie hier erhalten haben, verwenden die Funktion Rand und den Modulusoperator. Diese Methode erzeugt möglicherweise nicht einheitlich Zahlen (hängt vom Bereich und dem Wert von Rand_MAX Ab) und wird daher nicht empfohlen.

C++ 11 und Generierung über einen Bereich

Mit C++ 11 sind mehrere andere Optionen hinzugekommen. 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 Verteilungsarten, einschließlich Bernoulli, Poisson und Normal.

Wie kann ich einen Container mischen?

Der Standard sieht std::shuffle vor, das 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 für den Fall, dass 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.

132
Shoe

[edit] Warnung: Verwenden Sie Rand() nicht für Statistiken, Simulationen, Kryptografien oder andere Zwecke ernst.

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

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


Im Allgemeinen weisen die hohen Bits eine bessere Verteilung auf als die niedrigen Bits. Die empfohlene Methode zum Generieren von Zufallszahlen eines Bereichs für einfache Zwecke lautet daher:

((double) Rand() / (Rand_MAX+1)) * (max-min+1) + min

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

Die Division generiert eine Zufallszahl im Intervall [0, 1); "strecken" Sie dies auf den gewünschten Bereich. Nur wenn max-min + 1 in die Nähe von Rand_MAX kommt, 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 mehr verschlechtern können.


Es wird nicht garantiert, dass der integrierte 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 dessen 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 habe kürzlich festgestellt, dass LFSR ausgezeichnet und verdammt einfach zu implementieren ist seine Eigenschaften sind für Sie in Ordnung.

58
peterchen

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 randutils Bibliothek (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, nicht ausgiebig getestet wurde. Jedenfalls halte ich es für einen Versuch wert, frei (MIT-Lizenz) und nur für Header.

Minimalprobe: 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 der Zufallsgenerierung im Allgemeinen und der C++ - Bibliothek bestimmtes.

Boost.Random

Boost.Random (documentation) ist die Bibliothek, die die <random> von C++ 11 inspiriert hat, mit der sich ein Großteil der Schnittstelle teilt. Während Boost theoretisch auch eine externe Abhängigkeit ist, hat es mittlerweile den Status einer "Quasi-Standard" -Bibliothek, und sein Zufallsmodul könnte als die klassische Wahl für die Erzeugung von Zufallszahlen mit guter Qualität angesehen werden. Es bietet zwei Vorteile gegenüber der C++ 11-Lösung:

  • es ist portabler und benötigt lediglich Compiler-Unterstützung für C++ 03
  • sein random_device verwendet systemspezifische Methoden, um ein qualitativ hochwertiges Seeding zu ermöglichen

Der einzige kleine Fehler ist, dass das Modul, das random_device anbietet, nicht nur Header enthält. Man muss boost_random kompilieren und verknüpfen.

Minimalprobe: 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 das minimale Sample gut funktioniert, sollten echte Programme ein paar Verbesserungen gebrauchen:

  • mache mt19937 zu thread_local: der generator ist ziemlich plump (> 2 kb) und wird besser nicht auf dem stack vergeben
  • seed mt19937 mit 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

Obwohl die <random> - Bibliothek die idiomatischste Lösung ist, bietet sie nicht viel im Austausch für die Komplexität ihrer Schnittstelle, selbst für die Grundbedürfnisse. Der Fehler liegt in std::random_device: Der Standard schreibt keine Mindestqualität für seine Ausgabe vor (solange entropy()0 zurückgibt) und ab 2015 MinGW ( nicht der am häufigsten verwendete Compiler, aber kaum eine esoterische Wahl) wird immer 4 auf dem minimalen Sample ausgeben.

Minimalprobe: 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 fehlerhaft ist, sollte diese Lösung der Boost-Lösung entsprechen, und es gelten dieselben Vorschläge.

Godots Lösung

Minimalprobe: ein Würfelwurf

#include <iostream>
#include <random>

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

Dies ist eine einfache, effektive und saubere Lösung. Die Kompilierung dauert eine Weile - etwa zwei Jahre, vorausgesetzt, C++ 17 wird rechtzeitig veröffentlicht und die experimentelle randint -Funktion wird in den neuen Standard aufgenommen. Vielleicht verbessern sich bis dahin auch die Garantien für die Saatqualität.

Die schlechter-ist-besser Lösung

Minimalprobe: 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, portabel, schnell und ehrlich, in dem Sinne, dass bekannt ist, dass die Zufallszahlen, die man bekommt, kaum anständig sind und man daher nicht versucht ist, sie für ernsthafte Zwecke zu verwenden.

Die Accounting-Troll-Lösung

Minimalprobe: ein Würfelwurf

#include <iostream>

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

Während 9 ein ungewöhnliches Ergebnis für einen normalen Würfelwurf ist, muss man die exzellente Kombination guter Eigenschaften in dieser Lösung bewundern, die es schafft, die schnellste, einfachste, cachefreundlichste und portabelste zu sein. Durch Ersetzen von 9 durch 4 erhält man einen perfekten Generator für alle Arten von Dungeons und Dragons, wobei die symbolreichen Werte 1, 2 und 3 immer noch vermieden werden. Dieses Programm erzeugt tatsächlich undefiniertes Verhalten.

14
Alberto M

Wenn Rand_MAX 32767 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();
}
11
Mark Ransom

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

uniform_int sollte machen was du willst.

9
Jeff Thomas

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

Sie können auch einen eigenen Verschlüsselungsalgorithmus verwenden (wie AES ). Indem Sie einen Startwert und ein IV auswählen und dann die Ausgabe der Verschlüsselungsfunktion kontinuierlich neu verschlüsseln. Die Verwendung von OpenSSL ist einfacher, aber weniger männlich.

8
SoapBox

Sie sollten sich Rand_MAX für Ihren speziellen Compiler/Ihre spezielle Umgebung ansehen. Ich denke, Sie würden diese Ergebnisse sehen, wenn Rand () eine zufällige 16-Bit-Zahl erzeugt. (Sie nehmen anscheinend an, dass es sich um eine 32-Bit-Zahl handelt).

Ich kann nicht versprechen, dass dies die Antwort ist, aber bitte posten Sie Ihren Wert von Rand_MAX und ein wenig mehr Details zu Ihrer Umgebung.

5
abelenky

Überprüfen Sie, was Rand_MAX auf Ihrem System hat - ich schätze, es sind nur 16 Bit, und Ihr Bereich ist zu groß dafür.

Darüber hinaus lesen Sie diese Diskussion über: Generieren von Zufallszahlen innerhalb eines gewünschten Bereichs und die Hinweise zur Verwendung (oder Nichtverwendung) der C Rand () -Funktion .

3
Rob Walker

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();
}
}

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

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

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 Rand () wahrscheinlich nicht verwenden, da es nicht sehr gut darin ist, Zufallszahlen zu generieren. Ich weiß nicht, auf welcher Plattform Sie laufen, aber es gibt wahrscheinlich eine bessere Funktion, die Sie wie random () aufrufen können.

2
Jason Coco

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 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.

1
benf

Eine kleine Stichprobe von Zufallszahlen muss naturgemäß nicht gleichmäßig verteilt sein. Immerhin sind sie zufällig. Ich stimme zu, dass, wenn ein Zufallsgenerator Zahlen generiert, die konsistent gruppiert zu sein scheinen, wahrscheinlich etwas daran falsch ist.

Beachten Sie jedoch, dass die Zufälligkeit nicht unbedingt einheitlich ist.

Bearbeiten: Ich habe "kleine Probe" hinzugefügt, um zu klären.

0
Kluge

Die Lösung von man 3 Rand für eine Zahl zwischen 1 und 10 ist:

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 Gleichförmigkeit, wie einige andere Nachrichten zeigen, aber dies ist für die meisten Fälle ausreichend.

0
calandoa

Der folgende Code gibt Ihnen natürlich 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.

0

@ Lösung ((double) Rand() / (Rand_MAX+1)) * (max-min+1) + min

Warnung : Vergessen Sie nicht, dass aufgrund von Dehnungs- und möglichen Präzisionsfehlern (auch wenn Rand_MAX groß genug wäre) nur gleichmäßig verteilte Ergebnisse generiert werden können "Bins" und nicht alle Zahlen in [min, max].


@ Lösung: Bigrand

Warnung : Beachten Sie, dass dies die Bits verdoppelt, aber im Allgemeinen nicht alle Zahlen in Ihrem Bereich generieren kann, dh nicht unbedingt true, dass BigRand () alle Zahlen innerhalb seines Bereichs generiert.


Info : Ihr Ansatz (Modulo) ist "fein", solange der Bereich von Rand () Ihren Intervallbereich überschreitet und Rand () "gleichmäßig" ist. . Der Fehler für höchstens die ersten Max-Min-Zahlen ist 1/(Rand_MAX +1).

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

0
Ritualmaster