...

A. Veneziani - Generazione di numeri casuali in C++

by user

on
Category: Documents
23

views

Report

Comments

Transcript

A. Veneziani - Generazione di numeri casuali in C++
A. Veneziani - Generazione di numeri casuali in C++
Il calcolatore in quasi tutti i suoi usi si deve comportare come un sistema deterministico, dati ben precisi
input si presume che esso debba produrre, seguendo una logica, ben precisi e determinati output, relativi,
ad esempio, alla soluzione di un certo problema.
In alcuni casi invece è necessario che esso sia in grado anche di simulare fenomeni aleatori, ossia casuali,
quali tipicamente il lancio di un dado o il lancio di una moneta o altri simili. Questo non solo con lo scopo
di simulare tali situazioni, ma considerando che la capacità di generare situazioni casuali può essere
necessaria in numerosi contesti più complessi. Si pensi ai videogiochi, ad esempio ed alla possibilità di
dover simulare situazioni non ripetitive e quindi imprevedibili in alcuni elementi di essi (il mostro si
muoverà a destra o a sinistra ? L’astronave aliena devierà a in alto o in basso nello schermo per sfuggirci ?)
Considerato questo diventa più chiaro che è necessario che anche il calcolatore, macchina solitamente
totalmente deterministica (avete mai sentito parlare di programmi che hanno una logica “a caso” ?) abbia
la capacità di esprimere situazioni non deterministiche. Questa capacità al più basso livello si concretizza
con la possibilità di generare numeri casuali, invece che numeri definiti da procedimenti determinati e
quindi univoci. Vediamo come essi possono essere prodotti in C++ e poi vediamo un applicazi one:
Il listato di un semplice programma generatore di numeri casuali potrà essere questo:
#include <iostream>
using namespace std;
#include <time.h>
main()
{
int cc, n_cas;
srand(time(NULL));
cout << "Cento numeri casuali tra 1 e 10:
cc = 0;
while (cc < 100)
{
n_cas = rand() % 10 + 1;
cout << n_cas << endl;
cc++;
" << endl;
}
}
Vi sono diverse cose da puntualizzare riguardo a questo listato. La prima è che esso contiene due funzioni
C++ nuove fondamentali utili allo scopo di generare numeri casuali. Senza di esse questa operazione non
sarà possibile se non re-implementando l’algoritmo che genera i numeri casuali stessi1.
La prima è la funzione rand(); essa in C++ genera numeri casuali tra i valori 0 ed un valore massimo di 215 =
32768, ossia pari al range dei soli numeri positivi di una variabile int a 16 bit (short).
1
Tali numeri che in realtà sembrano privi di ogni regola sono invece generati tramite apposite funzioni matematiche con un particolare
procedimento ciclico di calcolo.
1
Tuttavia nel nostro problema ci siamo posti il problema di generare numeri sì casuali ma da 1 a 10, e quindi
questa fonte casuale deve essere rielaborata per ottemperare a quanto richiesto.
In realtà è piuttosto facile risolvere questo problema; si osservi l’ istruzione :
n_cas = rand() % 10 + 1;
essa ha esattamente questo scopo, ossia trasformare il campo di generazione dei numeri casuali su un
altro intervallo numerico (in questo caso da 1 a 10 compresi). Infatti: l’operazione di modulo ivi presente
è relativa ad una divisione per 10. Rende quindi il resto del numero casuale originario a cui venga applicata
una divisione intera per 10. Questo resto, sappiamo dall’aritmetica può essere un numero intero tra 0 e 9.
Essendo il numero originario casuale, anche il resto, in cascata, risulterà casuale. Per raggiungere il nostro
scopo quindi basta solo un altro passo: trasporre l’intervallo di nume ri interi 0, 9 verso quello da 1 a 10. I
effetti abbiamo sempre 10 numeri generati; basta quindi solo aumentare di una unità tutti i numeri
ottenuti dal primo passo di elaborazione (operazione di modulo) per ottenere i numeri da 1 a 10 voluti.
A questo punto, tramite un opportuno ciclo while attorno a questa istruzione (e a poche altre) ottengo i
100 numeri casuali voluti, con un procedimento ciclico.
Come si può osservare questo è piuttosto semplice; è stato istituito un contatore (cc) che conta quanti cicli
si stiano effettuando, grazie ad un incremento unitario interno al ciclo (cc++). All’esterno del ciclo il valore
di cc deve ovviamente essere inizializzato a 0 (cc = 0).
A questo punto il programma, aggiunto un opportuno output, parrebbe completo. In realtà non è così. La
funzione rand(), se non opportunamente inizializzata, genera purtroppo sempre la stessa sequenza di
numeri, sì casuali, ma immutabilmente ripetuti !
Questa cosa assomiglia a una situazione deterministica, perché numeri “a caso” prodotti sempre con una
stessa sequenza divengono comunque ovviamente prevedibili.
Per evitare ciò si deve effettuare
l’inizializzazione del generatore di numeri casuali o come certe volte si dice la randomizzazione dei numeri
casuali. In realtà la funzione che genera i numeri casuali parte da un valore che, nel caso appena
considerato, è sempre lo stesso. Dato ciò, siccome il procedimento di generazione non è verame nte
2
casuale, ma ben definito, essa genera sequenze di numeri sempre uguali. Diverse sequenze “casuali”2 si
possono ottenere solo partendo da un numero iniziale diverso. Tale inizializzazione del generatore viene
effettuata dalla funzione srand(). Per operare però srand deve avere come valore del suo unico parametro
un numero di volta in volta diverso, e questo è poco proponibile venga dato a mano dell’utente.
Si considera a questo scopo il valore dell’ora di sistema. In realtà essa è definita da ore, minuti e secondi,
ma è anche vero che essa può essere ridotta ad una quantità in secondi. In questo caso più che calcolare la
quantità in secondi presa dall’ inizio della giornata, viene presa la quantità in secondi dalla data 1/1/1970 3 .
Questo numero di secondi continuamente variabile ad ogni esecuzione viene passato alla routine di
generazione di numeri casuali per avere una particolare sequenza di numeri, di norma diversa dalle
precedenti.
Il valore suddetto viene generato tramite la funzione time(…), che genera un valore pari al numero di
secondi trascorsi dal momento indicato nel parametro. Il valore NULL di questo parametro sta ad indicare il
momento corrente (tempo attuale). E’ necessario usare questa forma, perché se fissassimo un momento
preciso tale valore non varierebbe mai e quindi si riproporrebbe il problema iniziale (mancanza di casualità
della sequenza di numeri casuali generati). Ad ogni modo considerando viceversa che il parametro sia
inizializzato ad altri valori, si otterranno i secondi tra le 00:00 dell’ 1/1/1970 e un momento indicato4. La
conoscenza dei contenuti di tale variabile strutturata và oltre gli scopi di questa esercitazione. Si noti infine
che l’uso della funzione time(…) presume l’inclusione del file header <time.h>.
2
3
4
In realtà non realmente dovute ad un processo effettivamente casuale, e proprio per questo tali numeri vengono detti pseudo -casuali.
Data considerata di riferimento per la nascita del sistema operativo Unix
Tale valore và inserito in una apposita variabile strutturata passata tramite il parametro
3
Fly UP