Generierung zufälliger Gleitkommazahlen

Lesezeit: 12 Minuten

Generierung zufalliger Gleitkommazahlen
Hasen

Wie erzeuge ich zufällige Floats in C++?

Ich dachte, ich könnte den ganzzahligen Rand nehmen und ihn durch etwas teilen, wäre das angemessen genug?

  • Es hängt eher davon ab, wofür Sie die Nummer wünschen und wie zufällig. Normalerweise gibt rand() 15 Bit Zufälligkeit aus, aber Floats haben eine Genauigkeit von 23 Bit, sodass einige Werte fehlen.

    – Peter Kirkham

    26. März 2009 um 16:11 Uhr

  • Ich habe meine Antwort aktualisiert, um alle wichtigen verfügbaren Optionen und meine Wahl, auf die ich mich konzentrieren möchte, einzubeziehen random Der in C++11 hinzugefügte Header wird durch das Standarddokument weiter unterstützt N3924: Abschreckendes rand() in C++14. Ich füge bei rand() In meiner Antwort gibt es hauptsächlich historische Überlegungen, aber auch die Realisierung von Legacy-Anwendungen.

    – Shafik Yaghmour

    17. Juni 2014 um 14:12 Uhr


  • Meine Antwort beinhaltet, wie man es vermeidet, jedes Mal die gleichen Zahlen zu erhalten <random> Header

    – Andreas DM

    10. April 2016 um 7:52 Uhr


Generierung zufalliger Gleitkommazahlen
John Dibling

rand() kann verwendet werden, um Pseudozufallszahlen in C++ zu generieren. In Kombination mit RAND_MAX und ein wenig Mathematik können Sie Zufallszahlen in jedem beliebigen Intervall Ihrer Wahl generieren. Für Lernzwecke und Spielzeugprogramme ist dies ausreichend. Wenn Sie brauchen wirklich Zufallszahlen mit Normalverteilung, müssen Sie eine fortgeschrittenere Methode anwenden.


Dadurch wird eine Zahl von 0,0 bis einschließlich 1,0 generiert.

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

Dadurch wird eine Zahl von 0,0 bis zu einer beliebigen Zahl generiert float, X:

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

Dadurch wird eine beliebige Zahl generiert LO zu einigen willkürlich HI:

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

Notiere dass der rand() Funktion wird oft nicht ausreichen, wenn Sie echte Zufallszahlen benötigen.


Vor dem Anruf rand()müssen Sie zunächst den Zufallszahlengenerator per Aufruf “seeden”. srand(). Dies sollte einmal während der Ausführung Ihres Programms erfolgen – nicht einmal bei jedem Aufruf rand(). Dies wird oft so gemacht:

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

Um anzurufen rand oder srand du musst #include <cstdlib>.

Um anzurufen timedu musst #include <ctime>.

  • Vorher aussäen nicht vergessen!

    – Anspruch

    26. März 2009 um 16:18 Uhr

  • Am besten beachten Sie, dass beide Limits inklusive sind.

    – dmckee — Ex-Moderator-Kätzchen

    26. März 2009 um 16:52 Uhr

  • Diese Antwort ist irreführend. Es wurde letzte Woche auf der Going Native 2013 behandelt; rand() Wird als schädlich angesehen, channel9.msdn.com/Events/GoingNative/2013/… für eine sehr ausführliche Erklärung.

    – Ade Miller

    8. September 2013 um 0:13 Uhr


  • Ich verstehe nicht, warum so viele Leute diese Antwort positiv bewertet haben. Es ist mathematisch falsch. RAND_MAX ist eine sehr kleine Zahl (normalerweise 2^16). Das bedeutet, dass Sie aus 23 Bits des Fließkommas nur 15 zufällig machen. Die anderen werden wahrscheinlich null sein. Sie erhalten zwar Zufallszahlen in gleichmäßiger Verteilung, aber mit geringer Genauigkeit. Zum Beispiel kann Ihr Zufallsgenerator 0,00001 und 0,00002 generieren, aber nicht 0,000017. Sie haben also eine gleichmäßige Verteilung, aber eine geringe Genauigkeit (256-mal weniger Genauigkeit als das tatsächliche Gleitkomma).

    – DanielHsH

    14. August 2014 um 8:25 Uhr


  • @DanielHsH: Das OP hat speziell gefragt, welche Mechanik verwendet werden könnte, um zufällige Floats zu generieren rand(). Diese Frage und meine Antwort konzentrierten sich speziell auf das Erlernen der Grundlagen und kümmerten sich nicht um ein hohes Maß an Präzision. Du musst gehen lernen, bevor du laufen lernen kannst.

    – Johannes Dibling

    14. August 2014 um 12:22 Uhr

1647108010 213 Generierung zufalliger Gleitkommazahlen
Shafik Yaghmur

C++11 bietet Ihnen viele neue Optionen mit random. Das kanonische Papier zu diesem Thema wäre N3551, Generierung von Zufallszahlen in C++11

Um zu sehen, warum verwenden rand() kann problematisch sein sehen rand() Wird als schädlich angesehen Präsentationsmaterial von Stephan T. Lavavej gegeben während der GoingNative 2013 Veranstaltung. Die Folien sind in den Kommentaren, aber hier ist eine direkte Verbindung.

Ich decke auch ab boost sowie verwenden rand da Legacy-Code möglicherweise noch seine Unterstützung benötigt.

Das folgende Beispiel wurde von der cpreference-Site destilliert und verwendet die std::mersenne_twister_engine Motor und die std::uniform_real_distribution die Zahlen in generiert [0,10) Intervall, wobei andere Engines und Distributionen auskommentiert sind (live sehen):

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

Die Ausgabe sieht ungefähr so ​​aus:

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

Die Ausgabe hängt davon ab, welche Distribution Sie wählen, also wenn wir uns dafür entschieden haben std::normal_distribution mit einem Wert von 2 für beide bedeuten und stddev z.B dist(2, 2) stattdessen wäre die Ausgabe ähnlich wie diese (live sehen):

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

Das Folgende ist eine modifizierte Version einiger Codes, die in dargestellt werden N3551 (live sehen) :

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

Die Ergebnisse sehen ähnlich aus wie:

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

Schub

Natürlich Boost.Random ist auch immer eine Option, hier verwende ich 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()

Wenn Sie verwenden müssen rand() dann können wir in die gehen C-Häufig gestellte Fragen für einen Führer auf Wie kann ich Fließkomma-Zufallszahlen generieren? die im Grunde genommen ein ähnliches Beispiel zum Generieren eines auf dem Intervall gibt [0,1):

#include <stdlib.h>

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

and to generate a random number in the range from [M,N):

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

  • can you fix your randMToN pls? either note that it’s [M,N] oder fügen Sie die zurück + 1. von Oben randZeroToOne. -> Denken Sie daran, es so zu nennen: randMToN(0.0, 1.0);

    – BeyelerStudios

    1. April 2016 um 7:51 Uhr

  • Seien Sie auch bei der Division durch Null vorsichtig (N-M). Eine gute Möglichkeit, mit diesem Fehler umzugehen, finden Sie hier: stackoverflow.com/questions/33058848/…

    – DrBeco

    26. Mai 2018 um 4:42 Uhr

  • Wenn jemand sehen möchte, wie es in der Standardbibliothek implementiert ist und warum es bessere Werte als die naive Division liefert, siehe hier: github.com/microsoft/STL/blob/…

    – Asmisow

    19. Dezember 2021 um 7:06 Uhr

Schauen Sie sich an Boost.Random. Du könntest so etwas machen:

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

Spielen Sie herum, Sie könnten besser das gleiche mt19937-Objekt herumreichen, anstatt jedes Mal ein neues zu konstruieren, aber hoffentlich verstehen Sie die Idee.

  • uniform_real verwendet ein halboffenes Intervall [min, max), which means you will get your minimum value but will never reach the maximum value. It’s something to consider, although if you round in some way, you can get over this problem.

    – Signal

    Jun 17, 2011 at 17:09

  • This is now part of C++11.

    – Tomas Andrle

    Nov 26, 2011 at 20:23

  • @Wolf in practical applications the odds of hitting any specific floating point value is so low that it doesn’t matter if the endpoint is included or excluded. If you need max but can use an open-ended min, you can reverse the interval easily: return min + max - gen();.

    – Mark Ransom

    Mar 19, 2015 at 21:22

Generierung zufalliger Gleitkommazahlen
Andreas DM

In modern c++ you may use the <random> header that came with c++11.
To get random float‘s you can use std::uniform_real_distribution<>.

You can use a function to generate the numbers and if you don’t want the numbers to be the same all the time, set the engine and distribution to be static.
Example:

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

It’s ideal to place the float‘s in a container such as 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 << " ";
}

Example output:

0.0518757 0.969106 0.0985112 0.0895674 0.895542

1647108011 938 Generierung zufalliger Gleitkommazahlen
Ivan Prodanov

Call the code with two float values, the code works in any range.

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

  • It might be worth mentioning that this is a potential use case for fmaf() (or the float fma() overload in C++) in C99 or C++11, which might preserve more precision. As in, fmaf((float)rand() / RAND_MAX, b - a, a).

    – Tim Čas

    Aug 9, 2017 at 1:14


  • This works, but note that it introduces a (very) small bias in the results. See here for more info (your method is the “FP Multiply (Biased)” one): pcg-random.org/posts/bounded-rands.html

    – Venryx

    Feb 25, 2021 at 9:35

  • Adding to @Venryx, there is apparantly an entire paper on this topic: hal.archives-ouvertes.fr/hal-03282794/document. Mundane as it is, this is not really a solved issue!

    – Mingye Wang

    Sep 3, 2021 at 16:47

1647108012 577 Generierung zufalliger Gleitkommazahlen
Rick

If you are using C++ and not C, then remember that in technical report 1 (TR1) and in the C++0x draft they have added facilities for a random number generator in the header file, I believe it is identical to the Boost.Random library and definitely more flexible and “modern” than the C library function, rand.

This syntax offers the ability to choose a generator (like the mersenne twister mt19937) and then choose a distribution (normal, bernoulli, binomial etc.).

Syntax is as follows (shameless borrowed from this 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;

  • It might be worth mentioning that this is a potential use case for fmaf() (or the float fma() overload in C++) in C99 or C++11, which might preserve more precision. As in, fmaf((float)rand() / RAND_MAX, b - a, a).

    – Tim Čas

    Aug 9, 2017 at 1:14


  • This works, but note that it introduces a (very) small bias in the results. See here for more info (your method is the “FP Multiply (Biased)” one): pcg-random.org/posts/bounded-rands.html

    – Venryx

    Feb 25, 2021 at 9:35

  • Adding to @Venryx, there is apparantly an entire paper on this topic: hal.archives-ouvertes.fr/hal-03282794/document. Mundane as it is, this is not really a solved issue!

    – Mingye Wang

    Sep 3, 2021 at 16:47

On some systems (Windows with VC springs to mind, currently), RAND_MAX is ridiculously small, i. e. only 15 bit. When dividing by RAND_MAX you are only generating a mantissa of 15 bit instead of the 23 possible bits. This may or may not be a problem for you, but you’re missing out some values in that case.

Oh, just noticed that there was already a comment for that problem. Anyway, here’s some code that might solve this for you:

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

Untested, but might work 🙂

  • What about float r = (float)((rand() << 9) | rand()) / RAND_MAX? (also untested)

    – Trap

    Mar 26, 2009 at 17:44

  • Argh, sorry, dividing by RAND_MAX won’t take you anywhere … the whole point of this trick was to have something that’s larger than RAND_MAX … fixed that for me as well.

    – Joey

    Mar 27, 2009 at 8:07

  • Be careful about composing random numbers without theory… consecutive calls to rand() might not be completely independent. Hint: if its a linear congruential generator, watch the low bit on consecutive calls: it alternates between 0 and 1.

    – RBerteig

    Mar 27, 2009 at 8:14

  • I know. For some applications this might be enough, though. But yes, you should probably use more than just two calls in this case. There is no silver bullet in this case, you can’t even rely on it being an LCG. Other PRNGs have weak high bits. The Boost solution should be the best here.

    – Joey

    Mar 27, 2009 at 8:46

  • (nb: The low bit returned by rand in MSVC isn’t the lowest bit of the RNG state. For 100 rand() calls I get the following: 1100100000111111101010010010011010101110110110111010011111100100000000010100011011000000100101100011. Java uses a 48-bit LCG and only uses 32 bits, VC seems to do it similarly)

    – Joey

    Mar 27, 2009 at 8:51

994710cookie-checkGenerierung zufälliger Gleitkommazahlen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy