70 Stimmen

Wie erzeugt man in C++ einen zufälligen Double, der gleichmäßig zwischen 0 und 1 verteilt ist?

Wie erzeugt man in C++ einen zufälligen Double, der gleichmäßig zwischen 0 und 1 verteilt ist?

Natürlich fallen mir einige Antworten ein, aber ich würde gerne wissen, was die gängige Praxis ist, die man hat:

  • Gute Einhaltung der Normen
  • Gute Zufälligkeit
  • Gute Geschwindigkeit

(Geschwindigkeit ist für meine Anwendung wichtiger als Zufälligkeit).

Herzlichen Dank!

PS: Falls das eine Rolle spielt, meine Zielplattformen sind Linux und Windows.

3voto

suszterpatt Punkte 8097

Wenn Geschwindigkeit Ihr Hauptanliegen ist, dann würde ich mich einfach für die

double r = (double)rand() / (double)RAND_MAX;

3voto

Johnny Cage Punkte 508

Meines Erachtens gibt es drei Möglichkeiten, dies zu tun,

1) Der einfache Weg.

double rand_easy(void)
{       return (double) rand() / (RAND_MAX + 1.0);
}

2) Der sichere Weg (standardkonform).

double rand_safe(void)
{
        double limit = pow(2.0, DBL_MANT_DIG);
        double denom = RAND_MAX + 1.0;
        double denom_to_k = 1.0;
        double numer = 0.0;

        for ( ; denom_to_k < limit; denom_to_k *= denom )
           numer += rand() * denom_to_k;

        double result = numer / denom_to_k;
        if (result == 1.0)
           result -= DBL_EPSILON/2;
        assert(result != 1.0);
        return result;
}

3) Der individuelle Weg.

Durch die Eliminierung rand() müssen wir uns nicht mehr um die Eigenheiten einer bestimmten Version kümmern, was uns mehr Spielraum für unsere eigene Implementierung gibt.

Anmerkung: Die Periode des hier verwendeten Generators beträgt ≅ 1,8e+19.

#define RANDMAX (-1ULL)
uint64_t custom_lcg(uint_fast64_t* next)
{       return *next = *next * 2862933555777941757ULL + 3037000493ULL;
}

uint_fast64_t internal_next;
void seed_fast(uint64_t seed)
{       internal_next = seed;
}

double rand_fast(void)
{
#define SHR_BIT (64 - (DBL_MANT_DIG-1))
        union {
            double f; uint64_t i;
        } u;
        u.f = 1.0;
        u.i = u.i | (custom_lcg(&internal_next) >> SHR_BIT);
        return u.f - 1.0;
}

Unabhängig von der Wahl kann die Funktionalität wie folgt erweitert werden,

double rand_dist(double min, double max)
{       return rand_fast() * (max - min) + min;
}

double rand_open(void)
{       return rand_dist(DBL_EPSILON, 1.0);
}

double rand_closed(void)
{       return rand_dist(0.0, 1.0 + DBL_EPSILON);
}

Abschließende Anmerkungen: Die schnelle Version - obwohl in C geschrieben - kann für die Verwendung in C++ angepasst werden, um als Ersatz für std::generate_canonical und funktioniert für jeden Generator, der Werte mit ausreichend signifikanten Bits ausgibt.

Die meisten 64-Bit-Generatoren nutzen die Vorteile ihrer vollen Breite, so dass dies wahrscheinlich ohne Modifikation (Shift-Anpassung) verwendet werden kann. z.B. funktioniert dies so wie es ist mit dem std::mt19937_64 Motor.

2voto

Amarjit Datta Punkte 251

Zuerst include stdlib.h

#include<stdlib.h>

Dann kann die folgende Funktion eine zufällige doppelte Zahl zwischen einem Bereich in der Programmiersprache C erzeugen.

double randomDouble() {
    double lowerRange = 1.0;
    double upperRange = 10.0;
    return ((double)rand() * (upperRange - lowerRange)) / (double)RAND_MAX + lowerRange;
}

Hier ist RAND_MAX in stdlib.h definiert

0voto

Ryan Punkte 557

Sie könnten den Mersenne-Twister-Algorithmus ausprobieren.

http://en.wikipedia.org/wiki/Mersenne_twister

Es hat eine gute Mischung aus Geschwindigkeit und Zufälligkeit und eine GPL-Implementierung.

0voto

Abhay Punkte 6851

Nun, wenn man Einfachheit und Geschwindigkeit als Hauptkriterien betrachtet, kann man eine kleine generische Hilfe wie diese hinzufügen :-

  // C++ rand generates random numbers between 0 and RAND_MAX. This is quite a big range
  // Normally one would want the generated random number within a range to be really
  // useful. So the arguments have default values which can be overridden by the caller
  int nextRandomNum(int low = 0, int high = 100) const {
    int range = (high - low) + 1;
    // this modulo operation does not generate a truly uniformly distributed random number
    // in the span (since in most cases lower numbers are slightly more likely), 
    // but it is generally a good approximation for short spans. Use it if essential
    //int res = ( std::rand() % high + low );
    int res = low + static_cast<int>( ( range * std::rand() / ( RAND_MAX + 1.0) ) );
    return res;
  }

Die Erzeugung von Zufallszahlen ist ein gut untersuchtes, komplexes und fortgeschrittenes Thema. Hier finden Sie einige einfache, aber nützliche Algorithmen, abgesehen von denen, die in anderen Antworten erwähnt wurden.

Ewig verwirrt

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X