Comment générer des flottants aléatoires en C++?
Je pensais que je pouvais prendre le nombre entier Rand et le diviser par quelque chose. Cela suffirait-il?
Rand()
peut être utilisé pour générer des nombres pseudo-aléatoires en C++. En combinaison avec Rand_MAX
et un peu de math, vous pouvez générer des nombres aléatoires dans n'importe quel intervalle arbitraire de votre choix. C'est suffisant pour l'apprentissage et les programmes de jouets. Si vous avez besoin de véritables nombres aléatoires avec une distribution normale, vous devrez employer une méthode plus avancée.
Cela générera un nombre compris entre 0.0 et 1.0.
float r = static_cast <float> (Rand()) / static_cast <float> (Rand_MAX);
Cela générera un nombre compris entre 0.0 et un nombre arbitraire de float
, X
:
float r2 = static_cast <float> (Rand()) / (static_cast <float> (Rand_MAX/X));
Cela générera un nombre de LO
arbitraire à certains HI
arbitraires:
float r3 = LO + static_cast <float> (Rand()) /( static_cast <float> (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 "créer" 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 ça:
srand (static_cast <unsigned> (time(0)));
Pour appeler Rand
ou srand
, vous devez #include <cstdlib>
.
Pour appeler time
, vous devez #include <ctime>
.
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, voir le matériel de présentation Rand () considéré comme nocif par Stephan T. Lavavej donné lors de l'événement GoingNative 2013 . Les diapositives sont dans les commentaires mais voici un lien direct .
Je couvre également boost
ainsi que l’utilisation de Rand
car le code existant peut nécessiter encore sa prise en charge.
L'exemple ci-dessous est dérivé du site cppreference et utilise le moteur std :: mersenne_twister_engine et le std :: uniform_real_distribution qui génère des nombres dans l'intervalle [0,10)
, avec d'autres moteurs et distributions commentés (le voir en direct):
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>
int main()
{
std::random_device rd;
//
// Engines
//
std::mt19937 e2(rd());
//std::knuth_b e2(rd());
//std::default_random_engine e2(rd()) ;
//
// Distribtuions
//
std::uniform_real_distribution<> dist(0, 10);
//std::normal_distribution<> dist(2, 2);
//std::student_t_distribution<> dist(5);
//std::poisson_distribution<> dist(2);
//std::extreme_value_distribution<> dist(0,2);
std::map<int, int> 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 semblable à la suivante:
0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****
La sortie variera en fonction de la distribution choisie, donc si nous décidons d’utiliser std :: normal_distribution avec une valeur de 2
pour les deux et stddev par exemple dist(2, 2)
à la place, le résultat serait semblable à ceci (le voir en direct):
-6
-5
-4
-3
-2 **
-1 ****
0 *******
1 *********
2 *********
3 *******
4 ****
5 **
6
7
8
9
Ce qui suit est une version modifiée de certains des codes présentés dans N3551
(le voir en direct):
#include <algorithm>
#include <array>
#include <iostream>
#include <random>
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<card,52> 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 TS 2C 8D 3H 6C JS 7S 6S
Renforcer
Bien sûr, Boost.Random est toujours une option aussi, ici j'utilise boost :: random :: uniform_real_distribution :
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>
int main()
{
boost::random::mt19937 gen;
boost::random::uniform_real_distribution<> dist(0, 10);
std::map<int, int> 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 C FAQ pour un guide sur Comment puis-je générer des virgules flottantes nombres aléatoires? , qui donne en gros un exemple similaire à celui-ci pour générer un intervalle [0,1)
:
#include <stdlib.h>
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 / (N-M) ) ) ;
}
Jetez un oeil à Boost.Random . Vous pouvez faire quelque chose comme ça:
float gen_random_float(float min, float max)
{
boost::mt19937 rng;
boost::uniform_real<float> u(min, max);
boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > gen(rng, u);
return gen();
}
En jouant, vous feriez mieux de passer le même objet mt19937 au lieu de construire un nouvel objet à 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, rappelez-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, ce qui est identique au Boost. Bibliothèque aléatoire et nettement plus souple 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 distribution (normale, bernoulli, binomiale, etc.).
La syntaxe est la suivante (emprunt sans vergogne à ce site ):
#include <iostream>
#include <random>
...
std::tr1::mt19937 eng; // a core engine class
std::tr1::normal_distribution<float> dist;
for (int i = 0; i < 10; ++i)
std::cout << dist(eng) << std::endl;
Dans la version moderne _c++
_, vous pouvez utiliser l’en-tête <random>
fourni avec _c++11
_.
Pour obtenir des variables aléatoires float
, vous pouvez utiliser _std::uniform_real_distribution<>
_.
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, réglez le moteur et la distribution sur être static
.
Exemple:
_float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
return dis(e);
}
_
Il est idéal de placer les float
dans un conteneur tel que _std::vector
_:
_int main()
{
std::vector<float> 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 (actuellement, Windows avec VC évoque), Rand_MAX
est ridiculement petit, i. e. seulement 15 bits. Lorsque vous divisez par Rand_MAX
, vous ne générez qu'une mantisse de 15 bits au lieu des 23 bits possibles. Cela peut ne pas être un problème pour vous, mais vous omettez certaines valeurs dans ce cas.
Oh, je viens de remarquer 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 de 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, donc IEEE Std 1003.1-201 l’inclut toujours et n’a pas de note qu’elle part quelque chose de si tôt.
Dans Windows, la méthode standard est CryptGenRandom () .
Si vous savez que votre format de 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 entier aléatoire à l'aide de méthodes en bits. Cela ne devrait être envisagé que si vous n'avez pas accès aux random
ou Boost.Random
de C++ 11 == qui sont bien meilleurs.
_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 distribution que celle utilisant la division.
Aucune des réponses ne m'a satisfait jusqu'à présent, alors j'ai écrit une nouvelle fonction de flottement aléatoire. Il fait des hypothèses au niveau du bit sur le 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, a.k.a. '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));
}
À mon avis, la réponse ci-dessus donne un flottant "aléatoire", mais aucun n’est vraiment un flottant aléatoire (c’est-à-dire qu’il manque une partie de la représentation du flottant). Avant de me lancer rapidement dans mon implémentation, examinons d'abord le format standard ANSI/IEEE pour les floats:
| signe (1 bit) | e (8 bits) | f (23 bits) |
le nombre représenté par ce mot est (-1 * signe) * 2 ^ e * 1.f
notez que le nombre 'e' est un nombre biaisé (avec un biais de 127) compris entre -127 et 126. La fonction la plus simple (et même la plus aléatoire) consiste à simplement écrire les données d'un entier aléatoire dans un float, Ainsi
int tmp = Rand();
float f = (float)*((float*)&tmp);
notez que si vous faites float f = (float)Rand();
, il convertira l'entier en float (ainsi 10 deviendra 10.0).
Alors maintenant, si vous voulez limiter la valeur maximale, vous pouvez faire quelque chose comme (ne savez pas si 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 float, vous pouvez voir que la valeur maximale d’un float est (environ) 2 ^ 127, valeur bien supérieure à la valeur maximale d’un int (2 ^ 32), excluant ainsi une partie importante de les nombres pouvant être représentés par un float. 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)
retournera un nombre aléatoire compris entre 0.0 et 255.0
Pour C++, il peut générer des nombres réels réels dans la plage spécifiée par la variable dist
.
#include <random> //If it doesnt work then use #include <tr1/random>
#include <iostream>
using namespace std;
typedef std::tr1::ranlux64_base_01 Myeng;
typedef std::tr1::normal_distribution<double> 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);
}
#include <cstdint>
#include <cstdlib>
#include <ctime>
using namespace std;
/* single precision float offers 24bit worth of linear distance from 1.0f to 0.0f */
float getval() {
/* Rand() has min 16bit, but we need a 24bit random number. */
uint_least32_t r = (Rand() & 0xffff) + ((Rand() & 0x00ff) << 16);
/* 5.9604645E-8 is (1f - 0.99999994f), 0.99999994f is the first value less than 1f. */
return (double)r * 5.9604645E-8;
}
int main()
{
srand(time(NULL));
...
Je ne pouvais pas poster deux réponses, alors voici la deuxième solution. log2 nombres aléatoires, biais massif vers 0.0f mais c’est vraiment un float aléatoire 1.0f à 0.0f.
#include <cstdint>
#include <cstdlib>
#include <ctime>
using namespace std;
float getval () {
union UNION {
uint32_t i;
float f;
} r;
/* 3 because it's 0011, the first bit is the float's sign.
* Clearing the second bit eliminates values > 1.0f.
*/
r.i = (Rand () & 0xffff) + ((Rand () & 0x3fff) << 16);
return r.f;
}
int main ()
{
srand (time (NULL));
...
Rand () renvoie un int compris entre 0 et Rand_MAX. Pour obtenir un nombre aléatoire compris entre 0.0 et 1.0, commencez par convertir le retour int de Rand () en float, puis divisez-le par Rand_MAX.