repetir - ¿Cómo generar un int aleatorio en C?
randomize en c (24)
¿Hay una función para generar un número int aleatorio en C? ¿O tendré que usar una biblioteca de terceros?
Nota : No use
rand()
por seguridad. Si necesita un número seguro criptográficamente, vea esta respuesta en su lugar.
#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.
Edit : Es posible que prefieras usar aleatorio y srandom .
Si necesita caracteres aleatorios o enteros seguros:
Como se explica en cómo generar con seguridad números aleatorios en varios lenguajes de programación , querrá hacer una de las siguientes acciones:
- Usa la API de
randombytes
- Reimplementar 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.
A pesar de todas las personas que sugieren rand()
aquí, ¡no querrás usar rand()
menos que tengas que hacerlo! Los números aleatorios que produce rand()
son a menudo muy malos. Para citar desde la página del manual de Linux:
Las versiones de
rand()
ysrand()
en la biblioteca de Linux C utilizan el mismo generador de números aleatorios querandom(3)
ysrandom(3)
, por lo que los bits de orden inferior deben ser tan aleatorios como los bits de orden superior. Sin embargo, en las implementaciones antiguas de rand () y en las implementaciones actuales en sistemas diferentes, los bits de orden inferior son mucho menos aleatorios que los bits de orden superior . No utilice esta función en aplicaciones destinadas a ser portátiles cuando se necesita una buena aleatoriedad. ( Use alrandom(3)
lugar. )
Con respecto a la portabilidad, random()
también está definido por el estándar POSIX desde hace bastante tiempo. rand()
es más antiguo, ya apareció 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 POSIX.1-2008 (IEEE Std 1003.1-2008), que recibió una actualización hace apenas un año (IEEE Std 1003.1-2008, Edición 2016). Así que consideraría que random()
es muy portátil.
POSIX.1-2001 también introdujo las lrand48()
y mrand48()
, vea 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 pseudoaleatoria 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 puede encontrarlo en sistemas como Linux y macOS / iOS.
Al escuchar una buena explicación de por qué el uso de rand()
para producir números aleatorios distribuidos uniformemente en un rango determinado es una mala idea, decidí echar un vistazo a qué tan sesgada es la salida en realidad. Mi caso de prueba fue el lanzamiento justo de dados. Aquí está el código 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);
}
Y aquí está su salida:
$ 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 uniformes necesitas que sean tus números aleatorios, pero lo anterior parece lo suficientemente uniforme para la mayoría de las necesidades.
Edición: sería una buena idea inicializar el PRNG con algo mejor que el time(NULL)
.
Bueno, STL es C ++, no C, así que no sé lo que quieres. Si quieres C, sin embargo, hay las funciones rand()
y srand()
:
int rand(void);
void srand(unsigned seed);
Ambos son parte de ANSI C. También existe la función random()
:
long random(void);
Pero por lo que puedo decir, random()
no es un ANSI estándar C. Una biblioteca de terceros puede no ser una mala idea, pero todo depende de qué tan aleatorio sea el número que realmente necesite generar.
Eche un vistazo a ISAAC (Indirection, Shift, Accumulate, Add, and Count). Se distribuye uniformemente y tiene una longitud promedio de ciclo de 2 ^ 8295.
En las modernas CPU x86_64 puede usar el generador de números aleatorios de hardware a través de _rdrand64_step()
Código de ejemplo:
#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
Esperemos que esto sea un poco más aleatorio que solo usar 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());
}
Esta es una buena manera de obtener un número aleatorio entre dos números de su elección.
#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));
}
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.
FWIW, la respuesta es que sí, hay una función stdlib.h
llamada rand
; esta función se ajusta principalmente para la velocidad y la distribución, no para la imprevisibilidad. Casi todas las funciones aleatorias incorporadas para varios lenguajes 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. Estos deben ser utilizados en cualquier tipo de aplicación relacionada con la seguridad.
Intente esto, lo junté 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;
}
La función rand()
en <stdlib.h>
devuelve un entero pseudo-aleatorio entre 0 y RAND_MAX
. Puedes 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 hay que tener en cuenta que esto altera 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;
}
}
La función estándar de C es rand()
. Es lo suficientemente bueno como para repartir cartas por solitario, pero es horrible. Muchas implementaciones de rand()
recorren una breve lista de números, y los bits bajos tienen ciclos más cortos. La forma en que algunos programas llaman rand()
es horrible, y calcular una buena semilla para pasar a srand()
es difícil.
La mejor manera de generar números aleatorios en C es usar una biblioteca de terceros como OpenSSL. Por ejemplo,
#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;
}
¿Por qué tanto código? Otros lenguajes como Java y Ruby tienen funciones para enteros o flotantes aleatorios. OpenSSL solo da bytes aleatorios, así que trato de imitar cómo Java o Ruby los transformaría en enteros o flotantes.
Para los enteros, queremos evitar el sesgo de módulo . Supongamos que obtuvimos 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 del 0 al 2767 aparecería con más frecuencia que el 2768 al 9999. Para eliminar el sesgo, podemos reintentar rand()
mientras el valor está por debajo de 2768, porque los valores 30000 de 2768 a 32767 se asignan uniformemente a los 10000 valores de 0 a 9999.
Para los flotantes, queremos 53 bits aleatorios, porque un double
contiene 53 bits de precisión (suponiendo que sea un doble IEEE). Si usamos más de 53 bits, obtenemos un sesgo de redondeo. Algunos programadores 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 siembra, quizás 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''s Perl_seed () es un ejemplo de cómo calcular una semilla en C para
srand()
. Mezcla bits de la hora actual, la ID del proceso y algunos punteros, si no puede leer/dev/urandom
. - El arc4random_uniform de OpenBSD () explica el sesgo de módulo.
- La API de Java para java.util.Random describe algoritmos para eliminar sesgos de enteros aleatorios y empaquetar 53 bits en flotantes aleatorios.
Mi solución minimalista debería funcionar para números aleatorios dentro del 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));
}
Quieres usar rand()
. Nota ( MUY IMPORTANTE ): asegúrese de establecer la semilla para la función de rand. Si no lo hace, sus números aleatorios no son realmente aleatorios . Esto es muy, muy, muy importante. Afortunadamente, usualmente puedes usar una combinación del temporizador de tics del sistema y la fecha para obtener una buena semilla.
STL no existe para C. Tienes que llamar a rand
, o mejor aún, al random
. Estos se declaran en el encabezado de la biblioteca estándar stdlib.h
. rand
es POSIX, 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 bit. 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.
Si necesita números pseudoaleatorios de mejor calidad que los que proporciona stdlib
, consulte Mersenne Twister . También es más rápido. Las implementaciones de muestra son abundantes, por ejemplo here .
Si su sistema admite la familia de funciones arc4random
, recomendaría arc4random
lugar de la función estándar rand
.
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 sin signo de 32 bits aleatorio.
arc4random_buf
pone contenido aleatorio en su parámetro buf : void *
. La cantidad de contenido está determinada por el parámetro bytes : size_t
.
arc4random_uniform
devuelve un entero sin signo de 32 bits aleatorio que sigue la regla: 0 <= arc4random_uniform(limit) < limit
, donde límite también es un entero de 32 bits sin signo.
arc4random_stir
lee datos de /dev/urandom
y pasa los datos a arc4random_addrandom
para aleatorizar adicionalmente su grupo de números aleatorios internos.
arc4random_addrandom
es utilizado por arc4random_stir
para completar su grupo interno de números aleatorios de acuerdo con los datos que se le han enviado.
Si no tiene estas funciones, pero está en Unix, puede usar este código:
/* 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 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 llamada a rand
, excepto que es más segura, y devuelve un valor long
(fácilmente modificable).
Sin embargo, /dev/urandom
puede ser un poco lento, por lo que se recomienda que lo uses 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 de acceso para open
en urandom_init
. Las llamadas y las API utilizadas en urandom_init
y urandom
son (creo) compatibles con POSIX, y como tales, deberían funcionar en la mayoría, si no en todos los sistemas compatibles con POSIX.
Notas: Una lectura de /dev/urandom
NO se bloqueará si no hay suficiente entropía disponible, por lo que los valores generados en tales circunstancias pueden ser criptográficamente inseguros. Si está preocupado por eso, entonces use /dev/random
, que siempre se bloqueará si no hay entropía suficiente.
Si está en otro sistema (es decir, Windows), use rand
o alguna API interna no portátil dependiente de la plataforma de Windows.
Función de arc4random
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;
}
Tuve un problema grave con el generador de números pseudoaleatorios en mi aplicación reciente: repetidamente llamé a mi programa C a través de un script pyhton y estaba utilizando como semilla el siguiente código:
srand(time(NULL))
Sin embargo, desde:
- rand generará la misma secuencia pseudoaleatoria y dará la misma semilla en srand (ver
man srand
); - Como ya se dijo, la función de tiempo 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 programa generó la misma secuencia de números. Puedes hacer 3 cosas para resolver este problema:
mezcla el tiempo de salida con otra información que cambia en las ejecuciones (en mi aplicación, el nombre de salida):
srand(time(NULL) | getHashOfString(outputName))
Utilicé djb2 como mi función hash.
Aumente la resolución del tiempo. En mi plataforma,
clock_gettime
estaba disponible, así que lo uso:#include<time.h> struct timespec nanos; clock_gettime(CLOCK_MONOTONIC, &nanos) srand(nanos.tv_nsec);
Use ambos métodos juntos:
#include<time.h> struct timespec nanos; clock_gettime(CLOCK_MONOTONIC, &nanos) srand(nanos.tv_nsec | getHashOfString(outputName));
La opción 3 le garantiza (hasta donde sé) la mejor aleatoriedad de semillas, pero puede crear una diferencia solo en una aplicación muy rápida. En mi opinión, la opción 2 es una apuesta segura.
Vamos a pasar por esto. Primero usamos la función srand () para sembrar el aleatorizador. Básicamente, la computadora puede generar números aleatorios basados en el número que se alimenta a srand (). Si da el mismo valor de semilla, se generarán los mismos números aleatorios cada vez.
Por lo tanto, tenemos que sembrar el aleatorizador con un valor que siempre está cambiando. Hacemos esto dándole el valor del tiempo actual con la función time ().
Ahora, cuando llamamos a rand (), se producirá un nuevo número aleatorio cada vez.
#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;
}
rand()
es la forma más conveniente de generar números aleatorios.
También puede obtener un número aleatorio de cualquier servicio en línea como random.org.
Programa C para generar un número aleatorio entre 9 y 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);
}
En general, podemos generar un número aleatorio entre lowerLimit y upperLimit-1
es decir, lowerLimit es inclusivo o diga r ∈ [lowerLimit, upperLimit)
#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;
}
#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]);
}