Willekeurige generatie van float-nummers

Hoe genereer ik willekeurige floats in C++?

Ik dacht dat ik de gehele rand kon nemen en het door iets kon delen, zou dat voldoende zijn?


Antwoord 1, autoriteit 100%

rand()kan worden gebruikt om pseudo-willekeurige getallen in C++ te genereren. In combinatie met RAND_MAXen een beetje wiskunde, kun je willekeurige getallen genereren in elk willekeurig interval dat je kiest. Dit is voldoende voor leerdoeleinden en speelgoedprogramma’s. Als je echtwillekeurige getallen met een normale verdeling nodig hebt, moet je een meer geavanceerde methode gebruiken.


Dit genereert een getal van 0,0 tot en met 1,0.

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

Dit genereert een getal van 0,0 tot een willekeurige float, X:

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

Dit genereert een getal van een willekeurige LOtot een willekeurige HI:

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

Houd er rekening mee dat de functie rand()vaak niet voldoende is als u echt willekeurige getallen nodig heeft.


Voordat u rand()aanroept, moet u eerst de generator voor willekeurige getallen “seeden” door srand()te bellen. Dit moet één keer worden gedaan tijdens de uitvoering van je programma — niet één keer elke keer dat je rand()aanroept. Dit wordt vaak als volgt gedaan:

srand (static_cast <unsigned> (time(0)));

Om te bellen met randof srandU moet #include <cstdlib>.

Om timete bellen, moet u #include <ctime>.


Antwoord 2, Autoriteit 37%

C++ 11 geeft u veel nieuwe opties met random. Het canonieke papier op dit onderwerp zou N3551, willekeurige getalgeneratie in C++ 11

Om te zien waarom het gebruik van rand()Problematisch kan zijn, zie de Rand () Beschouwd als schadelijk presentatiemateriaal door Stephan T. LAVAVEJ Gegeven tijdens het GoSnative 2013 evenement. De dia’s zijn in de opmerkingen, maar hier is een directe link .

Ik bedek ook boostEvenals met randAangezien Legacy-code nog steeds zijn ondersteuning vereist.

Het onderstaande voorbeeld wordt gedistilleerd uit de CPPReference-site en gebruikt de std :: mersenne_twister_engine Motor en de STD :: Uniform_real_distribution die nummers genereert De [0,10)Interval, met andere motoren en distributies opgenomen (zie het live ):

#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';
    }
}

uitvoer zal er als volgt uitzien:

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

De uitvoer is afhankelijk van de distributie die je kiest, dus als we besloten om voor std::normal_distributionmet een waarde van 2voor zowel gemiddeldeals stddevbijv. dist(2, 2)in plaats daarvan zou de uitvoer vergelijkbaar zijn met deze (bekijk het live):

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

Het volgende is een aangepaste versie van een deel van de code die wordt weergegeven in N3551(zie het leeft) :

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

De resultaten zien er ongeveer zo uit:

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

Boost

Natuurlijk is Boost.Randomaltijd een optie ook, hier gebruik ik boost:: willekeurig::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 ()

Als u rand()kunt gebruiken, kunnen we naar de C FAQ voor een gidsen op Hoe kan ik willekeurige nummers genereren? , die in principe een voorbeeld geeft, vergelijkbaar met dit voor het genereren van een op het interval [0,1):

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

en om een ​​willekeurig getal in het bereik te genereren van [M,N):

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

Antwoord 3, Autoriteit 16%

Bekijk Boost.Random . Je zou zoiets kunnen doen:

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

Speel wat rond, je kunt misschien beter hetzelfde mt19937-object doorgeven in plaats van elke keer een nieuw object te bouwen, maar hopelijk snap je het idee.


Antwoord 4, autoriteit 9%

In moderne c++mag je de <random>-header die bij c++11is geleverd.
Om willekeurige float‘s te krijgen, kun je std::uniform_real_distribution<>gebruiken.

U kunt een functie gebruiken om de getallen te genereren en als u niet wilt dat de getallen altijd hetzelfde zijn, stelt u de engine en distributie in op static.
Voorbeeld:

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

Het is ideaal om de float‘s in een container te plaatsen zoals 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 << " ";
}

Voorbeelduitvoer:

0.0518757 0.969106 0.0985112 0.0895674 0.895542

Antwoord 5, autoriteit 7%

Noem de code met twee floatwaarden, de code werkt in elk bereik.

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

Antwoord 6, autoriteit 5%

Als u C++ en niet C gebruikt, onthoud dan dat in technisch rapport 1 (TR1) en in de C++ 0x-ontwerp hebben ze toegevoegde faciliteiten voor een willekeurige nummergenerator in het koptekstbestand, ik geloof dat het identiek is aan De Boost.Random-bibliotheek en absoluut flexibeler en “modern” dan de C Bibliotheekfunctie, Rand.

Deze syntaxis biedt de mogelijkheid om een ​​generator te kiezen (zoals de Mersenne Twister MT19937) en dan Kies een distributie (normaal, Bernoulli, BINOMIAL enz.).

Syntax is als volgt (schaamteloos geleend van deze 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;

Antwoord 7

Op sommige systemen (Windows met VC Springs in Mind, momenteel), RAND_MAXis belachelijk klein, i. & nbsp; e. slechts 15 bit. Bij het delen van RAND_MAXU genereert slechts een Mantissa van 15 bit in plaats van de 23 mogelijke bits. Dit kan al dan niet een probleem voor u zijn, maar u mist in dat geval enkele waarden.

Oh, merkte gewoon dat er al een opmerking voor dat probleem was. Hoe dan ook, hier is een code die dit voor u kan oplossen:

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

Niettest, maar zou kunnen werken: -)


Antwoord 8

drand48(3)is de POSIX-standaardmanier . GLibC biedt ook een herintredende versie, drand48_r(3).

De functie is verouderd verklaard in SVID 3, maar er is geen adequaat alternatief geboden, dus IEEE Std 1003.1-2013bevat het nog steeds en heeft geen opmerkingen dat het binnenkort ergens heen gaat.

In Windows is de standaardmanier CryptGenRandom().


Antwoord 9

Ik was tot nu toe niet tevreden met de antwoorden, dus heb ik een nieuwe willekeurige float-functie geschreven. Het maakt bitsgewijze aannames over het float-gegevenstype. Het heeft nog steeds een rand()-functie nodig met minstens 15 willekeurige bits.

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

Antwoord 10

Naar mijn mening geeft het bovenstaande antwoord enige ‘willekeurige’ float, maar geen van hen is echt een willekeurige float (d.w.z. ze missen een deel van de float-representatie). Laten we, voordat ik me overhaast met mijn implementatie, eerst eens kijken naar het ANSI/IEEE-standaardformaat voor floats:

|teken (1-bit)| e (8-bits) | f (23-bit) |

het getal dat door dit woord wordt vertegenwoordigd is
(-1 * teken) * 2^e * 1.f

merk op dat het ‘e’-nummer een bevooroordeeld (met een bias van 127) getal is, dus variërend van -127 tot 126. De meest eenvoudige (en eigenlijk meest willekeurige) functie is om gewoon de gegevens van een willekeurige int in een drijver, dus

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

merk op dat als je float f = (float)rand();doet, het gehele getal wordt omgezet in een float (dus 10 wordt 10.0).

Dus als je nu de maximale waarde wilt beperken, kun je zoiets doen als (niet zeker of dit werkt)

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

maar als je naar de structuur van de float kijkt, kun je zien dat de maximale waarde van een float (ongeveer) 2^127 is, wat veel groter is dan de maximale waarde van een int (2^32), waardoor een aanzienlijk deel van de getallen dat kan worden weergegeven door een float.
Dit is mijn laatste implementatie:

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

het gebruik van deze functie randf(0, 8, 0)retourneert een willekeurig getal tussen 0,0 en 255,0


Antwoord 11

Als u weet dat uw drijvende-komma-indeling IEEE 754is (bijna alle moderne CPU’s inclusief Intel en ARM), dan kun je een willekeurig getal met drijvende komma bouwen van een willekeurig geheel getal met behulp van bitsgewijze methoden. Dit moet alleen worden overwogen als u geen toegang hebt tot C++11’s randomof Boost.Randomdie beide veel beter zijn.

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

Dit geeft een betere verdeling dan een die deling gebruikt.


Antwoord 12

Voor C++ kan het echte float-getallen genereren binnen het bereik gespecificeerd door distvariabele

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

Antwoord 13

rand() retourneert een int tussen 0 en RAND_MAX. Om een willekeurig getal tussen 0.0 en 1.0 te krijgen, cast je eerst de int return door rand() naar een float, dan deel je het door RAND_MAX.


Antwoord 14

#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));
...

Ik kon geen twee antwoorden plaatsen, dus hier is de tweede oplossing. log2 willekeurige getallen, enorme voorkeur voor 0.0f maar het is echt een willekeurige float 1.0f tot 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));
...

Other episodes