Comment générer un double aléatoire uniformément distribué entre 0 et 1 à partir de C++?

comment générer un double aléatoire uniformément distribué entre 0 et 1 à partir de C++?

bien sûr, je peux penser à certaines réponses, mais je voudrais savoir ce qu'est la pratique standard, d'avoir:

  • bonne conformité aux normes
  • Bonne aléatoire
  • bonne vitesse

(la vitesse est plus importante que le hasard pour mon application).

Merci beaucoup!

PS: dans le cas où cela compte, Mes plates-formes cibles sont Linux et Windows.

56
demandé sur static_rtti 2009-08-27 16:24:43

13 réponses

en C++11 et C++14 nous avons de bien meilleures options avec le en-tête aléatoire . La présentation Rand() considéré comme nocif par Stephan T. Lavavej explique pourquoi nous devrions éviter l'utilisation de rand() en C++ en faveur de random en-tête et N3924: décourager rand() En C++14 renforce encore ce point.

l'exemple ci-dessous est un version du code échantillon sur le site de référence CPP et utilise le std:: mersenne_twister_engine moteur et le std:: uniform_real_distribution qui génère des nombres dans la gamme [0,1) ( voir en direct ):

#include <iostream>
#include <iomanip>
#include <map>
#include <random>

int main()
{
    std::random_device rd;


    std::mt19937 e2(rd());

    std::uniform_real_distribution<> dist(0, 1);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::round(dist(e2))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

sortie sera similaire à la suivante:

0 ************************
1 *************************

depuis que le post a mentionné que la vitesse était importante ensuite, nous devrions considérer la section cppreference qui décrit les différents moteurs de nombres aléatoires ( emphasis mine ):

le choix du moteur à utiliser implique un certain nombre de compromis*: ** moteur linéaire congruent est modérément rapide et a une très petite les besoins de stockage pour l'état. Les générateurs à décalage Fibonacci sont très rapide, même sur les processeurs sans avancé instruction arithmétique l'état, au détriment d'un plus grand stockage et parfois de moins de souhaitable caractéristiques spectrales. Le Mersenne twister est plus lent et a plus besoins de stockage mais avec les bons paramètres possède la plus longue séquence non répétitive avec la plus souhaitable caractéristiques spectrales (pour une définition donnée de désirable).

donc s'il y a un désir pour un générateur plus rapide peut-être ranlux24_base ou ranlux48_base sont de meilleurs choix que mt19937 .

Rand ()

si vous êtes forcé d'utiliser rand() puis le C FAQ pour un guide sur Comment puis-je générer des nombres aléatoires à virgule flottante? , nous donne un exemple semblable à celui-ci pour générer un sur l'intervalle [0,1) :

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

et de générer un nombre aléatoire dans la gamme de [M,N) :

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}
28
répondu Shafik Yaghmour 2014-11-11 11:20:55

une solution de la vieille école comme:

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

devrait répondre à tous vos critères (portable, standard et rapide). évidemment, le nombre aléatoire généré doit être semé la procédure standard est quelque chose comme:

srand((unsigned)time(NULL));
55
répondu Elemental 2009-08-27 12:30:58
6
répondu Vijay Mathew 2009-08-27 12:31:13

Voici comment vous le feriez si vous utilisiez C++ TR1 .

5
répondu John D. Cook 2009-08-27 12:40:58

si la vitesse est votre principale préoccupation, alors je dirais simplement

double r = (double)rand() / (double)RAND_MAX;
3
répondu suszterpatt 2009-09-13 14:01:46

la bibliothèque standard C++11 contient un cadre décent et Quelques générateurs en bon état de service, ce qui est parfaitement suffisant pour les devoirs à la maison et l'utilisation à découvert.

Cependant, pour le code de grade de production, vous devez savoir exactement quelles sont les propriétés spécifiques des différents générateurs avant de les utiliser, puisque chacun d'eux ont leurs mises en garde. En outre, aucun d'entre eux ne passe les tests standard pour PRNGs comme TestU01, sauf pour les générateurs ranlux si utilisé avec un facteur de luxe généreux.

si vous voulez des résultats solides, reproductibles, alors vous devez apporter votre propre générateur.

Si vous voulez la portabilité, alors vous devez apporter votre propre générateur.

si vous pouvez vivre avec une portabilité restreinte, alors vous pouvez utiliser boost, ou le cadre C++11 en conjonction avec votre propre générateur(s).

plus de détails - y compris le code pour un générateur simple mais rapide d'excellente qualité et liens copieux - peut être trouvé dans mes réponses à des sujets similaires:

pour les déviations à virgule flottante professionnelles, il y a deux autres questions à considérer:

  • ouvert vs demi-ouvert vs fermé gamme, c'est à dire (0,1), [0, 1) ou [0,1]
  • méthode de conversion de intégrale à flottante (précision, vitesse)

les deux sont en fait les deux faces d'une même pièce, puisque la méthode de conversion tient compte de l'inclusion/exclusion de 0 et 1. Voici trois méthodes différentes pour le demi-intervalle ouvert:

// exact values computed with bc

#define POW2_M32   2.3283064365386962890625e-010
#define POW2_M64   5.421010862427522170037264004349e-020

double random_double_a ()
{
   double lo = random_uint32() * POW2_M64;
   return lo + random_uint32() * POW2_M32;
}

double random_double_b ()
{
   return random_uint64() * POW2_M64;
}

double random_double_c ()
{
   return int64_t(random_uint64()) * POW2_M64 + 0.5;
}

( random_uint32() et random_uint64() sont des espaces réservés pour vos fonctions réelles et normalement être transmises comme paramètres du modèle)

méthode a montre comment créer une déviation uniforme qui n'est pas biassée par une précision excessive pour des valeurs plus faibles; le code pour 64 bits n'est pas montré parce qu'il est plus simple et implique seulement masquer 11 bits. La distribution est uniforme pour toutes les fonctions mais sans cette astuce, il y aurait plus de valeurs différentes dans la zone plus proche de 0 qu'ailleurs (espacement entre les mailles plus fin dû à la variation de ulp).

Méthode c montre comment obtenir un uniforme s'écarter plus rapidement sur certaines plates-formes populaires où la FPU ne connaît qu'un signé de 64 bits de type intégral. Ce que vous voyez le plus souvent est la méthode b mais là le compilateur doit générer beaucoup de code supplémentaire sous le capot pour préserver la sémantique non signée.

mélangez et faites correspondre ces principes pour créer votre propre solution sur mesure.

tout cela est expliqué dans l'excellent article de Jürgen Doornik Conversion de nombres aléatoires de haute période en point flottant .

3
répondu DarthGizka 2017-05-23 10:30:46

comprend D'abord stdlib.h

#include<stdlib.h>

puis suivant peut être une fonction pour générer un nombre double aléatoire entre une gamme dans le langage de programmation C.

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

ici RAND_MAX est défini dans stdlib.h

2
répondu Amarjit Datta 2015-09-30 05:43:40

comme je le vois, il y a trois façons de faire avec cela,

1) la voie facile.

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

2) la voie de sécurité (conforme à la norme).

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) La coutume.

en éliminant rand() nous n'avons plus à nous soucier des idiosyncrasies d'une version particulière, ce qui nous donne plus de marge de manœuvre dans notre propre mise en œuvre.

Note: période de la génératrice utilisée ici est le segment 1.8 E+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;
}

quel que soit le choix, la fonctionnalité peut être étendue comme suit,

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

Final notes: la version fast - lorsqu'elle est écrite en C - peut être adaptée pour être utilisée en C++ Pour remplacer std::generate_canonical , et fonctionnera pour n'importe quel générateur émettant des valeurs avec suffisamment de bits significatifs.

la plupart des générateurs 64 bits profiter de leur largeur, de sorte que cela peut probablement être utilisé sans modification (changement de réglage). par exemple, cela fonctionne avec le moteur std::mt19937_64 .

1
répondu Johnny Cage 2015-04-08 13:12:12

en considérant la simplicité et la vitesse comme critères principaux, vous pouvez ajouter un petit helper générique comme ceci: -

  // 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;
  }

la génération de nombres aléatoires est un sujet bien étudié, complexe et avancé. Vous pouvez trouver ici quelques algorithmes simples mais utiles en dehors de ceux mentionnés dans d'autres réponses: -

Éternellement Confus

0
répondu Abhay 2009-08-27 12:38:47

vous pouvez essayer L'algorithme de Mersenne Twister.

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

il a un bon mélange de vitesse et d'aléatoire, et une mise en œuvre GPL.

-1
répondu Ryan 2009-08-27 12:31:24

C'est ce que j'ai fini par utiliser pour mes besoins:

int range_upper_bound = 12345;
int random_number =((double)rand()/(double)range_upper_bound);
-1
répondu Tim Siwula 2017-03-09 04:21:46
double randDouble()
{
  double out;
  out = (double)rand()/(RAND_MAX + 1); //each iteration produces a number in [0, 1)
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;
  out = (rand() + out)/RAND_MAX;

  return out;
}

pas aussi rapide que double X=((double)rand()/(double)RAND_MAX); , mais avec une meilleure distribution. Cet algorithme ne donne que RAND_MAX un choix de valeurs de retour uniformément espacées; celui-ci donne RANDMAX^6, de sorte que sa distribution n'est limitée que par la précision du double.

si vous voulez un double long juste ajouter quelques itérations. Si vous voulez un nombre dans [0, 1] plutôt que [0, 1) Faites simplement la ligne 4 Lire out = (double)rand()/(RAND_MAX); .

-2
répondu foo'pitier 2012-09-11 18:07:55
//Returns a random number in the range (0.0f, 1.0f).
// 0111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
// seee eeee eeee vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv
// sign     = 's'
// exponent = 'e'
// value    = 'v'
double DoubleRand() {
  typedef unsigned long long uint64;
  uint64 ret = 0;
  for (int i = 0; i < 13; i++) {
     ret |= ((uint64) (rand() % 16) << i * 4);
  }
  if (ret == 0) {
    return rand() % 2 ? 1.0f : 0.0f;
  }
  uint64 retb = ret;
  unsigned int exp = 0x3ff;
  retb = ret | ((uint64) exp << 52);
  double *tmp = (double*) &retb;
  double retval = *tmp;
  while (retval > 1.0f || retval < 0.0f) {
    retval = *(tmp = (double*) &(retb = ret | ((uint64) (exp--) << 52)));
  }
  if (rand() % 2) {
    retval -= 0.5f;
  }
  return retval;
}

cela devrait faire l'affaire, j'ai utilisé ceci article Wikipedia pour aider à créer ceci. Je crois que c'est aussi bon que drand48();

-2
répondu Dean 2013-08-21 20:26:12