Próbuję stworzyć grę w kości i muszę mieć w niej losowe liczby (aby zasymulować boki kostki. Wiem, jak zrobić to od 1 do 6). Za pomocą
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
nie działa zbyt dobrze, ponieważ gdy uruchamiam program kilka razy, oto wynik, który otrzymuję:
6
1
1
1
1
1
2
2
2
2
5
2
Dlatego potrzebuję polecenia, które za każdym razem wygeneruje inną liczbę losową, a nie tę samą 5 razy z rzędu. Czy istnieje polecenie, które to zrobi?
Odpowiedzi:
Najbardziej podstawowym problemem twojej aplikacji testowej jest to, że dzwonisz
srand
raz, a potem dzwoniszrand
jeden raz i kończysz.Głównym celem
srand
funkcji jest zainicjowanie sekwencji liczb pseudolosowych pseudolosowych losowym ziarnem.Oznacza to, że jeśli przejdą taką samą wartość , aby
srand
w dwóch różnych aplikacji (z tym samymsrand
/rand
realizacji) wtedy dostaniesz dokładnie tę samą sekwencję zrand()
wartości odczytu po czym w obu aplikacjach.Jednak w Twojej przykładowej aplikacji sekwencja pseudolosowa składa się tylko z jednego elementu - pierwszego elementu pseudolosowej sekwencji wygenerowanej z ziarna równego aktualnemu czasowi
second
precyzji. Czego spodziewasz się wtedy zobaczyć na wyjściu?Oczywiście, gdy zdarzy ci się uruchomić aplikację w tej samej sekundzie - używasz tej samej wartości początkowej - więc twój wynik jest oczywiście taki sam (o czym Martin York wspomniał już w komentarzu do pytania).
Właściwie powinieneś zadzwonić
srand(seed)
jeden raz, a potem zadzwonićrand()
wiele razy i przeanalizować tę sekwencję - powinna wyglądać losowo.EDYTOWAĆ:
Oh, rozumiem. Z pozoru opis słowny to za mało (może bariera językowa czy coś… :)).
DOBRZE. Przykład staromodnego kodu C oparty na tych samych
srand()/rand()/time()
funkcjach, które zostały użyte w pytaniu:^^^ TO sekwencja z jednego przebiegu programu ma wyglądać przypadkowe.
EDYCJA2:
Korzystając z biblioteki standardowej C lub C ++, ważne jest, aby zrozumieć, że w chwili obecnej nie ma ani jednej standardowej funkcji ani klasy, która ostatecznie tworzy faktycznie losowe dane (gwarantowane przez standard). Jedynym standardowym narzędziem, które rozwiązuje ten problem, jest std :: random_device, które niestety nadal nie daje gwarancji faktycznej losowości.
W zależności od charakteru aplikacji należy najpierw zdecydować, czy naprawdę potrzebujesz naprawdę losowych (nieprzewidywalnych) danych. Godny uwagi przypadek, w którym z całą pewnością potrzebujesz prawdziwej przypadkowości jest bezpieczeństwo informacji - np. Generowanie kluczy symetrycznych, asymetrycznych kluczy prywatnych, wartości soli, tokenów bezpieczeństwa itp.
Jednak liczby losowe o stopniu bezpieczeństwa to osobna branża warta osobnego artykułu.
W większości przypadków generator liczb pseudolosowych jest wystarczający - np. Do symulacji naukowych lub gier. W niektórych przypadkach konsekwentnie zdefiniowana sekwencja pseudolosowa jest nawet wymagana - np. W grach możesz zdecydować się na generowanie dokładnie takich samych map w czasie wykonywania, aby uniknąć przechowywania dużej ilości danych.
Oryginalne pytanie i powtarzająca się mnogość identycznych / podobnych pytań (a nawet wiele błędnych „odpowiedzi” na nie) wskazują, że przede wszystkim ważne jest, aby odróżnić liczby losowe od liczb pseudolosowych ORAZ zrozumieć, czym jest sekwencja liczb pseudolosowych w Po pierwsze ORAZ zdać sobie sprawę, że generatory liczb pseudolosowych NIE są używane w taki sam sposób, jak można używać prawdziwych generatorów liczb losowych.
^^^ TEN rodzaj intuicyjnych oczekiwań jest BARDZO NIEPRAWIDŁOWY i szkodliwy we wszystkich przypadkach związanych z generatorami liczb pseudolosowych liczb pseudolosowych - mimo że są rozsądne dla prawdziwych liczb losowych.
Chociaż istnieje sensowne pojęcie „liczby losowej” - nie ma czegoś takiego jak „liczba pseudolosowa”. Pseudo-Random Number Generator faktycznie produkuje liczb pseudolosowych sekwencji .
Kiedy eksperci mówią o jakości PRNG, w rzeczywistości mówią o statystycznych właściwościach wygenerowanej sekwencji (i jej godnych uwagi podsekwencji). Na przykład, jeśli połączysz dwa wysokiej jakości PRNG, używając ich obu na zmianę - możesz wytworzyć złą wynikową sekwencję - mimo że generują one dobre sekwencje każda z osobna (te dwie dobre sekwencje mogą po prostu korelować ze sobą, a zatem źle się łączyć).
Sekwencja pseudolosowa jest w rzeczywistości zawsze deterministyczna (z góry określona przez jej algorytm i parametry początkowe), tj. Właściwie nie ma w niej nic losowego.
W szczególności
rand()
/srand(s)
para funkcji zapewnia pojedynczą sekwencję liczb pseudolosowych na proces, która nie jest bezpieczna dla wątków (!), Generowaną za pomocą algorytmu zdefiniowanego w ramach implementacji. Funkcjarand()
generuje wartości w zakresie[0, RAND_MAX]
.Cytat ze standardu C11:
Wiele osób słusznie spodziewa się, że
rand()
dałoby to sekwencję półniezależnych, równomiernie rozłożonych liczb w zakresie0
doRAND_MAX
. Cóż, zdecydowanie powinno (w przeciwnym razie jest bezużyteczne), ale niestety nie tylko standard tego nie wymaga - istnieje nawet wyraźne zastrzeżenie, które stwierdza, że „nie ma gwarancji co do jakości wyprodukowanej sekwencji losowej” . W niektórych przypadkach historycznychrand
/srand
wdrożenie było rzeczywiście bardzo złej jakości. Chociaż w nowoczesnych wdrożeniach jest najprawdopodobniej wystarczająco dobry - ale zaufanie jest zepsute i niełatwe do odzyskania. Poza tym, że nie jest bezpieczny dla wątków, jego bezpieczne użycie w aplikacjach wielowątkowych jest trudne i ograniczone (nadal jest możliwe - możesz po prostu używać ich z jednego dedykowanego wątku).Nowy szablon klasy std :: mersenne_twister_engine <> (i jego wygodne typy czcionek -
std::mt19937
/std::mt19937_64
z dobrą kombinacją parametrów szablonu) zapewnia per-obiekt pseudolosowych generator liczb zdefiniowane w standardzie C ++ 11. Z tymi samymi parametrami szablonu i tymi samymi parametrami inicjalizacji różne obiekty będą generować dokładnie tę samą sekwencję wyjściową dla każdego obiektu na dowolnym komputerze w dowolnej aplikacji zbudowanej przy użyciu biblioteki standardowej zgodnej z C ++ 11. Zaletą tej klasy jest przewidywalna sekwencja wyjściowa o wysokiej jakości i pełna spójność między implementacjami.Istnieje również więcej silników PRNG zdefiniowanych w standardzie C ++ 11 - std :: linear_congruential_engine <> (historycznie używany jako
srand/rand
algorytm uczciwej jakości w niektórych implementacjach bibliotek standardowych C) i std :: subtract_with_carry_engine <> . Generują również w pełni zdefiniowane zależne od parametrów sekwencje wyjściowe na obiekt.Współczesny przykład C ++ 11 zastępujący przestarzały kod C powyżej:
Wersja poprzedniego kodu, która używa std :: uniform_int_distribution <>
źródło
rand()
isrand()
. Czy możesz to zaktualizować?rand()
isrand()
. Właściwie to po prostu odpowiada na pytanie z podanym opisem. Z opisu (który używarand
/srand
) jasno wynika, że należy wyjaśnić podstawowe koncepcje generowania liczb pseudolosowych - podobnie jak samo znaczenie ciągu pseudolosowego i jego zalążka. Próbuję zrobić dokładnie to i użyć najbardziej prosty i znajomyrand
/srand
kombinację. Zabawne jest to, że niektóre inne odpowiedzi - nawet z bardzo dużą oceną - zawierają te same nieporozumienia, co autor pytania.std::rand/std::srand
cechy i Nowa biblioteka C ++ podobastd::random_device<>
, std :: mersenne_twister_engine <> i mnóstwo losowych rozkładów wymaga pewnego wyjaśnienia.Użycie modulo może wprowadzić odchylenie do liczb losowych, w zależności od generatora liczb losowych. Zobacz to pytanie, aby uzyskać więcej informacji. Oczywiście możliwe jest uzyskanie powtarzających się liczb w losowej kolejności.
Wypróbuj kilka funkcji C ++ 11 dla lepszej dystrybucji:
Zobacz to pytanie / odpowiedź, aby uzyskać więcej informacji na temat liczb losowych w C ++ 11. Powyższe nie jest jedynym sposobem, aby to zrobić, ale jest jednym ze sposobów.
źródło
%6
jest znikomo mała. Może to istotne, jeśli piszesz grę w kości do wykorzystania w Las Vegas, ale nie ma znaczenia w prawie każdym innym kontekście.random_device
imt19937
już jest, dosłownie nie ma powodu, aby nie iść na całość i nie używać standarduuniform_int_distribution
.Jeśli korzystasz z bibliotek boost , możesz uzyskać generator losowy w następujący sposób:
Gdzie funkcja
current_time_nanoseconds()
podaje aktualny czas w nanosekundach, który jest używany jako ziarno.Oto bardziej ogólna klasa do uzyskiwania losowych liczb całkowitych i dat w zakresie:
źródło
http://en.cppreference.com/w/cpp/numeric/random/rand
źródło
%6
.) A jeśli zdecydowałeś się użyćstd::rand
C ++ APIrand
funkcji biblioteki C, to dlaczego nie użyćstd::time
istd::srand
ze względu na spójność stylu C ++?Może być pełny
Randomer
kod klasy do generowania liczb losowych!Jeśli potrzebujesz liczb losowych w różnych częściach projektu, możesz utworzyć oddzielną klasę,
Randomer
aby zawrzeć w niej wszystkie elementyrandom
.Coś w tym stylu:
Taka klasa przydałaby się później:
Możesz sprawdzić ten link jako przykład, jak używam takiej
Randomer
klasy do generowania losowych ciągów. Możesz również użyć,Randomer
jeśli chcesz.źródło
Scenariusz użycia
Porównałam problem Przewidywalności do worka sześciu kawałków papieru, z których każdy ma zapisaną wartość od 0 do 5. Za każdym razem, gdy wymagana jest nowa wartość, z worka wyciąga się kartkę papieru. Jeśli worek jest pusty, numery są umieszczane z powrotem w torbie.
... na tej podstawie mogę stworzyć swego rodzaju algorytm.
Algorytm
Torba jest zwykle plikiem
Collection
. Wybrałembool[]
(znaną również jako tablica logiczna, płaszczyzna bitowa lub mapa bitowa), aby przejąć rolę worka.Powodem, dla którego wybrałem, jest
bool[]
to, że indeks każdego elementu jest już wartością każdej kartki papieru. Gdyby dokumenty wymagały czegoś innego napisanego na nich, użyłbym plikuDictionary<string, bool>
zamiast niego litery. Wartość logiczna służy do śledzenia, czy liczba została jeszcze narysowana, czy nie.Wywołany licznik
RemainingNumberCount
jest inicjalizowany tak, aby5
odliczał w miarę wybierania liczby losowej. Dzięki temu nie musimy liczyć, ile kartek papieru pozostało za każdym razem, gdy chcemy narysować nową liczbę.Aby wybrać następną losową wartość, używam a
for..loop
do przeszukiwania worka indeksów i licznika do odliczania, gdyindex
zostaniefalse
wywołanyNumberOfMoves
.NumberOfMoves
służy do wyboru następnego dostępnego numeru.NumberOfMoves
jest najpierw ustawiana na losową wartość z zakresu od0
do5
, ponieważ dostępnych jest 0..5 kroków, które możemy wykonać przez worek. W następnej iteracjiNumberOfMoves
jest ustawiana losowa wartość z przedziału od0
do4
, ponieważ jest teraz 0..4 kroki, które możemy wykonać przez worek. Ponieważ liczby są używane, dostępne liczby zmniejszają się, więc zamiast tego używamyrand() % (RemainingNumberCount + 1)
do obliczenia następnej wartościNumberOfMoves
.Gdy
NumberOfMoves
licznik osiągnie zero,for..loop
powinno wyglądać następująco:for..loop
indeks.false
.for..loop
.Kod
Kod powyższego rozwiązania jest następujący:
(umieść następujące trzy bloki w głównym pliku .cpp jeden po drugim)
Klasa konsoli
Tworzę tę klasę Console, ponieważ ułatwia ona przekierowywanie danych wyjściowych.
Poniżej w kodzie ...
... można zastąpić ...
... a następnie tę
Console
klasę można usunąć w razie potrzeby.Główna metoda
Przykładowe użycie w następujący sposób:
Przykładowe dane wyjściowe
Po uruchomieniu programu otrzymałem następujące dane wyjściowe:
Oświadczenie końcowe
Ten program został napisany przy użyciu Visual Studio 2017 i zdecydowałem się na
Visual C++ Windows Console Application
projekt przy użyciu.Net 4.6.1
.Nie robię tu nic szczególnie specjalnego, więc kod powinien działać również na wcześniejszych wersjach Visual Studio.
źródło
Ilekroć przeprowadzasz podstawowe wyszukiwanie
random number generation
w Internecie w języku programowania C ++, to pytanie jest zwykle wyświetlane jako pierwsze! Chcę rzucić swój kapelusz na ring, aby, miejmy nadzieję, lepiej wyjaśnić koncepcję generowania liczb pseudolosowych w C ++ dla przyszłych programistów, którzy nieuchronnie będą szukać tego samego pytania w Internecie!Podstawy
Generowanie liczb pseudolosowych obejmuje proces wykorzystania deterministycznego algorytmu, który tworzy sekwencję liczb, których właściwości w przybliżeniu przypominają liczby losowe . Mówię, że w przybliżeniu przypominają , ponieważ prawdziwa przypadkowość jest raczej nieuchwytną tajemnicą w matematyce i informatyce. Stąd, dlaczego termin pseudolosowy jest używany, aby był bardziej pedantycznie poprawny!
Zanim będziesz mógł faktycznie użyć PRNG, tj.
pseudo-random number generator
Musisz podać algorytmowi wartość początkową, często nazywaną również ziarnem . Jednak ziarno należy ustawić tylko raz przed użyciem samego algorytmu!Tak więc, jeśli chcesz mieć dobrą sekwencję liczb, musisz dostarczyć wystarczającą ilość nasion do PRNG!
Stary sposób C.
Biblioteka C ++ kompatybilna wstecz ze standardem C, używa tak zwanego generatora liniowej kongruencji, który znajduje się w
cstdlib
pliku nagłówkowym! Ten PRNG działa poprzez nieciągłą funkcję fragmentaryczną, która wykorzystuje arytmetykę modularną, tj. Szybki algorytm, który lubi używaćmodulo operator '%'
. Poniżej przedstawiono typowe użycie tego PRNG w odniesieniu do pierwotnego pytania zadanego przez @Predictability:Powszechne użycie PRNG C obejmuje cały szereg problemów, takich jak:
std::rand()
nie jest zbyt intuicyjny dla prawidłowego generowania liczb pseudolosowych z podanego zakresu, np. Tworzenia liczb z zakresu [1, 6] tak, jak chciał @Predictability.std::rand()
wyklucza możliwość równomiernego rozmieszczenia liczb pseudolosowych ze względu na zasadę Pigeonhole .std::rand()
wysiewaniastd::srand( ( unsigned int )std::time( nullptr ) )
jest technicznie nieprawidłowy, ponieważtime_t
jest uważany za typ ograniczony . Dlatego konwersja ztime_t
naunsigned int
nie jest gwarantowana!Aby uzyskać bardziej szczegółowe informacje na temat ogólnych problemów związanych z używaniem PRNG języka C i jak je obejść, zobacz Using rand () (C / C ++): Porady dotyczące funkcji rand () w bibliotece standardowej !
Standardowy sposób C ++
Od czasu opublikowania standardu ISO / IEC 14882: 2011, tj. C ++ 11,
random
biblioteka od jakiegoś czasu jest częścią języka programowania C ++. Ta biblioteka wyposażona jest w wielu PRNGs i różnych rodzajów dystrybucji , takie jak: równomiernego rozkładu , rozkładu normalnego , rozkładu dwumianowego itp Poniższy przykład kodu źródłowego wykazuje bardzo podstawowe korzystanie zrandom
biblioteki, w odniesieniu do pierwotnego pytania @ przewidywalność za:32-bitowy Mersenne Twister silnika, z równomiernym rozkładem w całkowitoliczbowych wartości stosowano w powyższym przykładzie. (Nazwa silnika w kodzie źródłowym brzmi dziwnie, ponieważ jego nazwa pochodzi z okresu 2 ^ 19937-1). Przykład używa również
std::random_device
do zapoczątkowania silnika, który pobiera swoją wartość z systemu operacyjnego (jeśli używasz systemu Linux,std::random_device
zwraca wartość z/dev/urandom
).Zwróć uwagę, że nie musisz używać
std::random_device
do inicjowania żadnego silnika . Możesz użyć stałych lub nawetchrono
biblioteki! Nie musisz też używać 32-bitowej wersjistd::mt19937
silnika, są inne opcje ! Więcej informacji o możliwościachrandom
biblioteki można znaleźć na stronie cplusplus.comPodsumowując, programiści C ++ nie powinni
std::rand()
już używać , nie dlatego, że jest zły , ale dlatego, że obecny standard zapewnia lepsze alternatywy, które są prostsze i bardziej niezawodne . Mamy nadzieję, że wielu z Was uzna to za pomocne, zwłaszcza ci z Was, którzy niedawno szukali w interneciegenerating random numbers in c++
!źródło
Oto rozwiązanie. Utwórz funkcję, która zwraca liczbę losową i umieść ją poza funkcją główną, aby stała się globalna. Mam nadzieję że to pomoże
źródło
Ten kod tworzy losowe liczby od
n
dom
.przykład:
źródło
srand(time(0))
do głównej funkcjirandom(n, m)
?srand(time(0))
do funkcji main, a nie pętli for lub wewnątrz implementacji funkcji.losowo każdy plik RUN
źródło
Oto prosty generator losowy z ok. równe prawdopodobieństwo wygenerowania wartości dodatnich i ujemnych wokół 0:
źródło