Come generare un int casuale in C?


Risposte:


663

Nota : non utilizzare rand()per sicurezza. Se hai bisogno di un numero crittograficamente sicuro, vedi invece questa risposta .

#include <time.h>
#include <stdlib.h>

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.

Modifica : su Linux, potresti preferire usare random e srandom .


194
+1 per semplicità, ma è probabilmente una buona idea sottolineare che srand () dovrebbe essere chiamato solo una volta . Inoltre, in un'applicazione con thread, potresti voler assicurarti che lo stato del generatore sia memorizzato per thread e seeding il generatore una volta per ogni thread.
RBerteig,

41
@trusktr, è complicato. Ecco un motivo: time()cambia solo una volta al secondo. Se esegui il seeding da time(), per ogni chiamata a rand(), otterrai lo stesso valore per ogni chiamata in un solo secondo. Ma la ragione più grande è che le proprietà rand()e le funzioni simili sono meglio conosciute per il caso d'uso in cui vengono seminate esattamente una volta per ogni esecuzione e non per ogni singola chiamata. A seconda della "casualità" con proprietà non testate o non provate, si creano problemi.
RBerteig,

9
@trusktr per un semplice generatore congruenziale lineare (che è quello che di rand()solito è) il seeding non rand()avrebbe nella migliore delle ipotesi alcun effetto, e nella peggiore delle ipotesi avrebbe rotto le qualità conosciute del generatore. Questo è un argomento profondo. Inizia con la lettura di Knuth Vol 2 capitolo 3 su numeri casuali come la migliore introduzione alla matematica e alle insidie.
RBerteig,

21
Evita l'avvertimento del compilatore con un cast:srand((unsigned int)time(NULL));
GiovaMaster,

9
Tieni presente che questo è ancora un modo debole di vedere il PRNG. Proprio l'anno scorso, un virus di tipo cryptolocker su Linux ha fatto l'errore di seminare con il tempo, e questo ha ridotto drasticamente lo spazio di ricerca. Tutto quello che dovevi fare era avere un'idea decente di quando si è verificata l'infezione e quindi provare i semi di quel periodo. L'ultima volta che ho sentito, la migliore fonte di casualità è / dev / urandom, che è, presumibilmente, derivato da un miscuglio di fonti caotiche come le temperature sull'hardware. Se tutto ciò che vuoi davvero, tuttavia, è che il tuo programma agisca diversamente su ogni corsa, la soluzione sopra va bene.
Jemenake,

238

La rand()funzione in <stdlib.h>restituisce un numero intero pseudo-casuale compreso tra 0 e RAND_MAX. Puoi usare srand(unsigned int seed)per impostare un seme.

È pratica comune usare l' %operatore insieme rand()a ottenere una gamma diversa (sebbene si tenga presente che ciò getta un po 'fuori l'uniformità). Per esempio:

/* random int between 0 and 19 */
int r = rand() % 20;

Se ti interessa davvero l' uniformità puoi fare qualcosa del genere:

/* Returns an integer in the range [0, n).
 *
 * Uses rand(), and so is affected-by/affects the same seed.
 */
int randint(int n) {
  if ((n - 1) == RAND_MAX) {
    return rand();
  } else {
    // Supporting larger values for n would requires an even more
    // elaborate implementation that combines multiple calls to rand()
    assert (n <= RAND_MAX)

    // Chop off all of the values that would cause skew...
    int end = RAND_MAX / n; // truncate skew
    assert (end > 0);
    end *= n;

    // ... and ignore results from rand() that fall above that limit.
    // (Worst case the loop condition should succeed 50% of the time,
    // so we can expect to bail out of this loop pretty quickly.)
    int r;
    while ((r = rand()) >= end);

    return r % n;
  }
}

18
Va bene una pratica comune , ma non quella corretta. Vedi questo e questo .
Lazer,

35
@Lazer: Ecco perché ho detto "anche se tieni presente che questo getta un po 'fuori l'uniformità".
Laurence Gonsalves il

3
@AbhimanyuAryan %È l'operatore del modulo. Ti dà il resto di una divisione intera, quindi x % nti darà sempre un numero compreso tra 0e n - 1(purché xe nsiano entrambi positivi). Se lo trovi ancora confuso, prova a scrivere un programma con un inumero compreso tra 0 e 100 e stampa i % nper alcune ndelle tue scelte inferiori a 100.
Laurence Gonsalves

2
@necromancer Sono andato avanti e ho aggiunto una soluzione perfettamente uniforme.
Laurence Gonsalves,

2
@Lazer il secondo link che hai pubblicato in realtà non è ancora perfettamente uniforme. Lanciare in doppio e viceversa non aiuta. Il primo link che hai pubblicato ha una soluzione perfettamente uniforme, anche se eseguirà molti loop per piccoli limiti superiori. Ho aggiunto una soluzione perfettamente uniforme a questa risposta che non dovrebbe essere ripetuta nemmeno per piccoli limiti superiori.
Laurence Gonsalves il

53

Se hai bisogno di caratteri casuali sicuri o numeri interi:

Come indicato in come generare in modo sicuro numeri casuali in vari linguaggi di programmazione , ti consigliamo di effettuare una delle seguenti operazioni:

  • Usa l' API di libsodiumrandombytes
  • Riattiva tu stesso ciò di cui hai bisogno dall'implementazione sysrandom di libsodium , con molta attenzione
  • Più in generale, usare/dev/urandom , no /dev/random. Non OpenSSL (o altri PRNG spazio utente).

Per esempio:

#include "sodium.h"

int foo()
{
    char myString[32];
    uint32_t myInt;

    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized, it is not safe to use */
        return 1; 
    }


    /* myString will be an array of 32 random bytes, not null-terminated */        
    randombytes_buf(myString, 32);

    /* myInt will be a random number between 0 and 9 */
    myInt = randombytes_uniform(10);
}

randombytes_uniform() è crittograficamente sicuro e imparziale.


libsodium RNG dovrebbe essere seminato prima di chiamare randombytes_buf?
user2199593,

Basta chiamare sodium_init()ad un certo punto. Non preoccuparti dell'RNG, usa quelli del kernel.
Scott Arciszewski,

Nota: ho approvato la modifica recente sodium_init()anche se non fa necessariamente parte del mio esempio perché è un dettaglio importante.
Scott Arciszewski,

29

Andiamo attraverso questo. Per prima cosa usiamo la funzione srand () per seminare il randomizzatore. Fondamentalmente, il computer può generare numeri casuali in base al numero che viene inviato a srand (). Se fornissi lo stesso valore seed, gli stessi numeri casuali verrebbero generati ogni volta.

Pertanto, dobbiamo seminare il randomizzatore con un valore che cambia sempre. Lo facciamo inserendo il valore dell'ora corrente con la funzione time ().

Ora, quando chiamiamo rand (), ogni volta verrà prodotto un nuovo numero casuale.

#include <stdio.h>

int random_number(int min_num, int max_num);

int main(void)
{
    printf("Min : 1 Max : 40 %d\n", random_number(1,40));
    printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
    return 0;
}

int random_number(int min_num, int max_num)
{
    int result = 0, low_num = 0, hi_num = 0;

    if (min_num < max_num)
    {
        low_num = min_num;
        hi_num = max_num + 1; // include max_num in output
    } else {
        low_num = max_num + 1; // include max_num in output
        hi_num = min_num;
    }

    srand(time(NULL));
    result = (rand() % (hi_num - low_num)) + low_num;
    return result;
}

12
Nice Code, ma non è una buona idea chiamare 'srand (time (NULL));'. questo metodo produce lo stesso numero quando viene chiamato in un ciclo for.
RayOldProf,

1
Le modifiche suggerite che coinvolgono il codice vengono spesso respinte. Qualcuno ne ha fatto uno qui con il commento "l'algoritmo era sbagliato. Potrebbe produrre numeri più grandi del massimo". Non ho valutato il reclamo da solo.
Martin Smith,

1
@Martin Smith Problemi: 1) dovrebbe essere else{ low_num=max_num; hi_num=min_num+1;2) non riesce quando hi_num - low_num > INT_MAX. 3) Omette i valori nella rara situazione INT_MAX > hi_num - low_num > RAND_MAX.
chux - Ripristina Monica il

1
Il ripristino in questo modo farà sì che questa funzione produca lo stesso numero se viene chiamata più volte nello stesso secondo. Se vuoi davvero ridimensionarlo, ridimensionalo solo una volta al secondo.
Élektra,

1
Minore: hi_num = max_num + 1;manca di protezione da tracimazione.
chux - Ripristina Monica l'

25

Se hai bisogno di numeri pseudo casuali di qualità migliore rispetto a quanto stdlibprevisto, dai un'occhiata a Mersenne Twister . È anche più veloce. Le implementazioni di esempio sono numerose, ad esempio qui .


2
+1: Sembra bello ma stavo solo facendo un gioco d'ipotesi. Se avessi usato un generatore di numeri casuali in un'applicazione aziendale, lo avrei sicuramente usato.
Kredns,

4
Non usare un Mersenne Twister, usa qualcosa di buono come xoroshiro128 + o PCG. (Link rilevante.)
Veedrac

17

La funzione C standard è rand(). È abbastanza buono per distribuire carte per il solitario, ma è terribile. Molte implementazioni di rand()ciclo attraverso un breve elenco di numeri e i bit bassi hanno cicli più brevi. Il modo in cui alcuni programmi chiamano rand()è terribile, e calcolare un buon seme a cui passare srand()è difficile.

Il modo migliore per generare numeri casuali in C è utilizzare una libreria di terze parti come OpenSSL. Per esempio,

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } u;

    do {
        if (!RAND_bytes(u.c, sizeof(u.c))) {
            fprintf(stderr, "Can't get random bytes!\n");
            exit(1);
        }
    } while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
    return u.i % limit;
}

/* Random double in [0.0, 1.0) */
double random_double() {
    union {
        uint64_t i;
        unsigned char c[sizeof(uint64_t)];
    } u;

    if (!RAND_bytes(u.c, sizeof(u.c))) {
        fprintf(stderr, "Can't get random bytes!\n");
        exit(1);
    }
    /* 53 bits / 2**53 */
    return (u.i >> 11) * (1.0/9007199254740992.0);
}

int main() {
    printf("Dice: %d\n", (int)(random_uint(6) + 1));
    printf("Double: %f\n", random_double());
    return 0;
}

Perché così tanto codice? Altre lingue come Java e Ruby hanno funzioni per numeri interi o float casuali. OpenSSL fornisce solo byte casuali, quindi provo a imitare il modo in cui Java o Ruby li trasformerebbero in numeri interi o float.

Per gli interi, vogliamo evitare la distorsione del modulo . Supponiamo di avere numeri interi casuali a 4 cifre rand() % 10000, ma rand()possiamo restituire solo 0 a 32767 (come avviene in Microsoft Windows). Ogni numero da 0 a 2767 apparirebbe più spesso di ogni numero da 2768 a 9999. Per rimuovere il bias, possiamo riprovare rand()mentre il valore è inferiore a 2768, perché i valori di 30000 da 2768 a 32767 vengono mappati uniformemente sui 10000 valori da 0 a 9999.

Per i float, vogliamo 53 bit casuali, perché a doublecontiene 53 bit di precisione (supponendo che sia un doppio IEEE). Se utilizziamo più di 53 bit, otteniamo un errore di arrotondamento. Alcuni programmatori scrivono codice come rand() / (double)RAND_MAX, ma rand()potrebbero restituire solo 31 bit o solo 15 bit in Windows.

I RAND_bytes()semi di OpenSSL stessi, forse leggendo /dev/urandomin Linux. Se avessimo bisogno di molti numeri casuali, sarebbe troppo lento leggerli tutti /dev/urandom, perché devono essere copiati dal kernel. È più veloce consentire a OpenSSL di generare più numeri casuali da un seme.

Ulteriori informazioni sui numeri casuali:


Grazie per questa risposta estesa. Nota che tra le 24 risposte attuali a questa domanda, eri l'unico con un'interpretazione extra da affrontare float/ double, quindi ho chiarito la domanda per attenersi ai intnumeri per evitare di renderla troppo ampia. Ci sono altre domande C che si occupano specificamente di valori float/ doublecasuali, quindi potresti voler ripubblicare la seconda metà della tua risposta a domande come stackoverflow.com/questions/13408990/…
Cœur

11

Se il tuo sistema supporta la arc4randomfamiglia di funzioni, ti consiglio di utilizzare quelle invece della randfunzione standard .

La arc4randomfamiglia comprende:

uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)

arc4random restituisce un numero intero senza segno a 32 bit casuale.

arc4random_bufmette il contenuto casuale nel suo parametro buf : void *. La quantità di contenuto è determinata dal bytes : size_tparametro.

arc4random_uniformrestituisce un numero intero senza segno a 32 bit casuale che segue la regola 0 <= arc4random_uniform(limit) < limit:, dove limit è anche un numero intero a 32 bit senza segno.

arc4random_stirlegge i dati /dev/urandome li trasferisce arc4random_addrandomper randomizzare ulteriormente il pool di numeri casuali interno.

arc4random_addrandomviene utilizzato arc4random_stirper popolare il suo pool di numeri casuali interno in base ai dati passati ad esso.

Se non hai queste funzioni, ma sei su Unix, puoi usare questo codice:

/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */

int urandom_fd = -2;

void urandom_init() {
  urandom_fd = open("/dev/urandom", O_RDONLY);

  if (urandom_fd == -1) {
    int errsv = urandom_fd;
    printf("Error opening [/dev/urandom]: %i\n", errsv);
    exit(1);
  }
}

unsigned long urandom() {
  unsigned long buf_impl;
  unsigned long *buf = &buf_impl;

  if (urandom_fd == -2) {
    urandom_init();
  }

  /* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}

La urandom_initfunzione apre il /dev/urandomdispositivo e inserisce il descrittore di file urandom_fd.

La urandomfunzione è sostanzialmente la stessa di una chiamata a rand, tranne più sicura, e restituisce un long(facilmente modificabile).

Tuttavia, /dev/urandompuò essere un po 'lento, quindi si consiglia di usarlo come seme per un diverso generatore di numeri casuali.

Se il sistema non ha una /dev/urandom, ma non hanno una /dev/randomo un file simile, allora si può semplicemente modificare il percorso passato a openin urandom_init. Le chiamate e le API utilizzate nei urandom_inite urandomsono (credo) conformi a POSIX e, come tali, dovrebbero funzionare sulla maggior parte, se non su tutti i sistemi conformi a POSIX.

Note: Una lettura da /dev/urandomNON bloccherà se l'entropia è insufficiente, quindi i valori generati in tali circostanze potrebbero essere crittograficamente insicuri. Se sei preoccupato per questo, allora usa /dev/random, che bloccherà sempre se l'entropia è insufficiente.

Se si utilizza un altro sistema (ad esempio Windows), utilizzare rando alcune API non portatili dipendenti dalla piattaforma specifiche di Windows interne.

Wrapper funzione per la urandom, rando arc4randomle chiamate:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

8

STL non esiste per C. si deve chiamare rand, o meglio ancora, random. Questi sono dichiarati nell'intestazione della libreria standard stdlib.h. randè POSIX, randomè una funzione specifica BSD.

La differenza tra rande randomè che randomrestituisce un numero casuale a 32 bit molto più utilizzabile e in randgenere restituisce un numero a 16 bit. Le manpage di BSD mostrano che i bit inferiori di randsono ciclici e prevedibili, quindi randè potenzialmente inutile per piccoli numeri.


3
@Neil - poiché tutte le risposte finora menzionano l'STL, sospetto che la domanda sia stata modificata rapidamente per rimuovere un riferimento non necessario.
Michael Burr,

rand () non è inutile per i piccoli numeri: puoi spostarli e usare solo i bit più casuali se ne hai davvero bisogno.
Chris Lutz,

@Chris, è possibile se si conosce la dimensione del numero casuale, ma se la dimensione richiesta del numero casuale cambia durante il runtime (come mescolare un array dinamico ecc.) Sarebbe difficile aggirare un simile avvertimento.
dreamlax,

Non riesco a trovare alcuna funzione casuale qui :-(
kasia.b

@ kasia.b in quel link, c'è extern int rand(void);e extern void srand(unsigned int);.
RastaJedi

7

Dai un'occhiata a ISAAC (Indirection, Shift, Accumulate, Add e Count). È distribuito uniformemente e ha una durata media del ciclo di 2 ^ 8295.


2
ISAAC è un RNG interessante per la sua velocità ma non ha ancora ricevuto una seria attenzione crittografica.
user2398029,

4

Questo è un buon modo per ottenere un numero casuale tra due numeri a scelta.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

    #define randnum(min, max) \
        ((rand() % (int)(((max) + 1) - (min))) + (min))

int main()
{
    srand(time(NULL));

    printf("%d\n", randnum(1, 70));
}

Uscita la prima volta: 39

Uscita la seconda volta: 61

Uscita la terza volta: 65

Puoi modificare i valori in seguito randnuma qualunque numero tu scelga e genererà un numero casuale tra questi due numeri.


4

Si desidera utilizzare rand(). Nota ( MOLTO IMPORTANTE ): assicurarsi di impostare il seme per la funzione rand. In caso contrario, i numeri casuali non sono realmente casuali . Questo è molto, molto importante. Per fortuna, di solito è possibile utilizzare una combinazione del timer tick di sistema e la data per ottenere un buon seme.


6
Due punti a) i tuoi numeri casuali non sono "veramente" casuali, non importa come si semina il generatore. E b) è molto conveniente che la sequenza pseudo-casuale sia sempre la stessa in molte circostanze, ad esempio per i test.

16
se è MOLTO IMPORTANTE che il tuo numero sia veramente casuale, non dovresti usare la funzione rand ().
Tylerl,

1
I valori di rand non sono affatto "veramente" casuali, non importa se si imposta il seme o meno. Dato un seme noto la sequenza è prevedibile. La generazione di numeri casuali "veramente" è difficile. Non c'è entropia coinvolta con rand.
dreamlax,

2
Certo che lo faranno: il generatore viene creato per te dalla libreria (probabilmente a zero, ma è un seme valido).

4
Ah, ma l'algoritmo noto / seed noto è essenziale per il debug di qualsiasi programma che utilizza numeri casuali. Non è insolito registrare il seme utilizzato insieme a una corsa di simulazione in modo che possa essere ricreato per un'analisi più dettagliata. Non chiamare affatto srand () equivale a chiamare srand (1).
RBerteig,

4

FWIW, la risposta è che sì, c'è una stdlib.hfunzione chiamata rand; questa funzione è sintonizzata principalmente per la velocità e la distribuzione, non per l'imprevedibilità. Quasi tutte le funzioni casuali integrate per varie lingue e framework utilizzano questa funzione per impostazione predefinita. Esistono anche generatori di numeri casuali "crittografici" che sono molto meno prevedibili, ma funzionano molto più lentamente. Questi dovrebbero essere utilizzati in qualsiasi tipo di applicazione relativa alla sicurezza.


4

Si spera che sia un po 'più casuale del semplice utilizzo srand(time(NULL)).

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
    srand(rand());

    for (int i = 0; i < 10; i++)
        printf("%d\n", rand());
}

1
l'aggiunta di srand (rand ()) non aumenta la casualità della sequenza se questo programma viene eseguito più volte entro 1 secondo. time (NULL) restituirà comunque lo stesso valore per ciascuno di essi, il primo rand () restituirà lo stesso lungo, e la seconda chiamata a srand () avrà lo stesso valore, risultando comunque con la stessa sequenza casuale. L'uso dell'indirizzo di argc potrebbe aiutare, solo se è garantito che questo indirizzo sarà diverso ad ogni esecuzione del programma, il che non è sempre vero.
theferrit32

3

Bene, STL è C ++, non C, quindi non so cosa vuoi. Se vuoi C, tuttavia, ci sono le funzioni rand()e srand():

int rand(void);

void srand(unsigned seed);

Entrambi fanno parte di ANSI C. C'è anche la random()funzione:

long random(void);

Ma per quanto posso dire, random()non è standard ANSI C. Una libreria di terze parti potrebbe non essere una cattiva idea, ma tutto dipende da quanto casuale di un numero è davvero necessario generare.


3

C Programma per generare un numero casuale compreso tra 9 e 50

#include <time.h>
#include <stdlib.h>

int main()
{
    srand(time(NULL));
    int lowerLimit = 10, upperLimit = 50;
    int r =  lowerLimit + rand() % (upperLimit - lowerLimit);
    printf("%d", r);
}

In generale, possiamo generare un numero casuale tra lowerLimit e upperLimit-1

cioè lowerLimit è inclusivo o dire r ∈ [lowerLimit, upperLimit)


@Pang Questo è quello che ho chiaramente menzionato TRA 9 e 50, non DA 9 e 50.
Shivam K. Thakkar,

2
L'operazione del modulo ha introdotto un errore.
17-17

2

rand() è il modo più conveniente per generare numeri casuali.

Puoi anche prendere un numero casuale da qualsiasi servizio online come random.org.


1
Puoi anche prendere un numero casuale da qualsiasi servizio online come random.org Bounty se includi un modo portatile ed efficiente per farlo in C.
MD XF

2
#include <stdio.h>
#include <stdlib.h>

void main() 
{
    int visited[100];
    int randValue, a, b, vindex = 0;

    randValue = (rand() % 100) + 1;

    while (vindex < 100) {
        for (b = 0; b < vindex; b++) {
            if (visited[b] == randValue) {
                randValue = (rand() % 100) + 1;
                b = 0;
            }
        }

        visited[vindex++] = randValue;
    }

    for (a = 0; a < 100; a++)
        printf("%d ", visited[a]);
}

quindi definire in alto con le variabili @ b4hand
Muhammad Sadiq,

Al momento in cui ho fatto quel commento, non avevo i permessi di modifica universali e generalmente le modifiche al codice che hanno cambiato la risposta effettiva sarebbero state respinte. Se non ti interessa risolvere la tua risposta, posso farlo.
b4hand,

Se la tua intenzione era quella di produrre una permutazione casuale di valori univoci, questo codice ha ancora un bug. Produce il valore 84 due volte e non produce il valore 48. Inoltre, non esegue il seeding del generatore di numeri casuali, quindi la sequenza è la stessa per ogni esecuzione.
b4hand,

1
le variabili aeb sono definite in questo codice .. È privo di errori .. non c'è sintassi ed errore logico.
Muhammad Sadiq,

Non corretto. Ho già confermato a mano l'output come ho già detto.
b4hand,

1
#include <stdio.h>
#include <dos.h>

int random(int range);

int main(void)
{
    printf("%d", random(10));
    return 0;
}

int random(int range)
{
    struct time t;
    int r;

    gettime(&t);
    r = t.ti_sec % range;
    return r;
}

1

Nelle moderne CPU x86_64 è possibile utilizzare il generatore di numeri casuali hardware tramite _rdrand64_step()

Codice di esempio:

#include <immintrin.h>

uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
  // Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number

1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//generate number in range [min,max)
int random(int min, int max){
    int number = min + rand() % (max - min);
    return number; 
}

//Driver code
int main(){
    srand(time(NULL));
    for(int i = 1; i <= 10; i++){
        printf("%d\t", random(10, 100));
    }
    return 0;
}

0

Sentendo una buona spiegazione del perché usare rand()per produrre numeri casuali distribuiti uniformemente in un dato intervallo è una cattiva idea, ho deciso di dare un'occhiata a quanto sia effettivamente distorto l'output. Il mio caso di test era un lancio corretto dei dadi. Ecco il codice C:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{
    int i;
    int dice[6];

    for (i = 0; i < 6; i++) 
      dice[i] = 0;
    srand(time(NULL));

    const int TOTAL = 10000000;
    for (i = 0; i < TOTAL; i++)
      dice[(rand() % 6)] += 1;

    double pers = 0.0, tpers = 0.0;
    for (i = 0; i < 6; i++) {
      pers = (dice[i] * 100.0) / TOTAL;
      printf("\t%1d  %5.2f%%\n", dice[i], pers);
      tpers += pers;
    }
    printf("\ttotal:  %6.2f%%\n", tpers);
}

ed ecco il suo output:

 $ gcc -o t3 t3.c
 $ ./t3 
        1666598  16.67%     
        1668630  16.69%
        1667682  16.68%
        1666049  16.66%
        1665948  16.66%
        1665093  16.65%
        total:  100.00%
 $ ./t3     
        1667634  16.68%
        1665914  16.66%
        1665542  16.66%
        1667828  16.68%
        1663649  16.64%
        1669433  16.69%
        total:  100.00%

Non so quanto siano uniformi i tuoi numeri casuali, ma quanto sopra appare abbastanza uniforme per la maggior parte delle esigenze.

Modifica: sarebbe una buona idea inizializzare il PRNG con qualcosa di meglio di time(NULL).


rand () può fallire altri test di casualità, come i test irriducibili . rand () differisce da piattaforma a piattaforma; I valori di rand () di GNU / Linux potrebbero essere migliori dei valori di BSD o Windows.
George Koehler l'

Questo non è un modo valido per verificare la casualità.
Veedrac,

Dipende dallo scopo e dal modello di minaccia / rischio. Per RNG crittograficamente potente, utilizzare RDRAND (o RDSEED). Per un semplice lanciatore di dadi (non a livello di casinò) IMHO quanto sopra dovrebbe essere sufficiente. La parola chiave è " abbastanza buona ".
Mouse,

0

Ho avuto un serio problema con il generatore di numeri pseudo casuali nella mia recente applicazione: ho ripetutamente chiamato il mio programma C tramite uno script pyhton e stavo usando come seed il seguente codice:

srand(time(NULL))

Tuttavia, da quando:

  • rand genererà la stessa sequenza pseudo casuale dando lo stesso seme in srand (vedi man srand );
  • Come già detto, la funzione tempo cambia solo secondo dal secondo: se l'applicazione viene eseguita più volte nello stesso secondo, timerestituirà lo stesso valore ogni volta.

Il mio programma ha generato la stessa sequenza di numeri. Puoi fare 3 cose per risolvere questo problema:

  1. mescolare l'output temporale con alcune altre informazioni che cambiano durante l'esecuzione (nella mia applicazione, il nome dell'output):

    srand(time(NULL) | getHashOfString(outputName))

    Ho usato djb2 come funzione hash.

  2. Aumenta la risoluzione temporale. Sulla mia piattaforma, clock_gettimeera disponibile, quindi lo uso:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
  3. Utilizzare entrambi i metodi insieme:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec | getHashOfString(outputName));

L'opzione 3 ti assicura (per quanto ne so) la migliore casualità dei semi, ma può creare una differenza solo su un'applicazione molto veloce. Secondo me l'opzione 2 è una scommessa sicura.


Anche con queste euristiche, non fare affidamento su rand () per i dati crittografici.
domenukk,

rand()non dovrei essere usato per dati crittografici, sono d'accordo. Almeno per me, la mia applicazione non prevedeva dati crittografici, quindi per me era ok il metodo indicato.
Koldar

0

Nonostante tutti i suggerimenti delle persone rand()qui, non si desidera utilizzare a rand()meno che non sia necessario! I numeri casuali che rand()produce sono spesso molto cattivi. Per citare dalla pagina man di Linux:

Le versioni di rand()e srand()nella libreria C di Linux usano lo stesso generatore di numeri casuali di random(3)esrandom(3) , quindi i bit di ordine inferiore dovrebbero essere casuali come i bit di ordine superiore. Tuttavia, sulle implementazioni rand () precedenti e sulle implementazioni correnti su sistemi diversi, i bit di ordine inferiore sono molto meno casuali dei bit di ordine superiore . Non utilizzare questa funzione in applicazioni destinate a essere portatili quando è necessaria una buona casualità. ( Usa random(3)invece. )

Per quanto riguarda la portabilità, random()è anche definito dallo standard POSIX da un po 'di tempo. rand()è più vecchio, è apparso già nella prima specifica POSIX.1 (IEEE Std 1003.1-1988), mentre è random()apparso per la prima volta in POSIX.1-2001 (IEEE Std 1003.1-2001), ma l'attuale standard POSIX è già POSIX.1-2008 (IEEE Std 1003.1-2008), che ha ricevuto un aggiornamento solo un anno fa (IEEE Std 1003.1-2008, 2016 Edition). Quindi prenderei in considerazionerandom() molto portatile.

POSIX.1-2001 ha anche introdotto il lrand48()mrand48() funzioni e , vedere qui :

Questa famiglia di funzioni deve generare numeri pseudo-casuali utilizzando un algoritmo congruenziale lineare e l'aritmetica intera a 48 bit.

E una fonte pseudo casuale piuttosto buona è la arc4random()funzione disponibile su molti sistemi. Non fa parte di alcuno standard ufficiale, è apparso in BSD intorno al 1997 ma lo puoi trovare su sistemi come Linux e macOS / iOS.


random()non esiste su Windows.
Björn Lindqvist,

@ BjörnLindqvist Anche Windows non è un sistema POSIX; è praticamente l'unico sistema sul mercato che non supporta almeno le API POSIX di base (che anche i sistemi bloccati come iOS supportano). Windows supporta solo rand()in quanto è richiesto anche dallo standard C. Per ogni altra cosa, hai bisogno di una soluzione speciale solo per Windows, come al solito. #ifdef _WIN32è la frase che vedrai più spesso nel codice multipiattaforma che vuole supportare Windows e di solito c'è una soluzione che funziona con tutti i sistemi e una che è richiesta solo per Windows.
Mecki,

0

Per le applicazioni Linux C:

Questo è il mio codice rielaborato da una risposta sopra che segue le mie pratiche di codice C e restituisce un buffer casuale di qualsiasi dimensione (con codici di ritorno appropriati, ecc.). Assicurati di chiamare urandom_open()una volta all'inizio del programma.

int gUrandomFd = -1;

int urandom_open(void)
{
    if (gUrandomFd == -1) {
        gUrandomFd = open("/dev/urandom", O_RDONLY);
    }

    if (gUrandomFd == -1) {
        fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
                  errno, strerror(errno));
        return -1;
    } else {
        return 0;
    }
}


void urandom_close(void)
{
    close(gUrandomFd);
    gUrandomFd = -1;
}


//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
    int ret = 0; // Return value

    if (gUrandomFd == -1) {
        fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
        return -1;
    }

    ret = read(gUrandomFd, buf, size);

    if (ret != size) {
        fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
                 ret, size);
        return -1;
    } else {
        return 0;
    }
}

-2

La mia soluzione minimalista dovrebbe funzionare per numeri casuali nell'intervallo [min, max). Utilizzare srand(time(NULL))prima di richiamare la funzione.

int range_rand(int min_num, int max_num) {
    if (min_num >= max_num) {
        fprintf(stderr, "min_num is greater or equal than max_num!\n"); 
    }
    return min_num + (rand() % (max_num - min_num));
} 

-3

Prova questo, lo metto insieme da alcuni dei concetti già citati sopra:

/*    
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
 */

int random(int max) {
    srand((unsigned) time(NULL));
    return (rand() % max) + 1;
}

16
Questo codice non è buono Chiamare srand()ogni volta che vuoi chiamare rand()è un'idea terribile. Poiché in time()genere restituisce un valore in secondi chiamando questa funzione rapidamente restituirà lo stesso valore "casuale".
Blastfurnace,

3
Questa funzione verrebbe confusa con la random()funzione Unix .
George Koehler il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.