Jak wygenerować losowe zmiennoprzecinkowe w C ++?
Myślałem, że mógłbym wziąć liczbę całkowitą i podzielić ją przez coś, czy to wystarczy?
c++
random
floating-point
hasen
źródło
źródło
random
nagłówku dodanym w C ++ 11 jest dodatkowo wzmocniony przez standardowy dokument N3924: Odradzanie rand () w C ++ 14 .rand()
W mojej odpowiedzi zawarłem głównie względy historyczne, ale zdałem sobie również sprawę z tego, że starsze aplikacje istnieją.<random>
nagłówkiemOdpowiedzi:
rand()
może być używany do generowania liczb pseudolosowych w C ++. W połączeniu zRAND_MAX
odrobiną matematyki możesz generować losowe liczby w dowolnym wybranym przedziale czasowym. Jest to wystarczające do celów uczenia się i programów zabawek. Jeśli potrzebujesz naprawdę losowych liczb o rozkładzie normalnym, musisz zastosować bardziej zaawansowaną metodę.To wygeneruje liczbę od 0,0 do 1,0 włącznie.
To generuje liczbę od 0,0 do pewnego arbitralne
float
,X
:To wygeneruje liczbę od dowolnej
LO
do dowolnejHI
:Pamiętaj, że
rand()
funkcja często nie będzie wystarczająca, jeśli potrzebujesz naprawdę losowych liczb.Przed zadzwonieniem
rand()
musisz najpierw „zaszczepić” generator liczb losowych, dzwoniącsrand()
. Należy to zrobić raz podczas uruchamiania programu - nie raz za każdym razem, gdy dzwoniszrand()
. Często odbywa się to w następujący sposób:Aby zadzwonić
rand
lubsrand
musisz#include <cstdlib>
.Aby zadzwonić
time
, musisz#include <ctime>
.źródło
rand()
. To pytanie i moja odpowiedź koncentrowały się na nauce podstaw i nie dotyczyły wysokiego stopnia precyzji. Musisz nauczyć się chodzić, zanim zaczniesz biegać.C ++ 11 daje wiele nowych opcji
random
. Artykuł kanoniczny na ten temat to N3551, generowanie liczb losowych w C ++ 11Aby dowiedzieć się, dlaczego używanie
rand()
może być problematyczne, zapoznaj się z materiałem prezentacyjnym Rand () „Uważany za szkodliwy” Stephana T. Lavaveja podanym podczas imprezy GoingNative 2013 . Slajdy są w komentarzach, ale tutaj jest bezpośredni link .Obejmuję również,
boost
a także używam,rand
ponieważ starszy kod może nadal wymagać jego obsługi.Poniższy przykład jest oddestylowany ze strony cppreference i używa silnika std :: mersenne_twister_engine i std :: uniform_real_distribution, który generuje liczby w
[0,10)
przedziale, z innymi silnikami i dystrybucjami skomentowanymi ( zobacz na żywo ):wynik będzie podobny do następującego:
Dane wyjściowe będą się różnić w zależności od wybranej dystrybucji, więc jeśli zdecydujemy się na std :: normal_distribution z wartością
2
zarówno średnią, jak i stddev, np. Wynikdist(2, 2)
będzie podobny do tego ( zobacz na żywo ):Poniżej znajduje się zmodyfikowana wersja niektórych kodów przedstawionych w
N3551
( zobacz na żywo ):Wyniki będą wyglądać podobnie do:
Podnieść
Oczywiście Boost.Random jest również zawsze opcją, tutaj używam boost :: random :: uniform_real_distribution :
skraj()
Jeśli musisz użyć
rand()
, możemy przejść do C FAQ, aby uzyskać wskazówki na temat tego, jak wygenerować zmiennoprzecinkowe liczby losowe? , który w zasadzie daje przykład podobny do tego dla generowania interwału[0,1)
:i wygenerować liczbę losową z zakresu od
[M,N)
:źródło
randMToN
pls? albo zauważ, że to,[M,N]
albo dodaj+ 1.
powyższerandZeroToOne
. -> pomyśl, jak nazwać to w ten sposób:randMToN(0.0, 1.0);
(N-M)
. Dobry sposób radzenia sobie z tym błędem można znaleźć tutaj: stackoverflow.com/questions/33058848/...Spójrz na Boost.Random . Możesz zrobić coś takiego:
Pobaw się, lepiej lepiej przekazać ten sam obiekt MT19937 zamiast budować nowy za każdym razem, ale mam nadzieję, że masz pomysł.
źródło
max
, ale można użyć otwartymin
, można odwrócić interwał prosty sposób:return min + max - gen();
.W wersji współczesnej
c++
możesz użyć<random>
dołączonego nagłówkac++11
.Aby uzyskać losowe
float
, możesz użyćstd::uniform_real_distribution<>
.Możesz użyć funkcji do wygenerowania liczb, a jeśli nie chcesz, aby liczby były zawsze takie same , ustaw silnik i rozkład
static
.Przykład:
Idealnie jest umieścić go
float
w pojemniku, takim jakstd::vector
:Przykładowe dane wyjściowe:
źródło
std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
jest technicznie niepoprawny, 1.0 nigdy nie zostanie wygenerowany, patrz en.cppreference.com/w/cpp/numeric/random/…To create a distribution over the closed interval [a,b], std::nextafter(b, std::numeric_limits<RealType>::max()) may be used as the second parameter.
Wywołaj kod z dwiema
float
wartościami, kod działa w dowolnym zakresie.źródło
fmaf()
(lubfma()
przeciążenia zmiennoprzecinkowego w C ++) w C99 lub C ++ 11, który może zachować większą precyzję. Jak w,fmaf((float)rand() / RAND_MAX, b - a, a)
.Jeśli używasz C ++, a nie C, pamiętaj, że w raporcie technicznym 1 (TR1) i w wersji roboczej C ++ 0x dodali ułatwienia dla generatora liczb losowych w pliku nagłówkowym, uważam, że jest on identyczny z Boost. Random biblioteka i zdecydowanie bardziej elastyczna i „nowoczesna” niż funkcja biblioteki C, rand.
Ta składnia oferuje możliwość wyboru generatora (takiego jak Mersenne Twister MT19937), a następnie wyboru rozkładu (normalny, bernoulli, dwumianowy itp.).
Składnia jest następująca (bezwstydnie zapożyczony z tej strony ):
źródło
W niektórych systemach (Windows z pamięcią VC, obecnie)
RAND_MAX
jest absurdalnie mały, tj. mi. tylko 15 bitów. Przy dzieleniu przezRAND_MAX
generujesz mantysę 15 bitów zamiast 23 możliwych bitów. To może, ale nie musi stanowić problemu, ale w tym przypadku brakuje niektórych wartości.Och, właśnie zauważyłem, że był już komentarz do tego problemu. Tak czy inaczej, oto kod, który może rozwiązać ten problem:
Nie przetestowano, ale może działać :-)
źródło
drand48(3)
jest standardowym sposobem POSIX. Glibc udostępnia również wersję wielowejściowy,drand48_r(3)
.Funkcja została uznana za przestarzałą w SVID 3, ale nie podano odpowiedniej alternatywy, więc IEEE Std 1003.1-2013 nadal ją zawiera i nie ma żadnych notatek, że w najbliższym czasie będzie działać.
W systemie Windows standardowym sposobem jest CryptGenRandom () .
źródło
Żadna z dotychczasowych odpowiedzi nie była dla mnie satysfakcjonująca, dlatego napisałem nową losową funkcję float. Dokonuje bitowych założeń dotyczących typu danych zmiennoprzecinkowych. Nadal potrzebuje funkcji rand () z co najmniej 15 losowymi bitami.
źródło
Moim zdaniem powyższa odpowiedź daje trochę „losowych” liczb zmiennoprzecinkowych, ale żadna z nich nie jest naprawdę zmiennoprzecinkowa (tzn. Brakuje im części reprezentacji liczb zmiennoprzecinkowych). Zanim przejdę do mojej implementacji, rzućmy okiem na standardowy format ANSI / IEEE dla pływaków:
| znak (1-bit) | e (8 bitów) | f (23-bitowy) |
liczba reprezentowana przez to słowo to (znak * *) * 2 ^ e * 1.f
zwróć uwagę, że liczba „e” jest liczbą tendencyjną (z odchyleniem 127), a zatem w zakresie od -127 do 126. Najprostszą (a właściwie najbardziej losową) funkcją jest po prostu zapisanie danych losowej liczby int w liczbie zmiennoprzecinkowej, a zatem
zwróć uwagę, że jeśli to zrobisz
float f = (float)rand();
, przekształci liczbę całkowitą na liczbę zmiennoprzecinkową (w ten sposób 10 zmieni się w 10,0).Więc teraz, jeśli chcesz ograniczyć maksymalną wartość, możesz zrobić coś takiego (nie jestem pewien, czy to działa)
ale jeśli spojrzysz na strukturę pływaka, zobaczysz, że maksymalna wartość pływaka wynosi (około) 2 ^ 127, co jest znacznie większe niż maksymalna wartość int (2 ^ 32), co wyklucza znaczną część liczby, które mogą być reprezentowane przez liczbę zmiennoprzecinkową. Oto moje końcowe wdrożenie:
użycie tej funkcji
randf(0, 8, 0)
zwróci liczbę losową z zakresu od 0,0 do 255,0źródło
int e = (rand() % (max_exp - min_exp)) + min_exp_mod;
i mantysy:int f = (int)(frac_mod * (float)rand() / RAND_MAX);
zastępując ich odpowiednie linie powyżej. Zauważ, że błąd mantysy jest poważny: dlaRAND_MAX
mniejszych1 << 23
losowałbyś tylko mniejsze znaczące bity i otrzymywałeś zero dla najbardziej znaczących bitów przez cały czas!Jeśli wiesz, że twój format zmiennoprzecinkowy to IEEE 754 (prawie wszystkie współczesne procesory, w tym Intel i ARM), możesz zbudować losową liczbę zmiennoprzecinkową z losowej liczby całkowitej, stosując metody bitowe. Należy to wziąć pod uwagę tylko wtedy, gdy nie masz dostępu do C ++ 11
random
lubBoost.Random
oba są znacznie lepsze.Zapewni to lepszą dystrybucję niż ta wykorzystująca podział.
źródło
return (float)random23 / (1 << 23)
. (Tak, właśnie to przetestowałem , modyfikując twoją funkcjęrandom32
jako parametr i uruchamiając ją dla wszystkich wartości od zera do(1 << 23)-1
. I tak, twoja metoda rzeczywiście daje dokładnie takie same wyniki jak dzielenie przez1 << 23
.)W przypadku C ++ może generować rzeczywiste liczby zmiennoprzecinkowe w zakresie określonym przez
dist
zmiennąźródło
rand () zwraca wartość int między 0 a RAND_MAX. Aby uzyskać liczbę losową z zakresu od 0,0 do 1,0, najpierw rzutuj int int przez rand () na liczbę zmiennoprzecinkową, a następnie podziel przez RAND_MAX.
źródło
Nie mogłem opublikować dwóch odpowiedzi, więc oto drugie rozwiązanie. log2 liczb losowych, masywne odchylenie w kierunku 0,0f, ale to naprawdę losowa liczba zmiennoprzecinkowa od 1,0f do 0,0f.
źródło