View Full Version : [C] funzione rand()
tecno789
14-01-2012, 08:51
Ho bisogno di fare il programma stupido del lancio della moneta, ho provato ad utilizzare la rand(), ma purtroppo ad ogni run del programma, mi genera lo stesso numero e quindi esce sempre croce, com'è possibile sto fatto? come faccio a generare numeri diversi all'interno di un intervallo da me imposto??
Prima di usare la rand() prova a chiamare la srand() in questo modo:
srand ( time(NULL) );
Ricordati di includere time.h
#include <time.h>
La srand() puoi chiamarla una volta sola all'inizio del main (per esempio). Ha lo scopo di inizializzare il seme del generatore di numeri causali, in questo caso utilizzando un'informazione imprevedibile presa dall'orologio di sistema. Così ogni volta che avvii il programma dovrebbe avere valore diverso e i successivi usi di rand() danno valori diversi ad ogni esecuzione. Spero di ricordare bene :)
tecno789
14-01-2012, 10:46
Prima di usare la rand() prova a chiamare la srand() in questo modo:
srand ( time(NULL) );
Ricordati di includere time.h
#include <time.h>
La srand() puoi chiamarla una volta sola all'inizio del main (per esempio). Ha lo scopo di inizializzare il seme del generatore di numeri causali, in questo caso utilizzando un'informazione imprevedibile presa dall'orologio di sistema. Così ogni volta che avvii il programma dovrebbe avere valore diverso e i successivi usi di rand() danno valori diversi ad ogni esecuzione. Spero di ricordare bene :)
si è giusto, ricordi bene. ma a me serve generare dei valori diversi in un range e con la srand non si può fare.
Anche questo è fattibile.
Se vuoi generare un numero casuale compreso tra 0 e 99 (per esempio) devi fare:
int num_casuale = rand() % 100;
L'operatore % (modulo) ritorna il resto della divisione intera tra il risultato di rand() e 100 quindi al max 99. Sta a te adattare l'esempio al tuo caso.
tecno789
14-01-2012, 11:17
Anche questo è fattibile.
Se vuoi generare un numero casuale compreso tra 0 e 99 (per esempio) devi fare:
int num_casuale = rand() % 100;
L'operatore % (modulo) ritorna il resto della divisione intera tra il risultato di rand() e 100 quindi al max 99. Sta a te adattare l'esempio al tuo caso.
ma sai cosa? che ho provato a fare così ma genera sempre gli stessi identici numeri, infatti ho provato a fare anche qualche ciclo di generazione, vengono sempre fuori i soliti numeri , non cambiano, come mai??
:confused: strano...
Puoi postare il codice "incriminato"?
__ZERO_UNO__
14-01-2012, 13:52
Scrivere rand() % 100 non ha alcun senso perchè rand() genera numeri reali compresi fra 0 e 1 e quindi il risultato di quell'espressione sarà sempre rand().
Se ho capito bene quello che vuoi fare (simulare il lancio di una moneta) puoi fare così:
u = rand();
if u <= 0.5
risultato = croce;
else
risultato = testa;
per avere un singolo lancio. Se continui ad avere sempre lo stesso risultato controlla bene che ad ogni iterazione venga nuovamente "estratto" il numero casuale qui definito come u.
Per quanto riguarda l'estrazione di numeri casuali da un intervallo ti invito a leggere questo breve post http://www.hwupgrade.it/forum/showthread.php?t=2435128
Scrivere rand() % 100 non ha alcun senso perchè rand() genera numeri reali compresi fra 0 e 1
Ma anche no... rand() genera numeri casuali tra 0 e la costante RAND_MAX (definita in qualche header file che non ricordo)...
Infatti rand torna un int, mica un numero in virgola!
__ZERO_UNO__
14-01-2012, 14:03
Hai perfettamente ragione. Avevo dimenticato che si sta parlando di C.
Chiedo venia.
mesonepigreco
14-01-2012, 16:48
rand viene sconsigliato nell'uso (chiaramente se si tratta del lancio della monetina va bene) ma se vuoi usare il programma per stimare la percentuale di volte che esce testa, e quella che esce croce su un numero di eventi molto grande e ottenere stime precise (che sarebbe una cosa stupida con la monetina visto che si sa che è del 50 %, ma interessante in un infinità di altri casi) conviene usare lrand48() si inizializza in questo modo:
#include <stdlib.h>
#include <time.h>
int main() {
srand48(time(0)); // Inizializzazione del generatore di numeri casuali
.... codice ...
}
double FloatRandomNumber(double min, double max) {
return min + (max - min) * lrand48() / (double) RAND_MAX;
}
int IntRandomNumber(int min, int max) {
return min + lrand48() % (max - min + 1);
}
A questo punto puoi chiamare le due funzioni, la prima genera un numero casuale decimale in un intervallo dato, la seconda un intero. Con qualche accorgimento puoi migliorare ulteriormente la prima per fargli dire se non includere gli estremi dell'intervallo. Il casting a double su RAND_MAX occorre perché altrimenti lrand48() / RAND_MAX risulta una divisione tra interi, quindi il risultato è un intero, poiche lrand48() è sempre minore di RAND_MAX (tranne che in un caso su circa quattromiliardi o giù di li, dipende dalla cpu) quella divisione fa 0 e quindi salta tutto.
rand viene sconsigliato nell'uso (chiaramente se si tratta del lancio della monetina va bene) ma se vuoi usare il programma per stimare la percentuale di volte che esce testa, e quella che esce croce su un numero di eventi molto grande e ottenere stime precise (che sarebbe una cosa stupida con la monetina visto che si sa che è del 50 %, ma interessante in un infinità di altri casi) conviene usare lrand48() si inizializza in questo modo:
sicuro?
io nel man di lrand48 (che include anche drand48, erand48, lrand48, nrand48, mrand48, jrand48) leggo:
These functions are declared obsolete by SVID 3, which states that rand(3) should be used instead.
poi nel man di rand:
The versions of rand() and srand() in the Linux C Library use the same random number generator as random(3) and srandom(3), so the lower-order bits should be as random as the higher-order bits. However, on older rand() implementations, and on current implementations on different systems, the lower-order bits are much less random than the higher-order bits. Do not use this function in applications intended to be portable when good randomness is needed. (Use random(3) instead.)
mesonepigreco
15-01-2012, 11:06
In genere rand viene sconsigliata in genere perché in aclune vecchie implementazioni non ha una grande precisione. lrand48() e simili sono sconsigliate se devono essere all'interno di algoritmi di sicurezza, poiché sono facilmente violabili (da questo punto di vista penso siano obsolete). Nel manuale Practical UNIX & internet security ho trovato questo:
23.7.1 rand ( )
The original UNIX random number generator, rand( ) , is not a very good random number generator. It uses a 32-bit seed and maintains a 32-bit internal state. The output of the function is also 32 bits in length, making it a simple matter to determine the function's internal state by examining the output. As a result, rand( ) is not very random. Furthermore, the low-order bits of some implementations are not random at all, but flip back and forth between 0 and 1 according to a regular pattern. The rand( ) random number generator is seeded with the function srand( ) . On some versions of UNIX, a third function is provided, rand_r( ) , for multi threaded applications. (The function rand( ) itself is not safe for multi- threading, as it maintains internal state.)
Do not use rand( ) , even for simple statistical purposes.
23.7.2 random ( )
The function random( ) is a more sophisticated random number generator which uses nonlinear feedback and an internal table that is 124 bytes (992 bits) long. The function returns random values that are 32 bits in length. All of the bits generated by random( ) are usable.
The random( ) function is adequate for simulations and games, but should not be used for security related applications such as picking cryptographic keys or simulating one- time pads.
23.7.3 drand48 ( ), lrand48 ( ), and mrand48 ( )
The function drand48( ) is one of many functions which make up the System V random number generator. According to the Solaris documen- tation, the algorithm uses "the well-known linear congruential algorithm and 48-bit integer arithmetic." The function drand48( ) returns a double-precision number that is greater or equal to 0.0 and less than 1.0, while the lrand48( ) and mrand48( ) functions return random numbers within a specified integer range. As with random( ) , these functions provide excellent random numbers for simulations and games, but should not be used for security-related applications such as picking cryptographic keys or simulating one-time pads; linear congruential algorithms are too easy to break.
In genere rand viene sconsigliata in genere perché in aclune vecchie implementazioni non ha una grande precisione. lrand48() e simili sono sconsigliate se devono essere all'interno di algoritmi di sicurezza, poiché sono facilmente violabili (da questo punto di vista penso siano obsolete). Nel manuale Practical UNIX & internet security ho trovato questo:
da quanto ho visto, per gli algoritmi di sicurezza, in genere viene usata /dev/random (almeno su unix), per il resto random() dovrebbe andare abbastanza bene ed evitare il problema di rand() con le vecchie rappresentazioni
tecno789
15-01-2012, 16:24
da quanto ho visto, per gli algoritmi di sicurezza, in genere viene usata /dev/random (almeno su unix), per il resto random() dovrebbe andare abbastanza bene ed evitare il problema di rand() con le vecchie rappresentazioni
sinceramente non mi interessano molto ora gli algoritmi di sicurezza, più che altro, non mi viene ancora per bene il lancio della monetina :d
sinceramente non mi interessano molto ora gli algoritmi di sicurezza, più che altro, non mi viene ancora per bene il lancio della monetina :d
Continuo a non capire qual'è il problema :confused:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
srand(time(0));
int numero_casuale = rand() % 2;
// numero_casuale vale 1 oppure 0
if(numero_casuale == 0)
printf("TESTA\n");
else
printf("CROCE\n");
system("PAUSE");
return 0;
}
A me sembra sufficiente una cosa simile... Lanci imprevedibili ad ogni esecuzione! Forse preferisci il dollaro anzichè l'euro come moneta da lanciare? :D
Gimli[2BV!2B]
15-01-2012, 17:20
Se ripeti l'esecuzione velocemente viene sempre lo stesso risultato per un secondo, a causa di time che da un risultato in secondi: ipotizzo possa essere questo il problema...
Se si vogliono fare più estrazioni si devono fare in un ciclo dopo l'inizializzazione del seme random.
Resta che se si rilancia il programma nello stesso secondo si otterranno gli stessi risultati, ma direi che è una pignoleria un po' esagerata per questo caso.
Per completezza riporto anche un veloce esempio di aumento precisione temporale del seme iniziale (GNU/Linux \ BSD):#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
srand(ts.tv_nsec); //Usando solo i nanosecondi della struttura è possibile
//che si reinizializzi il seme allo stesso valore
int numero_casuale = rand() % 2;
// numero_casuale vale 1 oppure 0
if(numero_casuale == 0)
printf("TESTA\n");
else
printf("CROCE\n");
return 0;
}
tecno789
15-01-2012, 17:26
ma non si può fare un algoritmo che non dipenda dal tempo??
Gimli[2BV!2B]
15-01-2012, 18:18
Certo, basta trovare un'altro valore casuale che sia sempre diverso... ok, a quel punto non serve più rand.
Oppure un'altro valore che sia costantemente crescente; se non interessa la sicurezza potrebbe essere il numero assoluto di esecuzioni di quel programma (che occorrerebbe salvare da qualche parte).
Il tempo ha la caratteristica di essere un valore monotono crescente, è quindi perfetto per inizializzare srand a valori sempre diversi, con due limiti:
precisione di questa misurazione con ripetizione del seme in esecuzioni ravvicinate
overflow del seme se troppo precisa, con ripetizioni periodiche della sequenza dei semi; questo potrebbe creare problemi importanti solo se anche le esecuzioni dovessero avvenire con pattern temporali che si ripetono (usando solo i nanosecondi è più probabile, si dovrebbero prendere un po' di secondi e scalare ai microsecondi o ai millisecondi, ecc...)
Qui arrivano le mie conoscenze in materia, non escludo che mi sfugga qualcosa non essendomi mai interessato molto all'argomento.
Anni fa lessi un libro in cui si parlava del cosiddetto "pool di entropia", ovvero di un pozzo che viene riempito dal SO (Linux in quel caso) con dati casuali provenienti da sorgenti di input (tastiera, mouse, interrupt)... Praticamente puoi attingere da questo pozzo per prelevare i bytes che ti servono. Non ho mai usato una cosa del genere ma se vuoi puoi approfondire cerca un pò in rete, partendo da wiki:
http://it.wikipedia.org/wiki//dev/random
Anni fa lessi un libro in cui si parlava del cosiddetto "pool di entropia", ovvero di un pozzo che viene riempito dal SO (Linux in quel caso) con dati casuali provenienti da sorgenti di input (tastiera, mouse, interrupt)... Praticamente puoi attingere da questo pozzo per prelevare i bytes che ti servono. Non ho mai usato una cosa del genere ma se vuoi puoi approfondire cerca un pò in rete, partendo da wiki:
http://it.wikipedia.org/wiki//dev/random
è esattamente il /dev/random di cui sopra, vi si può attingere usando fread, ad esempio:
void seed_random(){
/* Lettura di un seed dal device random */
FILE* seme = fopen("/dev/random","r");
if(seme == NULL)
errore("Errore nella lettura del device urandom\n",1);
if(fread(&seed,3,1,seme) != 1)
errore("Errore nell'inizializzazione del seed\n",2);
fclose(seme);
}
dove seed è la variabile da inizializzare con il numero casuale
oltre a /dev/random c'è anche /dev/urandom, che a differenza del primo non si esaurisce mai, ma come c'è scritto nel man:
A read from the /dev/urandom device will not block waiting for more entropy. As a result, if there is not sufficient entropy in the entropy pool, the returned values are theoretically vulnerable to a cryptographic attack on the algorithms used by the driver.
Gimli[2BV!2B]
15-01-2012, 23:01
Vero, i /dev/?(u)random, me n'ero proprio dimenticato.
Tra l'altro non sapevo esistessero simili anche nel mondo Win.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.