Dlaczego rand () w języku C ++ wydaje się generować tylko liczby tego samego rzędu wielkości?


146

W małej aplikacji napisanej w C / C ++ mam problem z randfunkcją i być może zalążkiem:

Chcę utworzyć sekwencję liczb losowych, które mają różne rzędy, tj. Mają różne wartości logarytmu (podstawa 2). Wydaje się jednak, że wszystkie uzyskane liczby są tego samego rzędu, wahając się między 2 ^ 25 a 2 ^ 30.

Czy to dlatego, że rand()jest zasiane czasem uniksowym, który jest obecnie stosunkowo dużą liczbą? O czym ja zapominam? Rozsiewam rand()tylko raz na początku main().


7
FWIW, czy to C czy C ++? Jeśli przez C / C ++ masz na myśli, że faktycznie możesz używać C ++, a wzmianka o C była po prostu przypadkowa, może to en.cppreference.com/w/cpp/numeric/random/binomial_distribution może pomóc.
— R. Martinho Fernandes

9
Niestety obstawiłeś niewłaściwego konia. Nasiona nie powinny być twoim problemem. Twoim problemem była nieprawidłowa oczekiwana dystrybucja. Ponieważ bezstronny programista spodziewałby rand()się zwrócić równomiernie rozłożone liczby (dokumentacja z wysokim rankingiem Google wyraźnie tak mówi), nie sądzę, aby to pytanie było przydatne dla przyszłych czytelników. Dlatego głosuj negatywnie, ale nie zniechęcaj się do używania SO.
— Cesarz Orionii

12
@ doug65536 "... gdzie żadna liczba się nie powtarza" - to nie jest przypadkowe! Mógłbym sfinansować swoją emeryturę przy stole do gry w kości, gdyby moje kości rand () nigdy nie zwracały tej samej liczby dwa razy, dopóki wszystkie możliwe liczby nie zostały zwrócone.
— Chris Gregg,

6
@GalacticCowboy Nie myl okresowości z powtórzeniami poszczególnych liczb. Z cytowanego artykułu w Wikipedii: „powtarzający się wynik nie oznacza, że ​​został osiągnięty koniec okresu, ponieważ jego stan wewnętrzny może być większy niż wynik”. Byłoby bardzo, bardzo źle, gdyby PRNG wytwarzał wartość, a następnie miał gwarancję, że nie wytworzy tej wartości ponownie, dopóki wszystkie wartości nie zostaną zwrócone.
— Chris Gregg

12
Doug65536, nikt nie wybiera walk. Po prostu poprawnie stwierdzają, że się mylisz. PRNG mógłby całkiem szczęśliwie wypuścić następujące rzeczy, gdybym chciał RAND między 1 a 10: 2 4 7 2 8 1 5 9 7 3 To byłoby całkowicie poprawne, pomimo wielu 2 i 7. Myślę, że mylisz PRNG z funkcją tasowania na iPhonie.
— Relaks na Cyprze

Odpowiedzi:


479

Jest tylko 3% liczb od 1 do 2 30, które NIE są między 2 25 a 2 30 . Więc to brzmi całkiem normalnie :)

Ponieważ 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%


36
Tak, słuszna uwaga! Jest 31 razy więcej liczb między 2 ^ 25 a 2 ^ 30 niż między 1 a 2 ^ 25 :) dzięki za szybką odpowiedź. Muszę więc przemyśleć program. Pytanie odpowiedział.
— Tallaron Mathias

1
@TallaronMathias Rozważ skrócenie liczby poprzez >>przesunięcie bitów - to da ci mniejsze liczby. (Lub biorąc moduł z %.)
— Sean Allred

13
Spodziewam się, że jest to oczywiste dla większości programistów: Każda liczba całkowita bez znaku mniej niż 2 ^ 25 musi mieć swoje pierwsze 7 bitów równa się 0- i jeśli każdy bit jest przypadkowy ...
— BlueRaja - Danny Pflughoeft

118
@ BlueRaja-DannyPflughoeft - gdyby prawdopodobieństwa były oczywiste, kasyna wypadłyby z biznesu.
— Brett Hale

26
@BrettHale - nie sądzę jednak, że programiści są docelową grupą demograficzną kasyna.
— EkoostikMartin

272

Jaśniejszy zielony to region między 0 a 2 25 ; ciemniejsza zieleń to obszar między 2 25 a 2 30 . Tiki mają moc 2.

dystrybucja


42

Musisz być bardziej precyzyjny: chcesz różnych wartości logarytmu o podstawie 2, ale jaki rozkład chcesz dla tego? Standardowe funkcje rand () generują równomierną dystrybucję, będziesz musiał przekształcić te dane wyjściowe za pomocą funkcji kwantyli związanej z żądaną dystrybucją.

Jeśli podasz nam dystrybucję, możemy Ci powiedzieć, jakiej quantilefunkcji potrzebujesz.


13
+1, dystrybucja to kluczowy termin. Nie ma sensu mówić o liczbach losowych, kiedy nic nie wiadomo o rozkładzie. Mundur to tylko szczególny przypadek, aczkolwiek ważny. Może być dobrym miejscem na wskazanie różnych dystrybucji ze standardowej biblioteki C ++ 11.
— leftaround około

18

Jeśli chcesz różnych rzędów wielkości, dlaczego po prostu nie spróbować pow(2, rand())? A może wybierz kolejność bezpośrednio jako rand (), jak zasugerował Harold?


3
dobry pomysł, ale powinieneś poprawić swoją odpowiedź używając pow zamiast ^ (co jest logicznym operatorem xor, a nie potęgą, w języku C).
— kriss

6
Ponieważ rand()może wzrosnąć do RAND_MAX, naprawdę musisz przeskalować liczbę losową, aby wynik się nie przepełnił ...
— Floris

@Floris: ale jeśli wyskalujesz mały policzalny zakres w bardzo dużym zakresie, będziesz miał DUŻO dziur, co prawdopodobnie nie jest tym, czego oczekuje OP.
— André Caron

13

@ C4stor zrobił świetną uwagę. Ale dla bardziej ogólnego przypadku i łatwiejszego do zrozumienia dla człowieka (podstawa 10): dla zakresu od 1 do 10 ^ n ~ 90% liczb mieści się w przedziale od 10 ^ (n-1) do 10 ^ n, zatem ~ 99% liczb waha się od 10 ^ (n-2) do 10 ^ n. Dodawaj tyle miejsc po przecinku, ile chcesz.

Zabawna matematyka, jeśli będziesz to robić dla n, zobaczysz, że od 1 do 10 ^ n, 99,9999 ...% = 100% liczb to od 10 ^ 0 do 10 ^ n tą metodą.

Teraz o kodzie, jeśli chcesz losową liczbę o losowych rzędach wielkości, od 0 do 10 ^ n, możesz zrobić:

  1. Wygeneruj małą liczbę losową od 0 do n

  2. Jeśli znasz zakres, który ma n, wygeneruj dużą liczbę losową rzędu 10 ^ k, gdzie k> max {n}.

  3. Wytnij dłuższą liczbę losową, aby uzyskać n cyfr tej dużej liczby losowej.


46
Masz całkowitą rację, ale aby uzyskać NAPRAWDĘ łatwą do zrozumienia odpowiedź, OP powinien zadać sobie pytanie, dlaczego 90% liczb losowych od 1 do 100 to dwie cyfry.
— Zapytaj o Monikę,

13

Podstawowa (i poprawna) odpowiedź została już podana i zaakceptowana powyżej: jest 10 liczb od 0 do 9, 90 liczb od 10 do 99, 900 od 100 do 999 itd.

Aby uzyskać wydajny obliczeniowo sposób uzyskania rozkładu o rozkładzie w przybliżeniu logarytmicznym, należy przesunąć w prawo liczbę losową o liczbę losową:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Nie jest doskonały, ale jest znacznie szybszy niż komputer pow(2, rand()*scalefactor) . Będzie „grudkowaty” w tym sensie, że rozkład będzie jednorodny dla liczb w ramach współczynnika 2 (jednolity dla 128 do 255, połowa gęstości dla 256 do 1023 itd.).

Oto histogram częstotliwości liczb od 0 do 31 (w próbkach 1M):

wprowadź opis obrazu tutaj


czepianie się: to zachęca do bardzo małych liczb bardziej, niż można by się spodziewać. Prawdopodobieństwo uzyskania zera jest znacznie wyższe niż 10.
— Mooing Duck,

Cóż - chodzi o to, aby zachęcić małe liczby, więc cieszę się, że działa! Przeprowadziłem symulację Monte Carlo i daje mi to dwukrotny spadek prawdopodobieństwa, gdy liczby się podwajają - podobnie jak rozkład logów. Zaktualizowana odpowiedź ze zdjęciem.
— Floris

nie, mam na myśli, rand()>>(rand()&31);intuicyjnie można by oczekiwać, że 1/32 liczby będzie miała 32 bity, a 1/32 liczby miała 31 bitów, a 1/32 liczby miała 30 bitów itd. nie wyniki, które otrzymujesz, tylko około 1/64 liczby daje 32 bity, podczas gdy prawie połowa powinna wynosić 0. Ponieważ moja psychiczna matematyka nie zgadza się z twoimi pomiarami, będę musiał wykonać własne pomiary, aby obliczyć to na zewnątrz.
— Mooing Duck

2
Nie chcę powiedzieć, że twój kod jest zły. Prawdopodobnie to bym zrobił. Po prostu zasługuje na ostrzeżenie, że nie ma wyników tak rozłożone, jak można by się spodziewać.
— Mooing Duck,

1
Myślę, że problem wynika z myślenia o 0 jako liczbie 1-bitowej ... to rodzaj zagadki, na którą natrafisz, gdy mieszasz liczby całkowite i logarytmy. Ale to było dobre ćwiczenie i dałeś mi coś do przemyślenia. „Przetestuj ograniczenia swojego algorytmu” - nigdy się nie zestarzeje.
— Floris,

5

Jest dokładnie taka sama liczba liczb od 0 do 2 ^ 29 i 2 ^ 29 i 2 ^ 30.

Inny sposób spojrzenia na problem: rozważ binarną reprezentację generowanej liczby losowej, prawdopodobieństwo, że najwyższy bit to 1, równa się 1/2, a zatem otrzymujesz rząd 29 w połowie przypadków. Chcesz zobaczyć liczbę, która byłaby poniżej 2 ^ 25, ale to oznacza, że ​​5 najwyższych bitów to zero, co zdarza się z niskim prawdopodobieństwem 1/32. Są szanse, że nawet jeśli uruchomisz go przez długi czas, w ogóle nie zobaczysz zamówienia poniżej 15 (prawdopodobieństwo jest takie, jak wyrzucenie 6 6 razy z rzędu).

A teraz część twojego pytania o ziarno. Nie, ziarno prawdopodobnie nie może określić zakresu, z którego generowane są liczby, po prostu określa pierwszy, początkowy element. Pomyśl o rand () jako sekwencji wszystkich możliwych liczb w zakresie (z góry określona permutacja). Ziarno określa, gdzie zaczniesz rysować liczby z sekwencji. Dlatego jeśli chcesz (pseudo) losowości, do inicjalizacji sekwencji używasz bieżącego czasu: nie obchodzi cię, że pozycja, od której zaczynasz, nie jest równomiernie rozłożona, liczy się tylko to, że nigdy nie zaczynasz z tej samej pozycji.


2

jego użycie pow(2,rand()) da odpowiedzi w kolejności pożądanej wielkości !!


2

Jeśli chcesz użyć liczb losowych z usługi online, możesz użyć do tego wget, możesz również zobaczyć, że możesz również użyć usług takich jak random.org do generowania liczb losowych, możesz je złapać za pomocą wget, a następnie odczytać liczby z pobrany plik

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html


Witamy w SO. proszę powstrzymać się od publikowania linków jako odpowiedzi. Możesz podać szczegółowy szkic odpowiedzi, pozostawiając szczegóły do ​​przeczytania za pomocą linków.
— Shai
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.