Muszę wygenerować liczby losowe w określonym przedziale, [max; min].
Ponadto liczby losowe powinny być równomiernie rozmieszczone w przedziale, a nie znajdować się w określonym punkcie.
Obecnie generuję jako:
for(int i=0; i<6; i++)
{
DWORD random = rand()%(max-min+1) + min;
}
Z moich testów losowe liczby są generowane tylko wokół jednego punktu.
Example
min = 3604607;
max = 7654607;
Wygenerowane liczby losowe:
3631594
3609293
3630000
3628441
3636376
3621404
Z odpowiedzi poniżej: OK, RAND_MAX to 32767. Jestem na platformie C ++ Windows. Czy istnieje inna metoda generowania liczb losowych o jednolitym rozkładzie?
rand()
jednolity. Z jakiej biblioteki korzystasz?cstdlib.h
„srand()
nie jest jednolity: cplusplus.com/reference/cstdlib/randOdpowiedzi:
Dlaczego
rand
to zły pomysłWiększość odpowiedzi, które tu otrzymałeś, wykorzystuje
rand
funkcję i operator modułu. Ta metoda może nie generować liczb jednolicie (zależy to od zakresu i wartościRAND_MAX
) i dlatego jest odradzana.C ++ 11 i generowanie w zakresie
W C ++ 11 pojawiło się wiele innych opcji. Z których jeden odpowiada Państwa wymaganiom, do generowania liczb losowych w zakresie, całkiem ładnie:
std::uniform_int_distribution
. Oto przykład:const int range_from = 0; const int range_to = 10; std::random_device rand_dev; std::mt19937 generator(rand_dev()); std::uniform_int_distribution<int> distr(range_from, range_to); std::cout << distr(generator) << '\n';
A oto działający przykład.
Funkcja szablonu może pomóc niektórym:
template<typename T> T random(T range_from, T range_to) { std::random_device rand_dev; std::mt19937 generator(rand_dev()); std::uniform_int_distribution<T> distr(range_from, range_to); return distr(generator); }
Inne generatory losowe
<random>
Nagłówek oferuje niezliczone inne generatory liczb losowych z różnego rodzaju rozkładów tym Bernoulliego, Poissona i normalne.Jak mogę przetasować pojemnik?
Norma przewiduje
std::shuffle
, które można wykorzystać w następujący sposób:std::vector<int> vec = {4, 8, 15, 16, 23, 42}; std::random_device random_dev; std::mt19937 generator(random_dev()); std::shuffle(vec.begin(), vec.end(), generator);
Algorytm zmieni kolejność elementów losowo, z liniową złożonością.
Boost.Random
Inną alternatywą, jeśli nie masz dostępu do kompilatora C ++ 11 +, jest użycie Boost.Random . Jego interfejs jest bardzo podobny do interfejsu C ++ 11.
źródło
mt19937
typ?1 9 6 2 8 7 1 4 7 7
. Czy wiesz, jak to zrobić losowo za każdym razem, gdy uruchamiamy program?[edytuj] Ostrzeżenie: nie używaj
rand()
do statystyk, symulacji, kryptografii ani niczego poważnego.Wystarczy, że liczby wyglądają na przypadkowe dla typowego człowieka w pośpiechu, koniec.
Zobacz odpowiedź @ Jefffrey, aby uzyskać lepsze opcje, lub tę odpowiedź dla liczb losowych zabezpieczonych kryptograficznie.
Ogólnie rzecz biorąc, wysokie bity pokazują lepszy rozkład niż niskie bity, więc zalecanym sposobem generowania liczb losowych z zakresu do prostych celów jest:
((double) rand() / (RAND_MAX+1)) * (max-min+1) + min
Uwaga : upewnij się, że RAND_MAX + 1 nie przepełnia (dzięki Demi)!
Podział generuje liczbę losową w przedziale [0, 1); „rozciągnij” to do wymaganego zakresu. Tylko wtedy, gdy max-min + 1 zbliża się do RAND_MAX, potrzebujesz funkcji "BigRand ()", takiej jak opublikowana przez Mark Ransom.
Pozwala to również uniknąć pewnych problemów z krojeniem z powodu modulo, które może jeszcze bardziej pogorszyć twoje liczby.
Wbudowany generator liczb losowych nie gwarantuje jakości wymaganej do symulacji statystycznych. Liczby mogą "wyglądać losowo" dla człowieka, ale dla poważnych zastosowań powinieneś wziąć coś lepszego - lub przynajmniej sprawdzić jego właściwości (rozkład równomierny jest zwykle dobry, ale wartości mają tendencję do korelacji, a sekwencja jest deterministyczna ). Knuth ma świetny (choć trudny do odczytania) traktat o generatorach liczb losowych, a ostatnio odkryłem, że LFSR jest doskonały i cholernie prosty w implementacji, biorąc pod uwagę jego właściwości.
źródło
( rand() / ((double)RAND_MAX+1)) * (max-min+1) + min
prostu przenieś konwersję na podwojenie i uniknij problemu.Chciałabym uzupełnić doskonałe odpowiedzi Angry Shoe i Peterchena krótkim przeglądem stanu techniki w 2015 roku:
Kilka dobrych wyborów
randutils
randutils
Biblioteki (prezentacja) jest interesującą nowością, oferując prosty interfejs oraz (zadeklarowanej) solidne losowe możliwości. Ma wady polegające na tym, że dodaje zależność od projektu, a ponieważ jest nowy, nie został gruntownie przetestowany. W każdym razie, będąc wolnym (licencja MIT) i tylko nagłówkiem, myślę, że warto spróbować.Minimalna próbka: rzut kostką
#include <iostream> #include "randutils.hpp" int main() { randutils::mt19937_rng rng; std::cout << rng.uniform(1,6) << "\n"; }
Nawet jeśli ktoś nie jest zainteresowany biblioteką, strona internetowa ( http://www.pcg-random.org/ ) zawiera wiele interesujących artykułów na temat generowania liczb losowych w ogóle, aw szczególności biblioteki C ++.
Boost.Random
Boost.Random
(dokumentacja) jest biblioteką, która inspirowanaC++11
„s<random>
, z którymi akcje znacznie interfejsu. Chociaż teoretycznie jest również zależnością zewnętrzną,Boost
ma obecnie status biblioteki „quasi-standardowej”, a jejRandom
moduł można uznać za klasyczny wybór dla dobrej jakości generowania liczb losowych. Ma dwie zalety w stosunku doC++11
rozwiązania:random_device
metody zastosowania systemu specyficzne zaoferować posiew dobrej jakościJedyną małą wadą jest to, że oferta modułu
random_device
nie obejmuje samego nagłówka, należy ją skompilować i połączyćboost_random
.Minimalna próbka: rzut kostką
#include <iostream> #include <boost/random.hpp> #include <boost/nondet_random.hpp> int main() { boost::random::random_device rand_dev; boost::random::mt19937 generator(rand_dev()); boost::random::uniform_int_distribution<> distr(1, 6); std::cout << distr(generator) << '\n'; }
Chociaż minimalna próbka działa dobrze, prawdziwe programy powinny korzystać z kilku ulepszeń:
mt19937
athread_local
: generator jest dość obszerny (> 2 KB) i lepiej nie umieszczać go na stosiemt19937
z więcej niż jedną liczbą całkowitą: Mersenne Twister ma duży stan i może skorzystać z większej entropii podczas inicjalizacjiKilka niezbyt dobrych wyborów
Biblioteka C ++ 11
Będąc najbardziej idiomatycznym rozwiązaniem,
<random>
biblioteka nie oferuje zbyt wiele w zamian za złożoność interfejsu nawet dla podstawowych potrzeb. Wada tkwi wstd::random_device
tym, że Standard nie narzuca żadnej minimalnej jakości dla swoich wyników (o ileentropy()
zwraca0
), a od 2015 r. MinGW (nie jest najczęściej używanym kompilatorem, ale raczej nie jest ezoterycznym wyborem) zawsze będzie drukować4
na minimalnej próbce.Minimalna próbka: rzut kostką
#include <iostream> #include <random> int main() { std::random_device rand_dev; std::mt19937 generator(rand_dev()); std::uniform_int_distribution<int> distr(1, 6); std::cout << distr(generator) << '\n'; }
Jeśli implementacja nie jest zepsuta, to rozwiązanie powinno być równoważne z Boost i obowiązują te same sugestie.
Rozwiązanie Godota
Minimalna próbka: rzut kostką
#include <iostream> #include <random> int main() { std::cout << std::randint(1,6); }
To proste, skuteczne i zgrabne rozwiązanie. Tylko wada, kompilacja zajmie trochę czasu - około dwóch lat, pod warunkiem, że C ++ 17 zostanie wydany na czas, a
randint
funkcja eksperymentalna zostanie zatwierdzona do nowego standardu. Być może do tego czasu poprawią się również gwarancje jakości siewu.Gorzej-to-lepsze rozwiązanie
Minimalna próbka: rzut kostką
#include <cstdlib> #include <ctime> #include <iostream> int main() { std::srand(std::time(nullptr)); std::cout << (std::rand() % 6 + 1); }
Stare rozwiązanie C jest uważane za szkodliwe i nie bez powodu (zobacz inne odpowiedzi tutaj lub tę szczegółową analizę ). Mimo to ma swoje zalety: jest prosty, przenośny, szybki i uczciwy, w tym sensie, że wiadomo, że otrzymywane liczby losowe nie są przyzwoite, a zatem nie ma pokusy, aby używać ich do poważnych celów.
Troll księgowy
Minimalna próbka: rzut kostką
#include <iostream> int main() { std::cout << 9; // http://dilbert.com/strip/2001-10-25 }
Podczas gdy 9 to dość nietypowy wynik jak na zwykły rzut kostką, trzeba podziwiać doskonałe połączenie dobrych cech tego rozwiązania, które jest najszybsze, najprostsze, najbardziej przyjazne dla pamięci podręcznej i najbardziej przenośne. Zamieniając 9 na 4, uzyskuje się doskonały generator dla każdego rodzaju śmierci Dungeons and Dragons, jednocześnie unikając obciążonych symbolami wartości 1, 2 i 3. Jedyną małą wadą jest to, że z powodu złego temperamentu księgowych trolli Dilberta, ten program faktycznie wywołuje niezdefiniowane zachowanie.
źródło
randutils
Biblioteka nazywa się teraz PCG.Jeśli
RAND_MAX
wynosi 32767, można łatwo podwoić liczbę bitów.int BigRand() { assert(INT_MAX/(RAND_MAX+1) > RAND_MAX); return rand() * (RAND_MAX+1) + rand(); }
źródło
rand
połączenie powróci,0x1234
a drugie0x5678
, otrzymasz0x12345678
. To jedyna liczba, którą możesz uzyskać, zaczynając od0x1234
, ponieważ następna liczba zawsze będzie0x5678
. Otrzymujesz wyniki 32-bitowe, ale masz tylko 32768 możliwych liczb.Jeśli możesz, użyj Boost . Miałem szczęście z ich przypadkową biblioteką .
uniform_int
powinieneś robić, co chcesz.źródło
uniform_int
. Uzyskanie obiektywnego wyniku jest dość łatwe, pojawiło się wiele pytań, które demonstrują tę metodę.Jeśli obawiasz się losowości, a nie szybkości, powinieneś użyć bezpiecznej metody generowania liczb losowych. Można to zrobić na kilka sposobów ... Najłatwiejszym jest użycie generatora liczb losowych OpenSSL .
Możesz także napisać własne, korzystając z algorytmu szyfrowania (takiego jak AES ). Wybierając ziarno i IV, a następnie stale ponownie szyfrując wynik funkcji szyfrowania. Korzystanie z OpenSSL jest łatwiejsze, ale mniej męskie.
źródło
Powinieneś spojrzeć na
RAND_MAX
swój konkretny kompilator / środowisko. Myślę, że zobaczysz te wyniki, jeślirand()
generuje losową liczbę 16-bitową. (wydaje się, że zakładasz, że będzie to liczba 32-bitowa).Nie mogę obiecać, że to odpowiedź, ale proszę opublikuj swoją wartość
RAND_MAX
i trochę więcej szczegółów na temat swojego środowiska.źródło
Sprawdź, co
RAND_MAX
jest w Twoim systemie - domyślam się, że to tylko 16 bitów, a Twój zasięg jest na to za duży.Poza tym zobacz tę dyskusję na temat: Generowanie losowych liczb całkowitych w pożądanym zakresie i uwagi dotyczące używania (lub nie) funkcji C rand () .
źródło
To nie jest kod, ale ta logika może ci pomóc.
static double rnd(void) { return (1.0 / (RAND_MAX + 1.0) * ((double)(rand())) ); } static void InitBetterRnd(unsigned int seed) { register int i; srand( seed ); for( i = 0; i < POOLSIZE; i++){ pool[i] = rnd(); } } // This function returns a number between 0 and 1 static double rnd0_1(void) { static int i = POOLSIZE-1; double r; i = (int)(POOLSIZE*pool[i]); r = pool[i]; pool[i] = rnd(); return (r); }
źródło
Jeśli chcesz, aby liczby były równomiernie rozłożone w zakresie, powinieneś podzielić swój zakres na kilka równych sekcji, które reprezentują liczbę potrzebnych punktów. Następnie uzyskaj losową liczbę z min / max dla każdej sekcji.
Jako kolejna uwaga, prawdopodobnie nie powinieneś używać,
rand()
ponieważ nie jest to zbyt dobre w generowaniu liczb losowych. Nie wiem, na jakiej platformie pracujesz, ale prawdopodobnie jest lepsza funkcja, którą możesz nazwaćrandom()
.źródło
Powinno to zapewnić równomierne rozłożenie w całym zakresie
[low, high)
bez używania wartości zmiennoprzecinkowych, o ile całkowity zakres jest mniejszy niż RAND_MAX.uint32_t rand_range_low(uint32_t low, uint32_t high) { uint32_t val; // only for 0 < range <= RAND_MAX assert(low < high); assert(high - low <= RAND_MAX); uint32_t range = high-low; uint32_t scale = RAND_MAX/range; do { val = rand(); } while (val >= scale * range); // since scale is truncated, pick a new val until it's lower than scale*range return val/scale + low; }
a dla wartości większych niż RAND_MAX chcesz coś takiego
uint32_t rand_range(uint32_t low, uint32_t high) { assert(high>low); uint32_t val; uint32_t range = high-low; if (range < RAND_MAX) return rand_range_low(low, high); uint32_t scale = range/RAND_MAX; do { val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval } while (val >= range); return val + low; }
W przybliżeniu tak działa std :: uniform_int_distribution.
źródło
Ze swej natury mała próbka liczb losowych nie musi być równomiernie rozłożona. W końcu są przypadkowe. Zgadzam się, że jeśli generator liczb losowych generuje liczby, które konsekwentnie wydają się być zgrupowane, to prawdopodobnie jest z nim coś nie tak.
Pamiętaj jednak, że losowość niekoniecznie jest jednolita.
Edycja: dodałem „małą próbkę” dla wyjaśnienia.
źródło
Rozwiązanie podane przez człowieka 3 rand dla liczby od 1 do 10 włącznie to:
j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));
W twoim przypadku byłoby to:
j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));
Oczywiście nie jest to idealna przypadkowość ani jednorodność, jak wskazują inne komunikaty, ale w większości przypadków to wystarcza.
źródło
@Rozwiązanie
((double) rand() / (RAND_MAX+1)) * (max-min+1) + min
Ostrzeżenie : nie zapomnij z powodu rozciągania i możliwych błędów precyzji (nawet jeśli RAND_MAX był wystarczająco duży), będziesz w stanie wygenerować tylko równomiernie rozłożone „pojemniki”, a nie wszystkie liczby w [min, max].
@ Rozwiązanie: Bigrand
Ostrzeżenie : Zwróć uwagę, że podwaja to bity, ale nadal nie będzie w stanie wygenerować wszystkich liczb w twoim zakresie, tj. Niekoniecznie jest prawdą, że BigRand () wygeneruje wszystkie liczby w swoim zakresie.
Informacja : Twoje podejście (modulo) jest „w porządku”, o ile zakres rand () przekracza zakres interwałów, a rand () jest „jednolity”. Błąd najwyżej dla pierwszych liczb maksymalnych i minimalnych to 1 / (RAND_MAX +1).
Sugeruję również, aby przejść do nowego pakietu losowego e również w C ++ 11, który oferuje lepsze i więcej odmian implementacji niż rand ().
źródło
Oto rozwiązanie, które wymyśliłem:
#include "<stdlib.h>" int32_t RandomRange(int32_t min, int32_t max) { return (rand() * (max - min + 1) / (RAND_MAX + 1)) + min; }
Jest to rozwiązanie wiaderkowe, koncepcyjnie podobne do rozwiązań, które używają
rand() / RAND_MAX
do uzyskania zakresu zmiennoprzecinkowego między 0-1, a następnie zaokrąglić go do wiadra. Jednak używa obliczeń czysto całkowitych i korzysta z podziału na liczby całkowite, aby zaokrąglić wartość do najbliższego przedziału.Przyjmuje kilka założeń. Po pierwsze, zakłada, że
RAND_MAX * (max - min + 1)
zawsze będzie pasować doint32_t
. JeśliRAND_MAX
użyto 32767 i 32-bitowych obliczeń int, maksymalny możliwy zakres to 32767. Jeśli Twoja implementacja ma znacznie większy RAND_MAX, możesz temu zaradzić, używając większej liczby całkowitej (podobnejint64_t
) do obliczeń. Po drugie, jeśliint64_t
jest używany, aleRAND_MAX
nadal wynosi 32767, w zakresach większych niżRAND_MAX
zaczniesz pojawiać się „dziury” w możliwych numerach wyjściowych. Jest to prawdopodobnie największy problem z każdym rozwiązaniem pochodzącym ze skalowaniarand()
.Testowanie na ogromnej liczbie iteracji pokazuje jednak, że ta metoda jest bardzo jednolita dla małych zakresów. Jest jednak możliwe (i prawdopodobne), że z matematycznego punktu widzenia ma to pewne niewielkie odchylenie i prawdopodobnie powoduje problemy, gdy zbliża się zakres
RAND_MAX
. Przetestuj to sam i zdecyduj, czy spełnia Twoje potrzeby.źródło
Oczywiście poniższy kod nie daje liczb losowych, ale liczbę pseudolosową. Użyj poniższego kodu
#define QUICK_RAND(m,n) m + ( std::rand() % ( (n) - (m) + 1 ) )
Na przykład:
int myRand = QUICK_RAND(10, 20);
Musisz zadzwonić
srand(time(0)); // Initialize random number generator.
w przeciwnym razie liczby nie będą prawie przypadkowe.
źródło
Właśnie znalazłem to w Internecie. To powinno działać:
DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));
źródło