Génération aléatoire de nombres flottants C ++

Comment générer des flottants aléatoires en C ++?

Je pensais pouvoir prendre le nombre entier de rand et le diviser par quelque chose, serait-ce suffisant?

rand() peut être utilisé pour générer des nombres pseudo-aléatoires en C ++. En combinaison avec RAND_MAX et un peu de calcul, vous pouvez générer des nombres aléatoires dans tout intervalle arbitraire choisi. Ceci est suffisant à des fins d’apprentissage et de programmes de jouets. Si vous avez besoin de nombres vraiment aléatoires avec une dissortingbution normale, vous devrez utiliser une méthode plus avancée.


Cela générera un nombre compris entre 0,0 et 1,0 inclus.

 float r = static_cast  (rand()) / static_cast  (RAND_MAX); 

Cela va générer un nombre compris entre 0.0 et un float arbitraire, X :

 float r2 = static_cast  (rand()) / (static_cast  (RAND_MAX/X)); 

Cela générera un nombre de LO arbitraires à certains HI arbitraires:

 float r3 = LO + static_cast  (rand()) /( static_cast  (RAND_MAX/(HI-LO))); 

Notez que la fonction rand() ne sera souvent pas suffisante si vous avez besoin de nombres vraiment aléatoires.


Avant d’appeler rand() , vous devez d’abord “générer” le générateur de nombres aléatoires en appelant srand() . Cela devrait être fait une fois pendant l’exécution de votre programme – pas une fois à chaque fois que vous appelez rand() . Cela se fait souvent comme ceci:

 srand (static_cast  (time(0))); 

Pour appeler rand ou srand vous devez #include .

Pour appeler le time , vous devez #include .

C ++ 11 vous donne beaucoup de nouvelles options avec random . Le document canonique sur ce sujet serait N3551, Génération de nombres aléatoires en C ++ 11

Pour voir pourquoi l’utilisation de rand() peut être problématique, consultez le matériel de présentation rand () Considered Harmful de Stephan T. Lavavej lors de l’événement GoingNative 2013 . Les diapositives sont dans les commentaires mais voici un lien direct .

Je couvre également le boost ainsi que l’utilisation du rand car le code existant peut encore nécessiter son support.

L’exemple ci-dessous est extrait du site cppreference et utilise le moteur std :: mersenne_twister_engine et le fichier std :: uniform_real_dissortingbution qui génère des nombres dans l’intervalle [0,10) , avec les autres moteurs et les dissortingbutions mis en commentaire ( voir en direct ):

 #include  #include  #include  #include  #include  int main() { std::random_device rd; // // Engines // std::mt19937 e2(rd()); //std::knuth_b e2(rd()); //std::default_random_engine e2(rd()) ; // // Dissortingbtuions // std::uniform_real_dissortingbution<> dist(0, 10); //std::normal_distribution<> dist(2, 2); //std::student_t_dissortingbution<> dist(5); //std::poisson_distribution<> dist(2); //std::extreme_value_dissortingbution<> dist(0,2); std::map hist; for (int n = 0; n < 10000; ++n) { ++hist[std::floor(dist(e2))]; } for (auto p : hist) { std::cout << std::fixed << std::setprecision(1) << std::setw(2) << p.first << ' ' << std::string(p.second/200, '*') << '\n'; } } 

La sortie sera similaire à la suivante:

 0 **** 1 **** 2 **** 3 **** 4 ***** 5 **** 6 ***** 7 **** 8 ***** 9 **** 

La sortie variera en fonction de la dissortingbution que vous choisissez, donc si nous décidions d’aller avec std :: normal_dissortingbution avec une valeur de 2 pour les moyennes et les stddev, par exemple dist(2, 2) la sortie serait similaire ( voir vivre ):

 -6 -5 -4 -3 -2 ** -1 **** 0 ******* 1 ********* 2 ********* 3 ******* 4 **** 5 ** 6 7 8 9 

Voici une version modifiée d'une partie du code présenté dans N3551 ( voir en direct ):

 #include  #include  #include  #include  std::default_random_engine & global_urng( ) { static std::default_random_engine u{}; return u ; } void randomize( ) { static std::random_device rd{}; global_urng().seed( rd() ); } int main( ) { // Manufacture a deck of cards: using card = int; std::array deck{}; std::iota(deck.begin(), deck.end(), 0); randomize( ) ; std::shuffle(deck.begin(), deck.end(), global_urng()); // Display each card in the shuffled deck: auto suit = []( card c ) { return "SHDC"[c / 13]; }; auto rank = []( card c ) { return "AKQJT98765432"[c % 13]; }; for( card c : deck ) std::cout < < ' ' << rank(c) << suit(c); std::cout << std::endl; } 

Les résultats ressembleront à:

5H 5S AS 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QD JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S 7S 6S

Renforcer

Bien sûr, Boost.Random est toujours une option, ici j'utilise boost :: random :: uniform_real_dissortingbution :

 #include  #include  #include  #include  #include  #include  int main() { boost::random::mt19937 gen; boost::random::uniform_real_dissortingbution<> dist(0, 10); std::map hist; for (int n = 0; n < 10000; ++n) { ++hist[std::floor(dist(gen))]; } for (auto p : hist) { std::cout << std::fixed << std::setprecision(1) << std::setw(2) << p.first << ' ' << std::string(p.second/200, '*') << '\n'; } } 

rand()

Si vous devez utiliser rand() alors nous pouvons aller à la FAQ C pour un guide sur Comment puis-je générer des nombres aléatoires à virgule flottante? , qui donne essentiellement un exemple similaire à celui de générer un intervalle [0,1) :

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

et pour générer un nombre aléatoire compris entre [M,N) :

 double randMToN(double M, double N) { return M + (rand() / ( RAND_MAX / (NM) ) ) ; } 

Jetez un oeil à Boost.Random . Vous pourriez faire quelque chose comme ça:

 float gen_random_float(float min, float max) { boost::mt19937 rng; boost::uniform_real u(min, max); boost::variate_generator > gen(rng, u); return gen(); } 

Jouez, vous feriez mieux de faire passer le même object mt19937 au lieu d’en construire un à chaque fois, mais j’espère que vous en aurez l’idée.

Appelez le code avec deux valeurs float , le code fonctionne dans n’importe quelle plage.

 float rand_FloatRange(float a, float b) { return ((b - a) * ((float)rand() / RAND_MAX)) + a; } 

Si vous utilisez C ++ et non C, souvenez-vous que dans le rapport technique 1 (TR1) et dans le brouillon C ++ 0x, ils ont ajouté des fonctionnalités pour un générateur de nombres aléatoires dans le fichier d’en-tête. Bibliothèque aléatoire et certainement plus flexible et “moderne” que la fonction de bibliothèque C, rand.

Cette syntaxe offre la possibilité de choisir un générateur (comme le mersenne twister mt19937), puis de choisir une dissortingbution (normale, bernoulli, binomiale, etc.).

La syntaxe est la suivante (sans vergogne emprunté à ce site ):

  #include  #include  ... std::tr1::mt19937 eng; // a core engine class std::tr1::normal_dissortingbution dist; for (int i = 0; i < 10; ++i) std::cout << dist(eng) << std::endl; 

En c++ moderne, vous pouvez utiliser l’en-tête fourni avec c++11 .
Pour obtenir des float aléatoires, vous pouvez utiliser std::uniform_real_dissortingbution<> .

Vous pouvez utiliser une fonction pour générer les nombres et si vous ne voulez pas que les nombres soient identiques tout le temps, définissez le moteur et la dissortingbution comme étant static .
Exemple:

 float get_random() { static std::default_random_engine e; static std::uniform_real_dissortingbution<> dis(0, 1); // rage 0 - 1 return dis(e); } 

C’est idéal pour placer les float dans un conteneur tel que std::vector :

 int main() { std::vector nums; for (int i{}; i != 5; ++i) // Generate 5 random floats nums.emplace_back(get_random()); for (const auto& i : nums) std::cout < < i << " "; } 

Exemple de sortie:

 0.0518757 0.969106 0.0985112 0.0895674 0.895542 

Sur certains systèmes (Windows avec VC en tête, actuellement), RAND_MAX est ridiculement petit, i. e. seulement 15 bits. Lorsque vous RAND_MAX par RAND_MAX vous ne générez qu’une mantisse de 15 bits au lieu des 23 bits possibles. Cela peut ou peut ne pas être un problème pour vous, mais vous manquez certaines valeurs dans ce cas.

Oh, juste remarqué qu’il y avait déjà un commentaire pour ce problème. Quoi qu’il en soit, voici un code qui pourrait résoudre ce problème pour vous:

 float r = (float)((rand() < < 15 + rand()) & ((1 << 24) - 1)) / (1 << 24); 

Non testé, mais pourrait fonctionner 🙂

drand48(3) est la méthode standard POSIX. GLibC fournit également une version réentrante, drand48_r(3) .

La fonction a été déclarée obsolète dans SVID 3, mais aucune alternative adéquate n’a été fournie.

Dans Windows, la méthode standard est CryptGenRandom () .

Je n’ai pas été satisfait par aucune des réponses jusqu’à maintenant, alors j’ai écrit une nouvelle fonction de flottement aléatoire. Il crée des hypothèses au niveau du type de données float. Il a toujours besoin d’une fonction rand () avec au moins 15 bits aléatoires.

 //Returns a random number in the range [0.0f, 1.0f). Every //bit of the mantissa is randomized. float rnd(void){ //Generate a random number in the range [0.5f, 1.0f). unsigned int ret = 0x3F000000 | (0x7FFFFF & ((rand() < < 8) ^ rand())); unsigned short coinFlips; //If the coin is tails, return the number, otherwise //divide the random number by two by decrementing the //exponent and keep going. The exponent starts at 63. //Each loop represents 15 random bits, aka 'coin flips'. #define RND_INNER_LOOP() \ if( coinFlips & 1 ) break; \ coinFlips >>= 1; \ ret -= 0x800000 for(;;){ coinFlips = rand(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); //At this point, the exponent is 60, 45, 30, 15, or 0. //If the exponent is 0, then the number equals 0.0f. if( ! (ret & 0x3F800000) ) return 0.0f; RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP(); } return *((float *)(&ret)); } 

A mon avis, la réponse ci-dessus donne un flottant “aléatoire”, mais aucun d’entre eux n’est vraiment un flottant aléatoire (c’est-à-dire qu’ils manquent une partie de la représentation flottante). Avant de me lancer dans l’implémentation, examinons d’abord le format standard ANSI / IEEE pour les flottants:

| signe (1 bit) | e (8 bits) | f (23 bits) |

le nombre représenté par ce mot est (signe -1) * 2 ^ e * 1.f

notez que le nombre ‘e’ est un nombre biaisé (avec un biais de 127), allant de -127 à 126. La fonction la plus simple (et en réalité la plus aléatoire) est de simplement écrire les données d’un int aléatoire dans un flottant, Ainsi

 int tmp = rand(); float f = (float)*((float*)&tmp); 

notez que si vous faites float f = (float)rand(); il convertira l’entier en un flottant (donc 10 deviendra 10.0).

Alors maintenant, si vous voulez limiter la valeur maximale, vous pouvez faire quelque chose comme ça (pas sûr que cela fonctionne)

 int tmp = rand(); float f = *((float*)&tmp); tmp = (unsigned int)f // note float to int conversion! tmp %= max_number; f -= tmp; 

mais si vous regardez la structure du flottant, vous pouvez voir que la valeur maximale d’un flottant est (approximativement) 2 ^ 127, ce qui est beaucoup plus grand que la valeur maximale d’un int (2 ^ 32), ce qui exclut une partie significative de les nombres pouvant être représentés par un flotteur. Ceci est ma dernière implémentation:

 /** * Function generates a random float using the upper_bound float to determine * the upper bound for the exponent and for the fractional part. * @param min_exp sets the minimum number (closest to 0) to 1 * e^min_exp (min -127) * @param max_exp sets the maximum number to 2 * e^max_exp (max 126) * @param sign_flag if sign_flag = 0 the random number is always positive, if * sign_flag = 1 then the sign bit is random as well * @return a random float */ float randf(int min_exp, int max_exp, char sign_flag) { assert(min_exp < = max_exp); int min_exp_mod = min_exp + 126; int sign_mod = sign_flag + 1; int frac_mod = (1 << 23); int s = rand() % sign_mod; // note x % 1 = 0 int e = (rand() % max_exp) + min_exp_mod; int f = rand() % frac_mod; int tmp = (s << 31) | (e << 23) | f; float r = (float)*((float*)(&tmp)); /** uncomment if you want to see the structure of the float. */ // printf("%x, %x, %x, %x, %f\n", (s << 31), (e << 23), f, tmp, r); return r; } 

En utilisant cette fonction, randf(0, 8, 0) renverra un nombre aléatoire compris entre 0.0 et 255.0

Si vous savez que votre format à virgule flottante est IEEE 754 (presque tous les processeurs modernes, y compris Intel et ARM), vous pouvez créer un nombre à virgule flottante aléatoire à partir d’un nombre entier aléatoire en utilisant des méthodes basées sur les bits. Cela ne devrait être envisagé que si vous n’avez pas access au C ++ 11 random ou à Boost.Random qui sont tous deux beaucoup mieux.

 float rand_float() { // returns a random value in the range [0.0-1.0) // start with a bit pattern equating to 1.0 uint32_t pattern = 0x3f800000; // get 23 bits of random integer uint32_t random23 = 0x7fffff & (rand() < < 8 ^ rand()); // replace the mantissa, resulting in a number [1.0-2.0) pattern |= random23; // convert from int to float without undefined behavior assert(sizeof(float) == sizeof(uint32_t)); char buffer[sizeof(float)]; memcpy(buffer, &pattern, sizeof(float)); float f; memcpy(&f, buffer, sizeof(float)); return f - 1.0; } 

Cela donnera une meilleure dissortingbution qu'une division utilisasortingce.

Pour C ++, il peut générer des nombres réels dans la plage spécifiée par la variable dist

 #include  //If it doesnt work then use #include  #include  using namespace std; typedef std::tr1::ranlux64_base_01 Myeng; typedef std::tr1::normal_dissortingbution Mydist; int main() { Myeng eng; eng.seed((unsigned int) time(NULL)); //initializing generator to January 1, 1970); Mydist dist(1,10); dist.reset(); // discard any cached values for (int i = 0; i < 10; i++) { std::cout << "a random value == " << (int)dist(eng) << std::endl; } return (0); } 

rand () renvoie un int entre 0 et RAND_MAX. Pour obtenir un nombre aléatoire compris entre 0,0 et 1,0, commencez par convertir le retour int par rand () en valeur flottante, puis divisez-le par RAND_MAX.

Un nombre flottant totalement aléatoire est généré de la manière suivante: signe aléatoire, exposant aléatoire et mantisse aléatoire. Voici un exemple de génération de nombres aléatoires à partir de 0..MAXFLOAT avec une dissortingbution uniforme:

 static float frand(){ float f; UINT32 *fi = (UINT32*)&f; *fi = 0; const int minBitsRandGives = (1< <15); // RAND_MAX is at least (1<<15) UINT32 randExp = (rand()%254)+1; // Exponents are in range of [1..254] UINT32 randMantissa = ((rand() % minBitsRandGives) << 8) | (rand()%256); *fi = randMantissa | (randExp<<23); // Build a float with random exponent and random mantissa return f; } 

Remarque importante: RAND_MAX est par défaut égal à 2 ^ 16 (sur les systèmes 32 bits), donc rand () peut générer au maximum 15 bits aléatoires. Comme la virgule flottante a un total de 32 bits, il faut activer le rand () au moins 3 fois pour générer 32 bits aléatoires. J'ai utilisé 8 bits de rand () pour générer Exponent et 2 autres appels à rand () pour générer 23 bits de mantisse.

Erreur courante à éviter: Si vous utilisez (float)rand()/MAX_RAND pour obtenir un nombre à virgule flottante dans l'intervalle [0..1], vous obtiendrez toujours des nombres aléatoires dans une dissortingbution uniforme mais de faible précision . Par exemple, votre générateur aléatoire peut générer 0,00001 et 0,00002 mais ne peut pas générer 0,000017. Un tel aléatoire est 256 fois moins précis que la représentation réelle en virgule flottante.

Optimisation: ma fonction n'est pas optimisée pour la vitesse. Vous pouvez l'améliorer en remplaçant la division '%' par des opérations logiques binarys. Par exemple Au lieu de %256 utilisez &0xFF