Cómo generar un int aleatorio en C?

¿Hay una función para generar un número entero aleatorio en C? ¿O tendré que usar una biblioteca de terceros?

Nota : No use rand() para seguridad. Si necesita un número criptográficamente seguro, consulte esta respuesta .

 #include  #include  srand(time(NULL)); // should only be called once int r = rand(); // returns a pseudo-random integer between 0 and RAND_MAX 

La función rand() en devuelve un entero pseudoaleatorio entre 0 y RAND_MAX . Puede usar srand(unsigned int seed) para establecer una semilla.

Es una práctica común utilizar el operador % junto con rand() para obtener un rango diferente (aunque tenga en cuenta que esto elimina un poco la uniformidad). Por ejemplo:

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

Si realmente te importa la uniformidad, puedes hacer algo como esto:

 /* 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; } } 

Si necesita caracteres aleatorios seguros o enteros:

Como se trata en cómo generar números aleatorios de manera segura en varios lenguajes de progtwigción , querrá hacer una de las siguientes cosas:

  • Utilice la API de randombytes
  • Vuelva a implementar lo que necesita de la implementación de sysrandom de libsodium usted mismo, con mucho cuidado
  • En términos más generales, use /dev/urandom , no /dev/random . No OpenSSL (u otros PRNG de espacio de usuario).

Por ejemplo:

 #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() es criptográficamente seguro e imparcial.

Si necesita números pseudoaleatorios de mejor calidad que los que ofrece stdlib , consulte Mersenne Twister . Es más rápido, también. Las implementaciones de muestra son abundantes, por ejemplo aquí .

Vamos a pasar por esto. Primero usamos la función srand () para inicializar el aleatorizador. Básicamente, la computadora puede generar números aleatorios basados ​​en el número que se alimenta a srand (). Si diera el mismo valor de inicialización, entonces se generarían los mismos números aleatorios cada vez.

Por lo tanto, debemos sembrar el aleatorizador con un valor que siempre esté cambiando. Hacemos esto al alimentarlo con el valor de la hora actual con la función de tiempo ().

Ahora, cuando llamemos a rand (), se producirá un nuevo número aleatorio cada vez.

 #include  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; } 

La función C estándar es rand() . Es lo suficientemente bueno para repartir cartas por solitario, pero es horrible. Muchas implementaciones de rand() pasan por una breve lista de números, y los bits bajos tienen ciclos más cortos. La forma en que algunos progtwigs llaman rand() es horrible, y calcular una buena semilla para pasar a srand() es difícil.

La mejor forma de generar números aleatorios en C es usar una biblioteca de terceros como OpenSSL. Por ejemplo,

 #include  #include  #include  #include  /* 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(uc, sizeof(uc))) { fprintf(stderr, "Can't get random bytes!\n"); exit(1); } } while (ui < (-limit % limit)); /* ui < (2**size % limit) */ return ui % 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(uc, sizeof(uc))) { fprintf(stderr, "Can't get random bytes!\n"); exit(1); } /* 53 bits / 2**53 */ return (ui >> 11) * (1.0/9007199254740992.0); } int main() { printf("Dice: %d\n", (int)(random_uint(6) + 1)); printf("Double: %f\n", random_double()); return 0; } 

¿Por qué tanto código? Otros lenguajes como Java y Ruby tienen funciones para enteros aleatorios o flotantes. OpenSSL solo proporciona bytes aleatorios, por lo que trato de imitar cómo Java o Ruby los transformarían en enteros o flotantes.

Para enteros, queremos evitar el sesgo de módulo . Supongamos que tenemos algunos enteros aleatorios de 4 dígitos de rand() % 10000 , pero rand() solo puede devolver 0 a 32767 (como lo hace en Microsoft Windows). Cada número de 0 a 2767 aparecería más a menudo que cada número de 2768 a 9999. Para eliminar el sesgo, podemos reintentar rand() mientras el valor está por debajo de 2768, porque los 30000 valores de 2768 a 32767 se correlacionan uniformemente con los 10000 valores de 0 a 9999.

Para los flotadores, queremos 53 bits aleatorios, porque un double tiene 53 bits de precisión (suponiendo que es un doble IEEE). Si utilizamos más de 53 bits, obtenemos un sesgo de redondeo. Algunos progtwigdores escriben código como rand() / (double)RAND_MAX , pero rand() puede devolver solo 31 bits, o solo 15 bits en Windows.

RAND_bytes() OpenSSL se RAND_bytes() , tal vez leyendo /dev/urandom en Linux. Si necesitamos muchos números aleatorios, sería demasiado lento leerlos todos desde /dev/urandom , ya que deben copiarse desde el kernel. Es más rápido permitir que OpenSSL genere más números aleatorios a partir de una semilla.

Más sobre números aleatorios:

  • Perl_seed de Perl () es un ejemplo de cómo calcular una semilla en C para srand() . Mezcla bits de la hora actual, el ID del proceso y algunos punteros, si no puede leer /dev/urandom .
  • El arc4random_uniform () de OpenBSD explica el sesgo del módulo.
  • La API de Java para java.util.Random describe algoritmos para eliminar el sesgo de enteros aleatorios y empaquetar 53 bits en flotantes aleatorios.

Si su sistema es compatible con la familia de funciones arc4random , le recomendaría arc4random lugar de la función rand estándar.

La familia arc4random incluye:

 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 devuelve un entero aleatorio de 32 bits sin signo.

arc4random_buf pone contenido aleatorio en su parámetro buf : void * . La cantidad de contenido está determinada por los bytes : size_t parámetro bytes : size_t .

arc4random_uniform devuelve un entero aleatorio de 32 bits sin signo que sigue la regla: 0 <= arc4random_uniform(limit) < limit , donde limit es también un entero sin signo de 32 bits.

arc4random_stir lee datos de /dev/urandom y pasa los datos a arc4random_addrandom para aleatorizar adicionalmente su grupo interno de números aleatorios.

arc4random_addrandom es utilizado por arc4random_stir para poblar su grupo de números aleatorios internos de acuerdo con los datos que se le pasan.

Si no tiene estas funciones, pero está en Unix, puede usar este código:

 /* This is C, not C++ */ #include  #include  #include  #include  #include  #include  /* exit */ #include  /* 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 función urandom_init abre el dispositivo /dev/urandom y coloca el descriptor de archivo en urandom_fd .

La función urandom es básicamente la misma que una invocación a rand , excepto que es más segura y devuelve una respuesta long (fácilmente modificable).

Sin embargo, /dev/urandom puede ser un poco lento, por lo que se recomienda utilizarlo como semilla para un generador de números aleatorios diferente.

Si su sistema no tiene un /dev/urandom , pero tiene un archivo /dev/random o similar, entonces simplemente puede cambiar la ruta pasada para open en urandom_init . Las llamadas y API usadas en urandom_init y urandom son (creo) que cumplen con POSIX, y como tal, deberían funcionar en la mayoría, si no en todos los sistemas que cumplen con POSIX.

Notas: Una lectura de /dev/urandom NO se bloqueará si no hay suficiente entropía disponible, por lo que los valores generados bajo tales circunstancias pueden ser criptográficamente inseguros. Si le preocupa eso, use /dev/random , que siempre bloqueará si no hay suficiente entropía.

Si se encuentra en otro sistema (es decir, Windows), utilice rand o alguna API interna no portátil dependiente de la plataforma específica de Windows.

Función Wrapper para llamadas urandom , rand o arc4random :

 #define RAND_IMPL /* urandom(see large code block) | rand | arc4random */ int myRandom(int bottom, int top){ return (RAND_IMPL() % (top - bottom)) + bottom; } 

STL no existe para C. Tienes que llamar rand , o mejor aún, random . Estos se declaran en el encabezado de la biblioteca estándar stdlib.h . rand es POSIX, al random es una función de especificación BSD.

La diferencia entre rand y random es que random devuelve un número aleatorio de 32 bits mucho más utilizable, y rand típicamente devuelve un número de 16 bits. Las páginas de manual de BSD muestran que los bits más bajos de rand son cíclicos y predecibles, por lo que rand es potencialmente inútil para números pequeños.

Eche un vistazo a ISAAC (Indirection, Shift, Acumulate, Add y Count). Está distribuido uniformemente y tiene una longitud de ciclo promedio de 2 ^ 8295.

Desea usar rand() . Nota ( MUY IMPORTANTE ): asegúrese de establecer la semilla para la función rand. Si no lo haces, tus números aleatorios no son realmente aleatorios . Esto es muy, muy, muy importante. Afortunadamente, generalmente puede usar alguna combinación del temporizador de ticks del sistema y la fecha para obtener una buena semilla.

FWIW, la respuesta es que sí, hay una función stdlib.h llamada rand ; esta función está ajustada principalmente para velocidad y distribución, no para imprevisibilidad. Casi todas las funciones aleatorias integradas para varios idiomas y marcos usan esta función por defecto. También hay generadores de números aleatorios “criptográficos” que son mucho menos predecibles, pero funcionan mucho más lentamente. Deben usarse en cualquier tipo de aplicación relacionada con la seguridad.

Es de esperar que sea un poco más aleatorio que simplemente usar srand(time(NULL)) .

 #include  #include  #include  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()); } 

Bueno, STL es C ++, no C, así que no sé lo que quieres. Sin embargo, si desea C, existen las funciones rand() y srand() :

 int rand(void); void srand(unsigned seed); 

Ambos son parte de ANSI C. También está la función random() :

 long random(void); 

Pero hasta donde puedo decir, random() no es estándar ANSI C. Una biblioteca de terceros puede no ser una mala idea, pero todo depende de qué tan aleatorio de un número realmente necesite generar.

Esta es una buena manera de obtener un número aleatorio entre dos números de su elección.

 #include  #include  #include  #define randnum(min, max) \ ((rand() % (int)(((max) + 1) - (min))) + (min)) int main() { srand(time(NULL)); printf("%d\n", randnum(1, 70)); } 

Salida la primera vez: 39

Salida la segunda vez: 61

Salida la tercera vez: 65

Puede cambiar los valores después de randnum a cualquier número que elija, y generará un número aleatorio para usted entre esos dos números.

C Progtwig para generar un número aleatorio entre 9 y 50

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

En general, podemos generar un número aleatorio entre lowerLimit y upperLimit-1

es decir, lowerLimit es inclusivo o, por ejemplo, r ∈ [lowerLimit, upperLimit)

rand() es la forma más conveniente de generar números aleatorios.

También puede capturar números aleatorios de cualquier servicio en línea como random.org.

 #include  #include  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]); } 
 #include  #include  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; } 

En las CPU modernas x86_64 puede usar el generador de números aleatorios de hardware a través de _rdrand64_step()

Código de ejemplo:

 #include  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 

Al escuchar una buena explicación de por qué usar rand() para producir números aleatorios distribuidos uniformemente en un rango dado es una mala idea, decidí echar un vistazo a qué tan sesgada está realmente la salida. Mi caso de prueba fue tirar justo los dados. Aquí está el código C:

 #include  #include  #include  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); } 

y aquí está su resultado:

  $ 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% 

No sé qué tan uniforme necesita que sean sus números aleatorios, pero lo anterior parece lo suficientemente uniforme para la mayoría de las necesidades.

Editar: sería una buena idea inicializar el PRNG con algo mejor que el time(NULL) .

Tuve un problema serio con el generador de números pseudoaleatorios en mi aplicación reciente: repetidamente llamé a mi progtwig C a través de un script pyhton y estaba usando como semilla el siguiente código:

 srand(time(NULL)) 

Sin embargo, desde:

  • rand generará la misma secuencia pseudo aleatoria dará la misma semilla en srand (ver man srand );
  • Como ya se mencionó, la función de hora cambia solo de segundo a segundo: si su aplicación se ejecuta varias veces en el mismo segundo, el time devolverá el mismo valor cada vez.

Mi progtwig generó la misma secuencia de números. Puedes hacer 3 cosas para resolver este problema:

  1. tiempo de mezcla de salida con alguna otra información cambiando en ejecuciones (en mi aplicación, el nombre de salida):

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

    Utilicé djb2 como mi función hash.

  2. Aumenta la resolución del tiempo. En mi plataforma, clock_gettime estaba disponible, así que lo uso:

     #include struct timespec nanos; clock_gettime(CLOCK_MONOTONIC, &nanos) srand(nanos.tv_nsec); 
  3. Usa ambos métodos juntos:

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

La opción 3 le garantiza (hasta donde yo sé) la mejor aleatoriedad de semillas, pero puede crear una diferencia solo en aplicaciones muy rápidas. En mi opinión, la opción 2 es una apuesta segura.

A pesar de toda la sugerencia de gente rand() aquí, no querrás usar rand() menos que tengas que hacerlo. Los números aleatorios que rand() produce a menudo son muy malos. Para citar de la página man de Linux:

Las versiones de rand() y srand() en la biblioteca Linux C utilizan el mismo generador de números aleatorios que random(3) y srandom(3) , por lo que los bits de orden inferior deben ser tan aleatorios como los bits de orden superior. Sin embargo, en implementaciones anteriores de rand () y en implementaciones actuales en diferentes sistemas, los bits de orden inferior son mucho menos aleatorios que los bits de orden superior . No use esta función en aplicaciones destinadas a ser portátiles cuando se necesita buena aleatoriedad. ( Use al random(3) lugar ) .

Con respecto a la portabilidad, random() también está definido por el estándar POSIX desde hace bastante tiempo. rand() es anterior, aparecía ya en la primera especificación POSIX.1 (IEEE Std 1003.1-1988), mientras que random() apareció por primera vez en POSIX.1-2001 (IEEE Std 1003.1-2001), sin embargo, el estándar POSIX actual es ya en POSIX.1-2008 (IEEE Std 1003.1-2008), que recibió una actualización hace solo un año (IEEE Std 1003.1-2008, edición de 2016). Así que consideraría que random() es muy portable.

POSIX.1-2001 también introdujo las lrand48() y mrand48() , ver aquí :

Esta familia de funciones generará números pseudoaleatorios utilizando un algoritmo congruente lineal y una aritmética de enteros de 48 bits.

Y una buena fuente pseudo aleatoria es la función arc4random() que está disponible en muchos sistemas. No forma parte de ningún estándar oficial, apareció en BSD alrededor de 1997, pero puedes encontrarlo en sistemas como Linux y macOS / iOS.

Mi solución minimalista debería funcionar para números aleatorios en el rango [min, max) . Use srand(time(NULL)) antes de invocar la función.

 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)); } 

Intente esto, lo puse juntos a partir de algunos de los conceptos ya mencionados anteriormente:

 /* 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; }