wtorek, 23 kwietnia 2013

Algorytm Mersenne Twister - generator liczb pseudolosowych

Z pewnością zalicza się do jednego z ciekawszych i lepszych generatorów liczb pseudolosowych. Został stworzony i opracowany przez Makoto Matsumoto i Takuji Nishimura z Uniwersytetu Keio. Okres algorytmu wynosi \( 2^{19937}-1 \), natomiast stopień równomiernego rozmieszczenia jest 623-wymiarowy. Algorytm stosuje dwa techniczne zabiegi polepszające jego wydajność i zdolność do generowania pseudolosowości. Pierwszą z nich są tzw. niekompletne tablice służące do znajdowania okresu liczby pierwszej Mersenne'a. Drugą z nich jest szybki algorytm testujący zdolność do rozkładu wielomianu poprzez rekurencję liniową. Do życia algorytm potrzebuje tylko zdefiniowania rekurencji i szybkiego algorytmu obliczania wektora stanu z 1-bitowego strumienia wyjścia. Priorytetem Mersenne Twister'a jest złożoność obliczeniowa jego algorytmu odwrotnego rozkładu wielomianu pomnożona przez stopień danego wielomianu. W celu osiągnięcia bardziej równomiernego rozmieszczenia stosuje się m.in. algorytm Lenstry.

Porówanie wydajności Mersenne Twister z innymi generatorami liczb pseudolosowych:


Jak widać w powyższej tabeli algorytm MT19937 ma znaczną przewagę nad resztą, jeśli chodzi o stosunek okresu losowości do czasu wygenerowania \( 10^7 \) liczb pseudolosowych w danej przestrzeni roboczej.

Warto zaznaczyć, że algorytm Mersenne Twister nie nadaje się do użycia w kryptografii. Liczby przez niego generowane da się łatwo zgadnąć transformując liniowo macierz "hartującą" używaną przez algorytm, na jej odwrotność. Wtedy to używając liniowej rekurencji możemy zgadnąć dalsze ciągi generowanych liczb poprzez analizę wygenerowanych liczb.

Algorytm Mersenne Twister jest mniej więcej tak samo szybki jak algorytm rand znany z ANSI-C, co więcej zawiera o wiele większy okres i stopień równomiernego rozmieszczenia, co daje nam pseudolosowość o lepszej jakości. Poniżej zaprezentuje kod Mersenne Twister'a w C++, który pozwala zarówno na generowanie pseudolosowych liczb całkowitych, jak i też zmiennoprzecinkowych.

#include <iostream>
#include <ctime>

using namespace std;

// Parametry okresu
const unsigned long N = 624;
const unsigned long M = 397;
const unsigned long MATRIX_A   = 0x9908b0df;
const unsigned long UPPER_MASK = 0x80000000;
const unsigned long LOWER_MASK = 0x7fffffff;

// Parametry hartowania macierzy
const unsigned long TEMPERING_MASK_B = 0x9d2c5680;
const unsigned long TEMPERING_MASK_C = 0xefc60000;

// Makra przesuwajace bity
#define TEMPERING_SHIFT_U(y)    (y >> 11)
#define TEMPERING_SHIFT_S(y)    (y << 7)
#define TEMPERING_SHIFT_T(y)    (y << 15)
#define TEMPERING_SHIFT_L(y)    (y >> 18)

static unsigned long mt[N]; // tablica wektora stanu
static unsigned int mti = N+1;

// Funkcja inicjalizujaca tablice sola niezerowa
void sgenrand(unsigned long seed) {
    mt[0] = seed & 0xffffffff;
    for (mti=1; mti<N; mti++) {
        mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
    }
}

// Glowna funkcja losujaca zwracajaca typ calkowity
unsigned long genrand() {
    unsigned long y;
    static unsigned long mag01[2] = {0x0, MATRIX_A};

    if (mti >= N) {
        unsigned int kk;

        // Jesli sgenrand nie zostalo wywolane inicjalizuje tablice domyslnie
        if (mti == N+1)
            sgenrand(4357);

        for (kk=0; kk<N-M; kk++) {
            y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
            mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
        }
        for (; kk<N-1; kk++) {
            y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
            mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
        }
        y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
        mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];

        mti = 0;
    }

    y = mt[mti++];
    y ^= TEMPERING_SHIFT_U(y);
    y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
    y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
    y ^= TEMPERING_SHIFT_L(y);

    return y;
}

// Funkcja losujaca zwracajca typ zmiennoprzecinkowy
double genrand_d() {
    return (static_cast<double>(genrand()) /
            static_cast<unsigned long>(0xffffffff));
}

// Program wypisujacy pierwsze 100 wylosowanych liczb z przedzialu 0-100
int main()
{
    int min = 0;
    int max = 100;
    // Inicjalizacja generatora aktualnym czasem
    sgenrand(time(NULL));

    for (int i=0; i<100; i++) {
        cout << (genrand()%(max-min))+min << "\t";
        if (i%10==9)
            cout << "\n";
    }

    return 0;
}

Działanie algorytmu polega na początkowym utworzeniu 624 wektorów słów o długości 32 (oznaczmy ją w) lub 64 bitów i wartościach od 0 do \(2^{32}-1\) lub \(2^{64}-1\). To na tych słowach operuje Mersenne Twister w swych późniejszych fazach wykonania. Algorytm bazuje na rekurencji liniowej określonej wzorem:
$$ x_{k+n}=x_{k+m} ⨁ {(x_{k}^{u}│x_{k+1}^{l})A} $$
k to liczby całkowite postaci 0, 1, 2, 3, ... x oznacza wektor składający się z wyrazów 0 lub 1 i reprezentujący słowo. Liczba całkowita n to stopień rekurencji, liczba r (\(0≤r≤w-1\)), ukryta w implementacji \(x_{k}^{u}\) dzieli wektor na pół. Liczba m taka że \(1≤m≤n\). Określona jest także stała macierz wielkości \(w×w\) zapisana jako A składająca się ze zbioru zer i jedynek. Każdy z wektorów od zerowego do 623-go inicjalizujemy wartością początkową. Następnie generowane są wartości \(x_{k+n}\) poprzez podstawianie k w postaci 0, 1, 2, ...; Zapis \((x_{k}^{u}│x_{k+1}^{l})\) interpretujemy jako połączenie górnych bitów o długości w - r należących do wektora \(x_{k}\) i dolnych bitów o długości r należących do wektora \(x_{k+1}\). Tak połączony wektor mnożymy przez macierz A, zaś iloczyn dodajemy modulo dwa (XOR) do wektora \(x_{k+m}\). Tak otrzymany wynik generuje wektor \(x_{k+n}\). Macierz A ma postać: $$ A=\begin{pmatrix} &1&&&\\&&1&&\\&&&⋱&\\&&&&1\\a_{w-1}&a_{w-2}&…&…&a_{0} \end{pmatrix} $$ Pozwala to na proste mnożenie macierzy przebiegające tak: $$ xA=\begin{cases} \text{przesun_bity_w_prawo(x)} & x_0=0 \\ \text{przesun_bity_w_prawo(x)} ⨁ a & x_0=1 \end{cases} $$ , gdzie \( a=(a_{w-1}, a_{w-2}, ..., a_0) \), \( x=(x_{w-1}, x_{w-2}, ..., x_0) \) Rekurencję obliczamy z użyciem operatorów bitowych XOR, OR i AND.
Aby ulepszyć równomierne rozmieszczenie algorytmu stosuje się macierz odwrotną T o rozmiarze \(w×w\). Aby użyć jej w algorytmie należy zastosować poniższe operacje: $$ y=x⨁(x>>u) \\ y=y⨁(y>>s) AND b \\ y=y⨁(y>>t) AND c \\ y=y⨁(y>>l) $$, gdzie u, s, t, l to liczby całkowite, natomiast b i c to maski bitowe. Aby rekurencja działała należy stworzyć tablicę liczb całkowitych o długości n. Zrozumienie algorytmu wymaga najpierw zrozumienia jego strony matematycznej, szerzej opisanej w dokumencie przygotowanym przez samych autorów Mersenne Twister'a. Postarałem się ją nieco przybliżyć, lecz osoby, które chcą wejść głębiej w strukturę algorytmu muszą zajrzeć do źródła.

Źródło: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html

sobota, 6 kwietnia 2013

Algorytm Euklidesa - obliczanie NWD

Jest to jeden z najstarszych algorytmów służących do obliczania największego wspólnego dzielnika (NWD) i zarazem jeden z najprostszych algorytmów, jakie tylko sobie można wymarzyć. Został wymyślony przez Euklidesa, słynnego greckiego matematyka.
Danymi wejściowymi są dwie liczby naturalne, dla których szukamy NWD. Najpierw obliczamy resztę z dzielenia dwóch wybranych liczb. Następnie wartość drugiej z tych liczb przypisujemy, jako wartość pierwszej, a obliczoną wcześniej resztę z dzielenia zapisujemy jako drugą z tych liczb. Otrzymujemy dwie nowe liczby, dla których powtarzamy powyższy cykl operacji, dopóki druga z liczb nie będzie równa 0. Na koniec działania algorytmu największym wspólnym dzielnikiem jest pierwsza z tych liczb.
Aby można było łatwiej to sobie wyobrazić zapiszemy dwie liczby wejściowe jako a oraz b, gdzie a,b należą do zbioru liczb naturalnych.
  • Oblicz resztę z dzielenia liczb a i b.
  • Przypisz liczbie a liczbę b.
  • Przypisz liczbie b resztę z dzielenia liczb a i b obliczoną w pierwszym kroku.
  • Jeśli b jest różne od zera, przejdź do kroku 1.

Poniżej prezentuję program w języku C++ implementujący algorytm Euklidesa wykorzystujący operator modulo (reszty z dzielenia):

#include <iostream>

using namespace std;

int main()
{
    int a, b, c;
    cout << "Wprowadz dwie liczby: ";
    cin >> a;
    cin >> b;
    while (b != 0) {
        c = a%b;
        a = b;
        b = c;
    }

    cout << "NWD tych liczb to: " << a << endl;

    return 0;
}

Jest to pierwsza możliwa implementacja algorytmu Euklidesa stosującego resztę z dzielenia. Istnieje jeszcze jedna możliwa stosująca odejmowanie, ale o niej za chwilę. Zajmiemy się omówieniem programu. Najpierw tworzymy 3 zmienne a, b, c reprezentujące liczby całkowite. Następnie wartości liczb a oraz b pobieramy ze standardowego wejścia za pomocą obiektu cin. Wykonujemy pętlę while dopóki liczba b nie będzie równa 0. Wewnątrz pętli do zmiennej c przypisujemy resztę z dzielenia liczb a i b. Potem do liczby a przypisujemy liczbę b, a do liczby b przypisujemy wartość zmiennej c, czyli naszą resztę z dzielenia. Jeśli b równe jest zeru wychodzimy z pętli, w przeciwnym wypadku wracamy na jej początek. Po wyjściu z pętli pozostaje nam tylko wypisanie liczby a będącej największym wspólnym dzielnikiem na standardowe wyjście przy pomocy obiektu cout.

Druga implementacja tego algorytmu korzysta nie z operatora modulo i reszty z dzielenia, lecz z odejmowania liczb od siebie. Jest nieco wolniejsza, gdyż trzeba wykonać sporą liczbę odejmowań dla liczb, które się sporo od siebie różnią, jednak warto o niej wspomnieć. Danymi wejściowymi tutaj również są dwie liczby naturalne a, b. Algorytm polega na tym, że dopóki któraś z liczb nie osiągnie zera odejmujemy większą od mniejszej. Po czym większą z liczb zastępujemy różnicą tych liczb i tak do zera.
  • Oblicz różnicę liczb a i b.
  • Przypisz większej liczbie różnicę obliczoną w kroku pierwszym.
  • Jeśli a i b są różne od zera, przejdź do kroku 1.

Program w języku C++ implementujący algorytm Euklidesa stosujący odejmowanie:

#include <iostream>

using namespace std;

int main()
{
    int a, b, c;
    cout << "Wprowadz dwie liczby: " << endl;
    cin >> a;
    cin >> b;

    while (a != 0 && b != 0) {
        if (a > b) {
            c = a - b;
            a = c;
        } else {
            c = b - a;
            b = c;
        }
    }

    cout << "NWD: " << a + b << endl;

    return 0;
}

Tak jak w poprzedniej implementacji i tutaj będą nam potrzebne trzy zmienne a, b i c będące typem całkowitym. Wprowadzamy je ze standardowego wejścia. Następnie deklarujemy pętlę while, która wykonuje się dopóki a i b są różne od zera. Wewnątrz pętli stosujemy instrukcję warunkową. W przypadku, gdy a jest większe od b, przypisujemy do zmiennej c różnicę liczb a i b. Następnie do zmiennej a przypisujemy zmienną c, czyli otrzymaną różnicę. W innych przypadkach tj. gdy a jest mniejsze lub równe b, przypisujemy do zmiennej c różnicę liczb b i a, po czym inicjujemy zmienną b za pomocą wartości tej różnicy. Gdy pętla zakończy swe działanie, któraś ze zmiennych (a lub b) przechowuje największy wspólny dzielnik wobec czego najprostszą metodą, aby go odczytać nie stosując instrukcji warunkowych jest dodanie obu wartości, gdyż jedna z nich na pewno jest równa zeru. Sumę obu wartości wypisujemy na standardowe wyjście, co jest równoznaczne z wypisaniem NWD.

piątek, 22 marca 2013

Kopiowanie z PuTTy i Pine do schowka

Wydaje się, że to nic trudnego, aczkolwiek potrafi zirytować przeciętnego Kowalskiego, stąd też stwierdziłem, że warto wspomnieć o tym na blogu, ale przejdźmy do sedna.

Otóż, aby skopiować coś z PuTTy z włączonym programem Pine wystarczy zaznaczyć tekst do skopiowania myszką. Automatycznie zaznaczony tekst umieszczany jest w naszym windowsowym schowku i jest gotowy do wklejenia w miejscach gdzie chcemy go umieścić za pomocą Ctrl+C, Shift+Insert czy też za pomocą okienka kontekstowego z opcją wklej. Proste? Proste, a jednak wielu ludzi poświęca nieco czasu zanim do tego dojdzie.

czwartek, 17 maja 2012

Arch linux i czcionki

Linux nie jest systemem wiodącym prym w ilości i jakości czcionek z powodów licencyjnych, dlatego każdy szanujący się użytkownik tego systemu prędzej czy później zostaje zmuszony do instalacji tychże w swym środowisku. „Porządna” instalacja czcionek nie jest jednak operacją typu kopiuj/wklej tak jak miało to miejsce na windowsie. Na przykładzie archa widać, iż czcionki mogą być traktowane jako normalne pakiety oprogramowania wobec czego można nimi kierować za pomocą pacmana. Daje to dość duże pole do manewru dla końcowego użytkownika, gdyż może on zarządzać instalacją, deinstalacją tudzież aktualizacją czcionek poprzez menadżer pakietów. Nawet gdy upatrzonej przez nas czcionki nie ma w repozytoriach warto się troszkę pomęczyć, dla tej wygody, poprzez stworzenie pakietu czcionki i zainstalowanie go za pomocą pacmana. Nie jest to praca czasochłonna i poniżej postaram się ją zwięźle opisać na przykładzie instalacji pewnej czcionki zwanej The One Ring (taka tam z Władcy Pierścieni).

Pierwszym krokiem jaki polecam zrobić jest utworzenie osobnego katalogu, który posłuży nam do pracy. Najlepszym miejscem do tego będzie katalog /home. Przykładowo, tworzymy katalog fontspkgs za pomocą:
mkdir $HOME/fontspkgs
Następnie tworzymy plik o nazwie PKGBUILD którego zawartość powinna wyglądać tak:
pkgname=ttf-one-ring
pkgver=1.0
pkgrel=1
depends=('fontconfig' 'xorg-font-utils')
pkgdesc="The One Ring is font inspired Lord of the Rings trilogy."
arch=('any')
license=('custom:freeware')
source=($pkgname-$pkgver.zip::http://dl.dropbox.com/u/7773489/PKG/gaut-fonts_the-one-ring.zip)
install=$pkgname-$pkgver.install
md5sums=('a0afa52d60cea6c0363a2a8cb39a4095')

build()
{
    cd "$srcdir"
    unzip $pkgname-$pkgver
    mkdir -p $pkgdir/usr/share/fonts/TTF
    cp $srcdir/*.ttf $pkgdir/usr/share/fonts/TTF
}
  • pkgname to nazwa pakietu naszej czcionki. Zaleca się, aby do nazwy czcionki dodawać prefix "ttf-".
  • pkgver to wersja czcionki, zaś pkgrel to numer wydania czcionki.
  • depends to zależności. Obowiązkowe jest wpisanie 'fontconfig' oraz 'xorg-font-utils'. Pakiet 'unzip' jest również potrzebny, gdy ściągana przez nas czcionka mieści się w archiwum zip tak jak w powyższym przypadku. Przykładowo, gdyby mieściła się w archiwum rar, zamiast unzip wpisalibyśmy do zależności unrar.
  • pkgdesc jest opisem pakietu, w tym wypadku będącego czcionką. Warto tutaj wpisać pełną nazwę czcionki, co będzie pomocne przy wyszukiwaniu, gdyby nasz pakiet znalazł się kiedyś w repozytoriach.
  • arch opisuje architekturę systemu, na którą pakiet ten jest przeznaczony. W przypadku czcionek wpisujemy 'any', czyli dowolna.
  • license to znacznik, w którym wpisujemy licencję czcionki. W tym przypadku jest to licencja freeware. Dla licencji GNU GPL wyglądałoby to tak: license=('GPL').
  • source jest określeniem adresu url, z którego pobieramy archiwum z czcionką. Część przed "::" służy nadaniu nowej nazwy pobranemy pakietowi, czyli pakiet gaut-fonts_the-one-ring.zip zostanie po ściągnięciu przemianowany na ttf-one-ring-1.0.zip.
  • noextract nie jest opcją konieczną, gdy nasz pakiet mieści się w archiwum tar, jednak w przypadku zip mówi narzędziu makepkg, aby nie wypakowywało archiwum zaraz po pobraniu.
  • install określa nazwę skryptu instalacyjnego czcionki, który również należy utworzyć.
  • md5sums to tablica sum MD5 plików określonych przez nas w tablicy source. Generujemy je za pomocą polecenia makepkg -g wykonywanego w katalogu z plikami.

Funkcja build() to polecenia wykonywane w celu budowy pakietu. Warto zwrócić uwagę na polecenie unzip $pkgname-$pkgver, które należy zmienić na unrar $pkgname-$pkgver, gdy nasza czcionka mieści się w archiwum rar lub całkowicie usunąć, gdy jest w archiwum tar. Dalsze polecenia tylko kopiują plik czcionki do katalogu share.

Gdy uporamy się już z utworzeniem pliku PKGBUILD musimy stworzyć plik $pkgname-$pkgver.install, co w moim przypadku będzie miało postać ttf-one-ring-1.0.install. Zawartość powinna wyglądać tak:
post_install()
{
    echo -n "Updating font cache... "
    fc-cache -fs >/dev/null
    mkfontscale /usr/share/fonts/TTF /usr/share/fonts/Type1
    mkfontdir /usr/share/fonts/TTF /usr/share/fonts/Type1
    echo "done"
}

post_upgrade()
{
    post_install
}
Jest to skrypt zawierający polecenia poinstalacyjne, które aktualizują systemową bazę czcionek tak, aby były gotowe do użycia od zaraz.

Kolejnym działaniem jest "wypalenie" pliku pakietu za pomocą polecenia
makepkg
Utworzy to pakiet o nazwie $pkgname-$pkgver-$pkgrel-$arch.pkg.tar.xz, co w moim przypadku dało rezultat w postaci ttf-one-ring-1.0-1-any.pkg.tar.xz. Ostatecznym działaniem jest uruchomienie pacmana:
pacman -U ./ttf-one-ring-1.0-1-any.pkg.tar.xz
Pacman zainstaluje czcionkę w odpowiednich dla niej miejscach, po czym wykona nasz skrypt poinstalacyjny w celu aktualizacji bazy czcionek.

Mam nadzieję, że wielu z was skorzysta z tego sposobu instalacji czcionek, który jest dość popularny, gdyż zapewnia niemałą wygodę w użytkowaniu systemu i pozwala zachować jako taki porządek.

czwartek, 16 czerwca 2011

Szyfr przestawieniowy w C++

Ostatnio trochę grzebałem w zadaniach maturalnych z informatyki i trafiłem na polecenie napisania aplikacji używającej szyfru przestawieniowego opartego na macierzy do zaszyfrowania dowolnego tekstu. Długo się nie zastanawiając machnąłem sobie programik, którym mam zamiar się z wami podzielić i przy okazji nieco opisać jego bebechy. Kod programu z implementacją szyfru przestawieniowego:

#include <iostream>
#include <cstring>
#include <cmath>

using namespace std;

int main()
{
    string s = "ala ma kota a kot ma ale";
    string result = "";
    int matrix_size = ceil(sqrt(s.size()));
    char matrix[matrix_size][matrix_size];

    for (int i=0; i<matrix_size; i++) {
        for (int j=0; j<matrix_size; j++) {
            if ((i*matrix_size)+j >= s.length()) {
                matrix[i][j] = '_';
                continue;
            }
            matrix[i][j] = (s[(i*matrix_size)+j] == ' ' ? '_' : s[(i*matrix_size)+j]);
        }
    }

    for (int i=0; i<matrix_size; i++) {
        for (int j=0; j<matrix_size; j++)
            result += matrix[j][i];
    }

    cout << "Tekst zaszyfrowany: " << result << endl;

    return 0;
}

Zacznijmy od linii 15. Tam do wcześniej zdefiniowanego stringa wpisywany jest tekst, który chcemy zaszyfrować. Następnie obliczana jest długość tekstu (linia 17), zaś na jej podstawie program wylicza ilość kolumn i wierszów macierzy (linia 18), w której będzie umieszczony tekst. Funkcja sqrt() liczy pierwiastek z podanej liczby, natomiast ceil() zaokrągla w górę liczbę podaną jako argument. W linii 20 definiujemy macierz, do której następnie wpiszemy tekst do zaszyfrowania.
W liniach 23 - 29 mamy pętle, które wierszami, po jednej literce zapełniają każdą komórkę macierzy. Spacje zamieniane są na podkreślnik. Gdy ilość komórek jest większa od ilości znaków w tekście, końcowe komórki macierzy również są wypełniane podkreślnikami. Po wypełnieniu macierzy znakami w liniach 32 - 37 tekst z tablicy jest spisywany kolumnami do stringa result. W linii 40 na urządzenie wyjścia wypisywany jest zaszyfrowany tekst.

Jest to chyba najprostszy możliwy sposób w jaki można zaimplementować algorytm szyfrowania przestawieniowego.

sobota, 14 maja 2011

Algorytm losujący #1

Podstawowe biblioteki C++ nie oferują zbyt wiele w zakresie generowania losowości – zwłaszcza w kwestii generowania liczb losowych z określonego przez nas przedziału. Mamy co prawda funkcję rand() zdefiniowaną w pliku cstdlib (stdlib.h), jednakże jest ona dość niewygodna do stosowania w niektórych sytuacjach, nie biorąc już nawet pod uwagę zmniejszenia przejrzystości kodu.

Bardzo dobrą praktyką jest napisanie własnej implementacji funkcji losującej dostosowanej do naszych potrzeb. Poniżej przedstawię i opiszę jeden z wielu algorytmów, które bazują na funkcji rand() z biblioteki standardowej.

Schemat blokowy algorytmu


Kod funkcji losującej:

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

int main()
{
    int min, max, result;
    cout << "Podaj dolna granice przedzialu: ";
    cin >> min;
    cout << "Podaj gorna granice przedzialu: ";
    cin >> max;

    if (max >= min) {
        max -= min;
    } else {
        int tmp = min - max;
        min = max;
        max = tmp;
    }

    srand(time(NULL));

    if (max < 0) {
        result = min;
    } else {
        result = (rand()%max) + min;
    }

    cout << "Wynik: " << result;

    return 0;
}


Jak widać algorytm ten nie jest zbyt skomplikowany. Przyjmuje dwa argumenty: min (minimalna liczba jaką chcemy wylosować) i max (maksymalna liczba jaką chcemy wylosować). Następnie sprawdzane jest, czy maksymalna liczba jest większa lub równa minimalnej. Jest to takie małe zabezpieczenie, w przypadku gdyby ktoś korzystający z tego algorytmu pomylił się podczas kodzenia. Jeśli minimalna liczba jest większa od maksymalnej, ich wartości zostają zamienione miejscami. Dodatkowo w zarówno jednym, jak i drugim wyniku warunku od wartości maksymalnej odejmowana jest minimalna, co daje nam ilość możliwych wylosowanych liczb. Później sprawdzane jest czy owa ilość jest większa od zera, a jeśli nie to ostatecznie funkcja zwraca wartość minimalną (min). W przypadku gdy max jest większe od 0 za pomocą wbudowanej w biblioteki standardowe funkcji rand() losujemy liczbę. Wylosowana liczba podzielona przez ilość możliwych liczb do wylosowania daje resztę, do której dodajemy wartość minimalną. W efekcie otrzymujemy (pseudo)losową wartość z przedziału wprowadzonego na początku działania algorytmu.