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 distribuzionerand() % 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à