Corso C avanzato su Raspberry PI: Generare numeri casuali

L'esigenza di generare numeri casuali con un sistema embedded è spesso sentita dagli sviluppatori. Sebbene sembri che lo scopo principale di tale possibilità sia quello ludico e del gioco, la finalità va oltre tutte le immaginazioni. Gestire degli eventi random può trovare, infatti, la propria utilità in molte occasioni legate alla robotica, alla domotica e altre mille tipologie di applicazioni. Vediamo, in questa ottava puntata del corso, come generare facilmente i numeri aleatori col nostro amato Raspberry Pi.

Introduzione

Lo scopo della generazione di numeri casuali o di eventi casuali è sempre il medesimo: quello di simulare comportamenti imprevedibili per gli utenti finali che utilizzano il dispositivo. Il caso è legato, quindi, a tutti quegli eventi di cui non si conosce il verificarsi o l’esito a priori. Un microcontrollore è una macchina progettata per eseguire e riprodurre risultati programmati e scontati fin dall'inizio. Esso è, dunque, un sistema deterministico. Nel funzionamento normale, dunque, esso non è capace di produrre risultati assolutamente casuali, sconosciuti e imprevedibili per l’uomo. Ci sono, ovviamente, dei casi in cui occorre effettuare la generazione di eventi o di numeri casuali, per poter dare un senso di realtà e di “umanità” alle applicazioni informatiche create per il sistema embedded. La macchina non è in grado di generare un evento realmente casuale, e il più delle volte occorre affiancare questa funzione a un evento esterno che agevoli il raggiungimento del risultato. Per effettuare delle generazioni di numeri o di sequenze di numeri casuali, possiamo seguire alcuni metodi validi.

La funzione rand()

Il prototipo di questa funzione è contenuto nel file d'inclusione "stdlib.h". Essa restituisce un numero pseudo-casuale compreso tra 0 e RAND_MAX. Un esempio di utilizzo è il seguente:

n=rand();

Il valore di tale costante può cambiare da sistema a sistema. Senza una particolare metodologia, la funzione rand() restituirebbe sempre la medesima sequenza di numeri, a ogni boot di sistema. Occorre, pertanto, garantire l'incertezza della generazione utilizzando quest'altra chiamata di funzione:

srand(time(NULL));

che predispone il generatore di numeri a fornire sequenze sempre diverse.

Il seguente listato, il cui output può essere osservato in figura 1, genera venti numeri casuali e, in aggiunta, visualizza il valore di RAND_MAX.

#include "stdio.h"
#include "stdlib.h"
#include "time.h"
int main() {
    int n,i;
    printf("Il valore di RAND_MAX e': %d\n",RAND_MAX);
    printf(" \n");
    srand(time(NULL));
    for(i=1;i<=20;i++) {
        n = rand();
        printf("%d\n",n);
    }
    return 0; 
}

Nel caso del Raspberry Pi, il valore di RAND_MAX è di 2147483647.

Figura 1: la funzione rand() genera numeri pseudo-casuali compresi tra 0 e RAND_MAX

Figura 1: la funzione rand() genera numeri pseudo-casuali compresi tra 0 e RAND_MAX

Adeguamento della funzione rand()

La generazione di numeri casuali così elevati, raramente sono di pratica utilità. Occorre, infatti, adeguare l'intervallo di generazione ad applicazioni più vicine alle esigenze operative. Con una semplicissima modifica del codice, la funzione rand() riesce a generare dei numeri casuali, stavolta nel "range" più congeniale al programmatore. Per esempio, la generazione di numeri aleatori compresi tra 1 e 10 implica l'adozione del seguente statement, da sostituire nel listato visto prima:

n = rand() % 10 + 1;

Tale formula, stavolta, calcola il resto (o modulo) della divisione tra il numero generato e 10 (che praticamente può essere compreso tra 0 e 9) e aggiunge 1 per adeguarlo al range compreso tra 1 e 10. Il tutto è estremamente semplice. Si usi, dunque, la precedente formula per generare qualsiasi sequenza, anche di numeri negativi. La figura 2 mostra l'esecuzione del programma adeguato.

Figura 2: con l'utilizzo dell'operatore "%" (resto) è possibile generare numeri casuali nell'intervallo desiderato

Figura 2: con l'utilizzo dell'operatore "%" (resto) è possibile generare numeri casuali nell'intervallo desiderato

Di seguito alcuni esempi di utilizzo della formula, per la generazione di diversi intervalli casuali:

  • da 1 a 2: rand() % 2 + 1;
  • da 0 a 1: rand() % 2;
  • da 1 a 100: rand() % 100 + 1;
  • da 8 a 12: rand() % 5 + 8;
  • da -3 a +3: rand() % 7 -3;

E' equa la funzione rand()?

Sarebbe interessante verificare se la funzione rand() sia equa, ossia se segua la legge dei grandi numeri per la generazione di numeri casuali. Effettuando milioni di estrazioni aleatorie, il numero di esiti sortiti dovrebbe corrispondere a quelli dettati dalla probabilità matematica delle generazioni stesse. Il seguente programma esegue proprio tale verifica. Estrae, per un numero esorbitante di volte, nell'esempio cento milioni di volte (100.000.000), un valore compreso tra 1 e 10, contandolo in un apposito array di accumulatori. Alla fine dell'esecuzione i risultati sono mostrati a video, come si può osservare in figura 3.

#include "stdio.h"
#include "stdlib.h"
#include "time.h"
int main() {
    int n; 
    long i,q[11],somma;
    srand(time(NULL));
    for(i=1;i<=10;i++)
        q[i]=0;
    for(i=1;i<=100000000;i++) {
        n = rand() % 10 + 1;
        q[n]++;
    }
    printf("\n");
    for(i=1;i<=10;i++)
        printf("Numero di generazioni di %21d: %91d\n",i,q[i]);
    somma=0;
    for(i=1;i<=10;i++)
        somma=somma+q[i];
    printf(" \n");
    printf("Totale: %91d\n",somma);
    printf("\n");
    return 0; 
}

Occorre attendere alcuni secondi, prima che il programma visualizzi le quantità dei valori estratti. Come si vede dai risultati, il numero di estrazioni segue perfettamente quella dettata dalle relative probabilità matematiche.
[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2311 parole ed è riservato agli abbonati MAKER. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici MAKER e potrai fare il download (PDF) dell'EOS-Book del mese. ABBONATI ORA, è semplice e sicuro.

Abbonati alle riviste di elettronica

6 Commenti

  1. Adriano Gandolfo Adriano Gandolfo 5 marzo 2019
  2. Giovanni Di Maria Giovanni Di Maria 5 marzo 2019
    • Nello Roscini 11 marzo 2019
  3. Giovanni Di Maria Giovanni Di Maria 5 marzo 2019
    • Stefano Lovati Stefano Lovati 6 marzo 2019
  4. Giordana Francesca Brescia Giordana Francesca Brescia 5 marzo 2019

Scrivi un commento