back to top

Numeri random in C++

La generazione di numeri random รจ una funzionalitร  importante in molti programmi e applicazioni, come videogiochi, crittografia, simulazioni e intelligenza artificiale. In questo articolo vedremo come generare numeri pseudo-casuali in C++ utilizzando la funzione rand() e il piรน moderno std::random della libreria <random> introdotta con C++11.

La funzione rand() e il valore RAND_MAX

Vediamo come generare numeri casuali in C++ mediante la funzione rand() la quale viene utilizzata per generare un numero compreso nell’intervallo tra 0 e RAND_MAX, dove RAND_MAX รจ un valore che cambia a seconda del compilatore usato (in genere 32767).

Pubblicitร 

Nell’esempio che segue vediamo come generare un numero compreso tra 0 e 999:

#include<iostream>
#include<cstdlib>
using namespace std;

int main()
{
  // genero un numero casuale compreso tra 0 e 999
  cout<<rand()%1000<<endl;
}

Generare un numero random compreso in un intervallo: l’operatore modulo (%)

L’istruzione rand()%1000 genera un numero intero compreso tra 0 e 999. Se utilizziamo l’istruzione rand()%N, infatti, otterremo un numero compresi tra 0 ed N-1.

Per impostare un massimo si ricorre all’operatore modulo (%) che, come sappiamo, serve per calcolare il resto di una divisione intera e questo spiega perchรฉ il valore massimo puรฒ essere N-1! Facciamo un esempio: un qualsiasi numero diviso per 5 puรฒ dare come resto 0, 1, 2, 3 e 4… non puรฒ dare 5 perchรฉ altrimenti avremmo una divisione intera senza resto!

Qualche esempio:

rand()%2 // genera un numero compreso tra 0 e 1
rand()%100 // genera un numero compreso tra 0 e 99
rand()%43 // genera un numero compreso tra 0 e 42
rand()%10+5 // genera un numero compreso tra 5 e 14

Nell’ultima istruzione abbiamo aggiunto +5 (questo numero prende il nome di offset) a entrambi i numeri dell’intervallo (il minore ed il maggiore).

Il numero casuale รจ sempre lo stesso!

Proviamo a salvare, compilare il codice visto all’inizio di questo articolo e ad eseguire il programma. Noterete che tutte le volte che eseguite il codice otterrete sempre lo stesso numero! Ma com’รจ possibile? Non doveva essere un generatore di numeri casuali??

La spiegazione di questo fenomeno รจ legata alla funzione rand(): questa, infatti, non genera un numero realmente casuale ma, semplicemente, restituisce il prossimo valore pseudo-casuale nella sequenza di valori che va da 0 a RAND_MAX. Per ottenere un numero casuale, quindi, รจ necessario cambiare il punto iniziale (seme) di quella sequenza usando srand().

La pseudo-casualitร  รจ proprio questo: il risultato generato dall’algoritmo sembra casuale ma in realtร  non lo รจ. E’ prevedibile.

Generare un numero random in C++ utilizzando srand() e time()

Per migliorare la casualitร  del risultato dobbiamo modificare il codice visto in precedenza con le funzioni srand() e time(). Vediamo un esempio che prevede l’utilizzo di un semplice ciclo for per creare non uno ma 10 numeri casuali:

#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;

int main()
{
  // inizializzo il generatore di numeri pseudo-casuali
  srand(time(NULL));

  for(int i=0;i<10;i++) {
    // genero un numero casuale compreso tra 0 e 9
    cout<<rand()%10<<endl;
  }
}

La funzione srand() serve a inizializzare la funzione per la generazione dei numeri casuali: senza di essa allo stesso seed (seme) il programma estrarrebbe sempre gli stessi numeri casuali. Un trucco per rendere casuale il seme รจ quello di impostarlo con time(NULL) o time(0) incorporando la relativa funzione che si trova nella libreria ctime (che abbiamo incluso nel nostro progetto).

Si noti che nel primo esempio proposto in questo articolo la funzione srand() non era presente: tutte le volte che rand() viene usato senza che il programmatore abbia premesso srand() รจ come se fosse stato impostato srand(1)… ed รจ per questo che l’esempio precedente restituiva sempre la stessa sequenza di risultati con conseguenze non ottimali sulla casualitร  del risultato atteso dal codice.

Superare i limiti di rand() con il metodo std::random

Come abbiamo giร  avuto modo di notare, l’uso di rand() per la generazione di numeri random in C++ presenta alcune limitazioni:

  • Bassa qualitร  della casualitร  โ€“ rand() non รจ adatto per applicazioni critiche (es. crittografia) poichรฉ puรฒ generare sequenze prevedibili.
  • Non รจ thread-safe โ€“ in ambienti multi-thread puรฒ causare problemi.
  • Bias nella distribuzione โ€“ rand() % N non genera numeri perfettamente uniformi se RAND_MAX + 1 non รจ un multiplo di N.

Per risolvere questi problemi, si consiglia di usare la libreria <random> introdotta con C++11 che offre migliore qualitร  e controllo sui numeri generati.

E’ bene precisare che si tratta ancora di numeri pseudo-casuali ma il risultato che รจ possibile ottenere รจ ben migliore rispetto a quello visto in precedenza.

Ecco un esempio che utilizza il generatore Mersenne Twister (std::mt19937) per generare un numero casuale tra 1 e 100 in C++:

#include <iostream>
#include <random>

int main() {
    // Creazione del generatore di numeri casuali con seed casuale
    std::random_device rd;
    std::mt19937 gen(rd()); // Generatore Mersenne Twister
    std::uniform_int_distribution<int> distribuzione(1, 100); // Intervallo [1, 100]

    // Genera un numero casuale
    int numero_casuale = distribuzione(gen);
    std::cout << "Numero casuale generato: " << numero_casuale << std::endl;

    return 0;
}

Ma quali sono i vantaggi di <random> rispetto a rand()?

  • Migliore casualitร : usa algoritmi avanzati come il Mersenne Twister
  • Distribuzioni personalizzabili: puoi generare numeri con distribuzione normale, esponenziale, poissoniana, etc.
  • Migliore sicurezza: std::random_device puรฒ essere usato anche per applicazioni crittografiche.

Generare numeri casuali in virgola mobile

Se vuoi generare numeri casuali con virgola mobile รจ possibile usare std::uniform_real_distribution<float>:

#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> distribuzione(0.0, 1.0);

    float numero_casuale = distribuzione(gen);
    std::cout << "Numero casuale generato: " << numero_casuale << std::endl;

    return 0;
}

Questo codice genererร  numeri casuali tra 0.0 e 1.0 con una distribuzione uniforme.

Conclusione

La generazione di numeri casuali รจ un aspetto fondamentale della programmazione, ma come abbiamo visto, il termine casuale รจ spesso una semplificazione.

In realtร , rand() e perfino i generatori avanzati come std::mt19937 producono numeri pseudo-casuali, cioรจ sequenze deterministiche che sembrano casuali, ma sono generate seguendo algoritmi prevedibili.

Questo รจ sufficiente per la maggior parte delle applicazioni, come i videogiochi o la simulazione di dati. Tuttavia, quando lโ€™imprevedibilitร  รจ cruciale (come nella crittografia, nella sicurezza informatica o nelle simulazioni scientifiche) la pseudo-casualitร  non basta.

In questi casi, entrano in gioco generatori veramente casuali, che si basano su fenomeni fisici imprevedibili (ad esempio, il rumore termico nei circuiti elettronici o il decadimento radioattivo).

Se il tuo obiettivo รจ avere un numero sufficientemente casuale per un gioco o una simulazione, std::mt19937 รจ piรน che adeguato. Ma se devi garantire una sicurezza assoluta, potresti dover esplorare strumenti piรน sofisticati come hardware random number generators (HRNG) o servizi esterni per lโ€™entropia casuale.

Alla fine, la vera domanda non รจ โ€œCome generare un numero casuale?โ€, ma โ€œQuanto casuale deve essere il mio numero?โ€. E la risposta dipende sempre dal contesto in cui lo stai usando. 

Altri contenuti interessanti

Pubblicitร 
Massimiliano Bossi
Massimiliano Bossi
Stregato dalla rete sin dai tempi delle BBS e dei modem a 2.400 baud, ho avuto la fortuna di poter trasformare la mia passione in un lavoro (nonostante una Laurea in Giurisprudenza). Adoro scrivere codice e mi occupo quotidianamente di comunicazione, design e nuovi media digitali. Orgogliosamente "nerd" sono il fondatore di MRW.it (per il quale ho scritto centinaia di articoli) e di una nota Web-Agency (dove seguo in prima persona progetti digitali per numerosi clienti sia in Italia che all'estero).

Leggi anche...

Vibe Coding: cosโ€™รจ, come funziona e quali sono i migliori strumenti AI per programmare

Immagina di poter scrivere software senza dover digitare una...

I migliori libri per imparare a programmare in Python

Imparare a programmare in Python รจ un passo fondamentale...

Il file manifest.json: cos’รจ e a cosa serve

Il file manifest.json รจ un componente chiave nelle applicazioni web moderne,...

Java: cos’è e a cosa serve l’operatore modulo (%)

In Java, l'operatore modulo è rappresentato dal simbolo "%"...

Radice quadrata in C: vediamo come calcolarla in diversi modi

La radice quadrata è un'operazione matematica piuttosto comune (in...

Sperimentare la sequenza di Collatz in C++

Vediamo come verificare la congettura di Collatz con C++....
Pubblicitร