Generuj liczby losowe równomiernie w całym zakresie

95

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?

anand
źródło
2
Zbuduj Dice-O-Matic: gamesbyemail.com/News/DiceOMatic
Jarett Millard
1
Nie miałem pojęcia, że ​​C ++ jest rand()jednolity. Z jakiej biblioteki korzystasz? cstdlib.h„s rand()nie jest jednolity: cplusplus.com/reference/cstdlib/rand
Mike Warren
3
Nie, rand () jest jednolity (z wyjątkiem niektórych wczesnych błędnych implementacji). to, co nie jest jednolite, to użycie operatora modułu „%” do ograniczenia zakresu. Zobacz stackoverflow.com/questions/2999075/…, aby znaleźć właściwe rozwiązanie, lub jeśli masz dostępne „arc4random_uniform”, możesz również użyć tego bezpośrednio.
John Meacham,
@ Alien01: Czy rozważyłbyś zmianę zaakceptowanej odpowiedzi na „But” („Dlaczego rand to zły pomysł” itp.)? Moja odpowiedź jest naprawdę nieaktualna i za każdym razem, gdy otrzymuję za nią głosowanie, mam wrażenie, że ktoś biegnie niewłaściwą alejką.
peterchen
Niezła biała księga o losowości w języku c ++ 11.
Pupsik

Odpowiedzi:

159

Dlaczego randto zły pomysł

Większość odpowiedzi, które tu otrzymałeś, wykorzystuje randfunkcję i operator modułu. Ta metoda może nie generować liczb jednolicie (zależy to od zakresu i wartości RAND_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.

But
źródło
24
Zwróć uwagę na tę odpowiedź, ponieważ jest ona znacznie nowocześniejsza.
gsamaras
To jest właściwa odpowiedź. Dzięki! Mimo to chciałbym zobaczyć bardziej szczegółowy opis każdego kroku tego kodu. Np. Co to jest mt19937typ?
Apollo
@Apollo Dokumentacja mówi: „32-bitowy Mersenne Twister autorstwa Matsumoto i Nishimura, 1998”. Zakładam, że jest to algorytm do generowania liczb pseudolosowych.
But
@Shoe dla danego przedziału generuje liczby w tej samej kolejności 1 9 6 2 8 7 1 4 7 7. Czy wiesz, jak to zrobić losowo za każdym razem, gdy uruchamiamy program?
1
@Richard Jaka jest alternatywa?
But
61

[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.

peterchen
źródło
4
BigRand może dawać lepsze wyniki, nawet jeśli żądany zakres nie przekracza RAND_MAX. Zastanów się, kiedy RAND_MAX to 32767 i chcesz uzyskać 32767 możliwych wartości - dwie z tych 32768 liczb losowych (w tym zero) będą mapowane na to samo wyjście, a prawdopodobieństwo ich wystąpienia będzie dwukrotnie większe niż inne. Nie jest to idealna przypadkowa własność!
Mark Ransom
8
(RAND_MAX + 1) to zły pomysł. Może to spowodować kumulację i dać wartość ujemną. Lepiej zrobić coś takiego: ((double) RAND_MAX) + 1,0
Demi,
4
@peterchen: Myślę, że źle zrozumiałeś, co mówił Demi. Miała na myśli to: po ( rand() / ((double)RAND_MAX+1)) * (max-min+1) + min prostu przenieś konwersję na podwojenie i uniknij problemu.
Mooing Duck
3
Ponadto zmienia to jedynie rozkład z najniższych 32767 wartości w zakresie do równomiernie rozłożonych 32767 wartości w zakresie, a pozostałe 4017233 wartości nigdy nie zostaną wybrane przez ten algorytm.
Mooing Duck
2
Podana odpowiedź jest odchylona o 1. Prawidłowe równanie to: ((double) rand () / (RAND_MAX + 1.0)) * (max-min) + min "max-min + 1" jest używane, gdy używasz% not * . Zobaczysz dlaczego, kiedy robisz min = 0, max = 1. Czy peterchen lub @ peter-mortensen może to zmienić.
davepc
18

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

randutilsBiblioteki (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 inspirowana C++11„s <random>, z którymi akcje znacznie interfejsu. Chociaż teoretycznie jest również zależnością zewnętrzną, Boostma obecnie status biblioteki „quasi-standardowej”, a jej Randommoduł można uznać za klasyczny wybór dla dobrej jakości generowania liczb losowych. Ma dwie zalety w stosunku do C++11rozwiązania:

  • jest bardziej przenośny, potrzebuje tylko obsługi kompilatora dla C ++ 03
  • jego random_devicemetody zastosowania systemu specyficzne zaoferować posiew dobrej jakości

Jedyną małą wadą jest to, że oferta modułu random_devicenie 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ń:

  • make mt19937a thread_local: generator jest dość obszerny (> 2 KB) i lepiej nie umieszczać go na stosie
  • ziarno mt19937z więcej niż jedną liczbą całkowitą: Mersenne Twister ma duży stan i może skorzystać z większej entropii podczas inicjalizacji

Kilka 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 w std::random_devicetym, że Standard nie narzuca żadnej minimalnej jakości dla swoich wyników (o ile entropy()zwraca 0), a od 2015 r. MinGW (nie jest najczęściej używanym kompilatorem, ale raczej nie jest ezoterycznym wyborem) zawsze będzie drukować 4na 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 randintfunkcja 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.

Alberto M.
źródło
randutilsBiblioteka nazywa się teraz PCG.
tay10r
10

Jeśli RAND_MAXwynosi 32767, można łatwo podwoić liczbę bitów.

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}
Mark Okup
źródło
Myślę, że to nie działa. Generatory liczb pseudolosowych są zazwyczaj deterministyczne. Na przykład, jeśli pierwsze randpołączenie powróci, 0x1234a drugie 0x5678, otrzymasz 0x12345678. To jedyna liczba, którą możesz uzyskać, zaczynając od 0x1234, ponieważ następna liczba zawsze będzie 0x5678. Otrzymujesz wyniki 32-bitowe, ale masz tylko 32768 możliwych liczb.
user694733
@ user694733 dobry generator liczb losowych ma okres większy niż liczba wyjść, które może wygenerować, więc po 0x1234 nie zawsze występuje 0x5678.
Mark Ransom
9

Jeśli możesz, użyj Boost . Miałem szczęście z ich przypadkową biblioteką .

uniform_int powinieneś robić, co chcesz.

Jeff Thomas
źródło
Pracowałem trochę nad uniform_int z twisterem merseinne i niestety dla niektórych zakresów wartości zwracane przez uniform_int nie są tak jednolite, jak bym się spodziewał. Na przykład uniform_int <> (0, 3) ma tendencję do generowania więcej
zer
@ScaryAardvark, który brzmi jak zła implementacja uniform_int. Uzyskanie obiektywnego wyniku jest dość łatwe, pojawiło się wiele pytań, które demonstrują tę metodę.
Mark Ransom
@Mark Ransom. Tak, całkowicie się zgadzam.
ScaryAardvark
8

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.

SoapBox
źródło
Nie mogę korzystać z żadnej biblioteki innej firmy? Jestem ograniczony tylko do C ++.
an i
Następnie idź męską drogą, zaimplementuj AES lub inny algorytm szyfrowania.
SoapBox
2
Kod RC4 jest trywialny w kodzie i wystarczająco losowy dla wszystkich praktycznych celów (z wyjątkiem WEP, ale nie jest to całkowicie wina RC4). To znaczy, to niesamowicie trywialny kod. Około 20 linii. Wpis Wikipedii zawiera pseudokod.
Steve Jessop
4
Dlaczego nie możesz użyć kodu innej firmy? Jeśli jest to zadanie domowe, powinieneś to powiedzieć, ponieważ wiele osób wolałoby udzielić pomocnych wskazówek, zamiast przedstawiać kompletne rozwiązania w tym przypadku. Jeśli to nie jest praca domowa, kopnij gościa, który mówi „nie ma kodu strony trzeciej”, bo jest kretynem.
DevSolar
Bardziej bezpośredni link do dokumentacji funkcji rand () OpenSSL: openssl.org/docs/crypto/rand.html#
DevSolar
5

Powinieneś spojrzeć na RAND_MAXswój konkretny kompilator / środowisko. Myślę, że zobaczysz te wyniki, jeśli rand()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_MAXi trochę więcej szczegółów na temat swojego środowiska.

abelenky
źródło
3

Sprawdź, co RAND_MAXjest 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 () .

Rob Walker
źródło
Ok RAND_MAX jest 32767. Jestem na platformie Windows C ++ .. Czy istnieje inna metoda generowania liczb losowych z jednolitym rozkładem?
an i
2

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);
}
user3503711
źródło
2

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().

Jason Coco
źródło
1

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.

benf
źródło
0

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.

Kluge
źródło
„równomiernie rozłożony” ma dobrze zdefiniowane znaczenie, a standardowe generatory losowe zwykle się do tego zbliżają.
peterchen
Tak, masz rację, generatory liczb losowych powinny generować dane wyjściowe, które z biegiem czasu są generalnie jednolite w dystrybucji. Chyba chodzi mi o to, że w przypadku niewielkiej liczby instancji (6, jak pokazano w przykładzie), wynik nie zawsze będzie jednolity.
Kluge,
Kluge ma rację. Jednolity rozkład w małej próbie wskazuje, że próbka zdecydowanie nie jest losowa.
Bill the Lizard,
1
Bill, to nic takiego nie wskazuje. Małe próbki są przeważnie bez znaczenia, ale jeśli RNG ma być jednolite, a wynik jest jednolity, dlaczego jest to gorsze niż niejednorodna mała próbka?
Dan Dyer,
2
Istotne rozkłady w obu przypadkach wskazują na nielosowość: myślę, że Bill po prostu ma na myśli, że 6 równomiernie rozmieszczonych wyników również byłoby podejrzanych. W OP 6 wartości mieści się w zakresie 32k / 4M, czyli <1% żądanego zakresu. Prawdopodobieństwo, że będzie to fałszywy alarm, jest zbyt małe, aby się nad tym zastanawiać.
Steve Jessop,
0

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.

calandoa
źródło
1
To tylko zmienia rozkład, aby wyglądał bardziej równomiernie, ale tak naprawdę nie jest już nawet dla dużych zasięgów (takich jak przypadek OP)
Mooing Duck
0

@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 ().

Mistrz rytuałów
źródło
0

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_MAXdo 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ć do int32_t. Jeśli RAND_MAXuż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 (podobnej int64_t) do obliczeń. Po drugie, jeśli int64_tjest używany, ale RAND_MAXnadal wynosi 32767, w zakresach większych niż RAND_MAXzaczniesz pojawiać się „dziury” w możliwych numerach wyjściowych. Jest to prawdopodobnie największy problem z każdym rozwiązaniem pochodzącym ze skalowania rand().

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.

Ryan
źródło
-1

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.

Michael Haephrati
źródło
1
Pytanie dotyczy jednolitej dystrybucji. To proponowane rozwiązanie nie zapewni równomiernego rozkładu. Biblioteka Standard C ++ zawiera narzędzia do generowania liczb pseudolosowych . Ci zrobić zapewnienie równomiernego rozkładu, jeżeli są wymagane.
Niespodziewane
-3

Właśnie znalazłem to w Internecie. To powinno działać:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));
anand
źródło
Wyjaśnij, do czego ich potrzebujesz, istnieje mnóstwo algorytmów dla PRNG. Byłoby też łatwiej, gdybyś zamiast publikowania odpowiedzi zredagował swoje główne pytanie.
peterchen
To działa najlepiej dla mnie ... Jestem w stanie uzyskać lepiej rozłożone liczby losowe dzięki tej formule ..
an i
4
Jeśli Twój zakres przekracza RAND_MAX, wyniki mogą nie być jednolite. Oznacza to, że w zakresie znajdują się wartości, które nie będą reprezentowane bez względu na to, ile razy wywołasz funkcję.
dmckee --- ex-moderator kitten
4
Ponadto, jeśli max i min są zarówno bez znaku int, a min to 0, a max to MAX_UINT, to ((max) - (min) +1) będzie równe 0, a wynikiem zawsze będzie 0. Uważaj na przepełnienie podczas wykonywania tego typu obliczeń! Jak zauważył dmckee, rozciąga to dystrybucję w zakresie docelowym, ale nie gwarantuje więcej niż unikalnych wartości RAND_MAX.
jesup