8. Posuvný register

8.2 Problém - Hod kockou

  Vytvorte program umožňujúci zrealizovať náhodný pokus, pri ktorom sa hádže hracou kockou s cieľom zistiť, koľkokrát padne stena s jednou, dvomi, tromi, štyrmi, piatim resp. šiestimi bodkami (relatívne početnosti týchto náhodných udalostí). Počet pokusov načítajte zo vstupu. V programe však nesmie byť na generovanie náhodných čísel použitá funkcia rand, s využitím posuvného registra vytvorte vlastný generátor pseudonáhodných čísel!


Riešenie

Zrealizovať náhodný pokus - hod kockou - znamená náhodne vygenerovať číslo z množiny {1, 2, 3, 4, 5, 6}. S použitím generátora pseudonáhodných čísel, ktorý je v C k dispozícii, by to skutočne nebol žiadny probém:

#include <stdlib.h>
#include <time.h>
...

randomize();
for (int i = 1; i <= PocetPokusov; i++)
   printf("Padlo cislo %d\n", (rand() % 6) + 1);

  Operátor % (mod, modulo) znamená zvyšok po celočíselnom delení, pri delení číslom 6 máme zvyšky 0, 1, 2, 3, 4, 5.

Vlastný generátor pseudonáhodných čísel môžeme vytvoriť na základe vhodného rekurentného vzťahu:

xn+1 = f( xn-k, ..., xn-1, xn) ,   0<= k <= n

Podľa tohto vzťahu každý ďalší člen v generovanej postupnosti čísel určíme na základe hodnôt predchádzajúcich členov postupnosti. Na rovnakom princípe funguje v podstate väčšina počítačom používaných generátorov pseudonáhodných číselných postupností.

Veľkú skupinu metód generovania tvoria tzv. kongruenčné metódy, pri ktorých majú príslušné rekurentné vzťahy väčšinou jeden z tvarov:

xn+1 = (xn-k+ xn) mod M,    xn+1 = (A·xn) mod M,    xn+1 = (A·xn + C) mod M

V postupnosti čísel generovanej podľa kongruenčných metód sa objavujú "len" čísla 0, 1, ..., M-1 (zvyšky). Aby sme takto získali "dobrú" postupnosť čísel, t.j. takú, v ktorej sa budú všetky čísla objavovať s rovnakou pravdepodobnosťou, musíme nájsť nielen vhodný rekurentný vzťah, ale i vhodné "štartovacie" hodnoty - prvé členy postupnosti, na základe ktorých sa začne výpočet ďalších.


V našom programe využijeme generátor založený na tomto rekurentnom vzťahu:

xn+1 = f(xn-3, xn-2, xn-1, xn)

Ďalší člen teda vždy vypočítame z hodnôt štyroch predchádzajúcich. Pre konkrétnosť uvažujme vzťah v tvare:

xn+1 = ((7·xn-3 + xn + 991) mod 6) + 1

Potrebujeme teda 4 počiatočné hodnoty. Nech napr. x0 = 3, x1 = 5, x2 = 6, x3 = 2

Vytvoríme 5 prvkový posuvný register (ako lineárny zoznam), štyri počiatočné hodnoty načítame zo vstupu:

Vypočítame ďalší člen:
x4 = ((7·x0 + x3 + 991) mod 6) + 1 = 1

Posunieme prvky v registri doľava odobratím prvku zo začiatku a pridaním nového prvku na koniec.

Vypočítame ďalší člen:
x5 = ((7·x1 + x4 + 991) mod 6) + 1 = 2

Posunieme prvky v registri doľava a vypočítame ďalší člen atď.

  Ak potrebujeme kvôli výpočtu nasledujúcej hodnoty uchovávať menší počet predchádzajúcich hodnôt, vystačíme aj s "obyčajnými" premennými príslušného typu (napr. int či float). Pri zložitejších numerických metódach založených na rekurentných vzťahoch však použitie posuvného registra môže výpočet významne zjednodušiť už tým, že sa vyhneme "postupnému" posúvaniu hodnôt:   x0 = x1;  x1 = x2;  x2 = x3;  x3 = x4;  atď.

[Program]