Generování náhodných čísel
Počítače jsou deterministické stroje, což znamená, že stejný program vždy na stejný vstup vrátí stejný výstup. Často ovšem chceme, aby naše programy obsahovaly prvky "náhody", když chceme například:
- Hodit si kostkou v deskové hře
- Udělit náhodný počet zranění v rozsahu zbraně
- Oživit hráče na náhodné pozici na mapě
Počítače samy o sobě opravdovou náhodu vytvořit nemohou, nicméně můžou ji simulovat pomocí tzv. pseudo-náhodných generátorů čísel (pseudo-random number generation).
Vygenerovat (pseudo-)náhodnou sekvenci čísel pomocí deterministických operací můžeme například následujícím algoritmem:
- Začneme s číslem
S
, které se nazývá počáteční náhodná hodnota (random seed). - Aplikujeme nějakou matematickou operaci na
S
a vyjde nám nové čísloN
. N
použijeme jako vygenerované "náhodné číslo".- Nastavíme
S = N
. - Opakujeme postup od bodu 2).
Ukázka kódu, který takovýto algoritmus implementuje:
int S = 5;
int vygeneruj_cislo() {
int N = S;
N = (5 * N + 3) % 6323;
N = (4 * N + 2) % 8127;
S = N;
return N;
}
int main() {
int r1 = vygeneruj_cislo(); // 114
int r2 = vygeneruj_cislo(); // 2294
int r3 = vygeneruj_cislo(); // 4348
int r4 = vygeneruj_cislo(); // 2971
int r5 = vygeneruj_cislo(); // 723
return 0;
}
Takovýto algoritmus bude generovat (nekonečnou) sekvenci čísel, která bude lidem připadat "náhodná" (bude těžké uhodnout, jaké číslo algoritmus vrátí příště).
Volba počáteční hodnoty S
Určite jste si všimli, že výše zmíněný algoritmus bude pokaždé generovat stejnou sekvenci čísel pro
stejné počáteční S
. To se může hodit, chceme-li například mít možnost zpětně přehrát sekvenci
pseudo-náhodných čísel, například pro odladění chyby v programu. Nicméně pokud by sekvence byla pokaždé
stejná, tak o (pseudo-)náhodě nemůže být řeč.
Proto se obvykle hodnota seedu volí tak, aby při každém spuštění programu byla jiná. Přirozenou
volbou pro počáteční hodnotu S
je tak například čas1 při spuštění programu. Lze ale také použít
například pohyby myši nebo stisky kláves, které nedávno na počítači proběhly.
1Ve formě UNIX časového razítka, tedy počtu vteřin uběhlých od 1. 1. 1970.
Pseudo-náhodný generátor ve standardní knihovně C
Při praktickém použití si obvykle nebudete psát generátor pseudo-náhodných sami, ale použijete již
hotové řešení. To nabízí například standardní knihovna C ve formě funkcí srand
(nastav hodnotu
seedu) a rand
(vygeneruj pseudo-náhodné číslo):
#include <stdlib.h>
#include <time.h>
int main() {
int cas = (int) time(NULL); // získej současný čas
srand(cas); // nastav S na současný čas
int cislo1 = rand(); // pseudo-náhodné číslo z intervalu [0, RAND_MAX]
int cislo2 = rand() % 100; // z intervalu [0, 99]
int cislo3 = rand() % 100 + 5; // z intervalu [5, 104]
float cislo4 = rand() / (float) RAND_MAX; // z intervalu [0.0, 1.0]
return 0;
}