¿Hay una función para generar un número int aleatorio en C? ¿O tendré que usar una biblioteca de terceros?
srand
: por qué llamarlo solo una vez .
¿Hay una función para generar un número int aleatorio en C? ¿O tendré que usar una biblioteca de terceros?
srand
: por qué llamarlo solo una vez .
Respuestas:
Nota : No lo use
rand()
para seguridad. Si necesita un número criptográficamente seguro, consulte esta respuesta .
#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.
Editar : en Linux, es posible que prefiera usar aleatorio y srandom .
time()
solo cambia una vez por segundo. Si inicia desde time()
, para cada llamada a rand()
, obtendrá el mismo valor para cada llamada durante un solo segundo. Pero la razón más importante es que las propiedades rand()
y funciones como esta se conocen mejor para el caso de uso donde se siembran exactamente una vez por ejecución, y no en cada llamada. Dependiendo de la "aleatoriedad" con propiedades no comprobadas o no probadas conduce a problemas.
rand()
generalmente es) sembrar con rand()
, en el mejor de los casos, no tendría ningún efecto, y en el peor de los casos rompería las cualidades conocidas del generador. Este es un tema profundo. Comience leyendo el Capítulo 3 de Knuth Vol. 2 sobre números aleatorios como la mejor introducción a las matemáticas y dificultades.
srand((unsigned int)time(NULL));
La rand()
función en <stdlib.h>
devuelve un entero pseudoaleatorio entre 0 y RAND_MAX
. Puedes usar srand(unsigned int seed)
para establecer una semilla.
Es una práctica común usar el %
operador en conjunto con rand()
para obtener un rango diferente (aunque tenga en cuenta que esto arroja algo de 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;
}
}
%
es el operador de módulo. Le da el resto de una división entera, por x % n
lo que siempre le dará un número entre 0
y n - 1
(siempre x
y cuando n
sean positivos). Si todavía lo encuentra confuso, intente escribir un programa que i
cuente de 0 a 100, e imprima i % n
para algunos n
de su elección más pequeños que 100.
Como se explica en cómo generar de forma segura números aleatorios en varios lenguajes de programación , querrá hacer uno de los siguientes:
randombytes
/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.
sodium_init()
en algún momento. No te preocupes por el RNG, usa el núcleo.
sodium_init()
aunque no sea necesariamente parte de mi ejemplo porque es un detalle importante.
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 proporcionó el mismo valor inicial, se generarían los mismos números aleatorios cada vez.
Por lo tanto, tenemos que sembrar el aleatorizador con un valor que siempre esté cambiando. Hacemos esto al darle 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;
}
else{ low_num=max_num; hi_num=min_num+1;
2) falla cuando hi_num - low_num > INT_MAX
. 3) Omite valores en la situación rara INT_MAX > hi_num - low_num > RAND_MAX
.
hi_num = max_num + 1;
carece de protección contra el desbordamiento.
Si necesita números pseudoaleatorios de mejor calidad que los que stdlib
ofrece, consulte Mersenne Twister . También es más rápido. Las implementaciones de muestra son abundantes, por ejemplo aquí .
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()
ciclo a través de una lista corta 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 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 aleatorios o flotantes. 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 tenemos algunos enteros aleatorios de 4 dígitos rand() % 10000
, pero rand()
solo podemos devolver 0 a 32767 (como lo hace en Microsoft Windows). Cada número del 0 al 2767 aparecería con mayor frecuencia que cada número del 2768 al 9999. Para eliminar el sesgo, podemos volver a intentar rand()
mientras el valor esté por debajo de 2768, porque los valores de 30000 de 2768 a 32767 corresponden de manera uniforme a los valores de 10000 de 0 a 9999.
Para 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 sesgo de redondeo. Algunos programadores escriben código como rand() / (double)RAND_MAX
, pero rand()
pueden devolver solo 31 bits, o solo 15 bits en Windows.
OpenSSL se inicia RAND_bytes()
, tal vez leyendo /dev/urandom
en Linux. Si necesitamos muchos números aleatorios, sería demasiado lento leerlos todos /dev/urandom
, ya que deben copiarse del núcleo. 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:
srand()
. Mezcla bits de la hora actual, la ID del proceso y algunos punteros, si no puede leer /dev/urandom
.float
/ double
, por lo que he aclarado la pregunta para mantener los int
números para evitar que sea demasiado amplia. Hay otras preguntas que tratan específicamente de C float
/ double
valores aleatorios, por lo que puede volver a publicar su segunda mitad de su respuesta a preguntas tales como stackoverflow.com/questions/13408990/...
Si su sistema admite la arc4random
familia de funciones, recomendaría usarlas en lugar de la rand
función estándar .
La arc4random
familia 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 sin signo de 32 bits.
arc4random_buf
pone contenido aleatorio en su parámetro buf : void *
. La cantidad de contenido está determinada por el bytes : size_t
parámetro.
arc4random_uniform
devuelve un entero aleatorio sin signo de 32 bits que sigue la regla:, 0 <= arc4random_uniform(limit) < limit
donde limit también es un entero sin signo de 32 bits.
arc4random_stir
lee datos /dev/urandom
y los transfiere para arc4random_addrandom
aleatorizar adicionalmente su grupo interno de números aleatorios.
arc4random_addrandom
es utilizado por arc4random_stir
para llenar su grupo interno de números aleatorios 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 <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_init
función abre el /dev/urandom
dispositivo y coloca el descriptor de archivo urandom_fd
.
La urandom
función es básicamente la misma que una llamada a rand
, excepto que es más segura y devuelve un long
(fácilmente modificable).
Sin embargo, /dev/urandom
puede ser un poco lento, por lo que se recomienda que lo use como semilla para un generador de números aleatorios diferente.
Si el sistema no tiene una /dev/urandom
, pero no tienen un /dev/random
archivo o similar, entonces sólo tiene que cambiar la ruta pasó a open
en urandom_init
. Las llamadas y las API utilizadas en urandom_init
y urandom
son (creo) compatibles con POSIX, y como tal, 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, use /dev/random
, que siempre se bloqueará si no hay suficiente entropía.
Si está en otro sistema (es decir, Windows), utilice rand
o alguna API interna no portátil dependiente de la plataforma específica de Windows.
Envoltorio para la función urandom
, rand
o arc4random
las llamadas:
#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. Usted tiene que llamar rand
, o mejor aún, 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
generalmente devuelve un número de 16 bits. Las páginas de manual de BSD muestran que los bits más bajos rand
son cíclicos y predecibles, por lo que rand
es potencialmente inútil para números pequeños.
extern int rand(void);
y extern void srand(unsigned int);
.
Eche un vistazo a ISAAC (Indirección, Cambio, Acumular, Agregar y Contar). Está distribuido uniformemente y tiene una duración promedio del ciclo de 2 ^ 8295.
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 randnum
a los números que elija, y generará un número aleatorio para usted entre esos dos números.
Que desea utilizar rand()
. Nota ( MUY IMPORTANTE ): asegúrese de establecer la semilla para la función rand. Si no lo hace, sus números aleatorios no son realmente aleatorios . Esto es muy, muy, muy importante. Afortunadamente, generalmente puede usar alguna combinación del temporizador de tic del sistema y la fecha para obtener una buena semilla.
FWIW, la respuesta es que sí, hay una stdlib.h
función 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 de manera predeterminada. 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.
Con suerte, esto es 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());
}
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 existe la random()
función:
long random(void);
Pero, por lo que puedo decir, random()
no es ANSI C. estándar. Una biblioteca de terceros puede no ser una mala idea, pero todo depende de qué tan aleatorio de un número realmente necesite generar.
C Programa para generar números aleatorios 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 dice r ∈ [lowerLimit, upperLimit)
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.
#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]);
}
#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;
}
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 <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
#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;
}
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 lo sesgada que está 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 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 time(NULL)
.
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 usando como semilla el siguiente código:
srand(time(NULL))
Sin embargo, desde:
man srand
);time
devolverá el mismo valor cada vez.Mi programa generó la misma secuencia de números. Puede hacer 3 cosas para resolver este problema:
mezclar la salida del tiempo con alguna otra información que cambia en las ejecuciones (en mi aplicación, el nombre de la salida):
srand(time(NULL) | getHashOfString(outputName))
Solía djb2 como mi función hash.
Aumenta 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 yo sé) la mejor aleatoriedad inicial, pero puede crear una diferencia solo en aplicaciones muy rápidas. En mi opinión, la opción 2 es una apuesta segura.
rand()
No debería usarse para datos criptográficos, estoy de acuerdo. Al menos para mí, mi aplicación no incluía datos criptográficos, por lo que para mí estaba bien el método dado.
A pesar de todas las sugerencias de personas rand()
aquí, ¡no querrás usar a rand()
menos que tengas que hacerlo! Los números aleatorios que rand()
produce a menudo son muy malos. Para citar de la página de manual de Linux:
Las versiones de
rand()
ysrand()
en la Biblioteca Linux C usan 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 implementaciones antiguas 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 utilice esta función en aplicaciones destinadas a ser portátiles cuando se necesita una buena aleatoriedad. ( Use en surandom(3)
lugar )
En cuanto a la portabilidad, random()
también está definida por el estándar POSIX desde hace bastante tiempo. rand()
es anterior, 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), pero el estándar POSIX actual ya es 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). Por lo tanto, consideraría random()
que es muy portátil.
POSIX.1-2001 también introdujo las funciones lrand48()
y mrand48()
, ver aquí :
Esta familia de funciones generará números pseudoaleatorios utilizando un algoritmo lineal congruencial y una aritmética de enteros de 48 bits.
Y una fuente pseudoaleatoria bastante buena es la arc4random()
función 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.
random()
no existe en Windows
rand()
ya que también lo requiere el estándar C. Para cualquier otra cosa, necesita una solución especial solo para Windows, como de costumbre. #ifdef _WIN32
es la frase que verá con mayor frecuencia en el código multiplataforma que desea admitir Windows y, por lo general, hay una solución que funciona con todos los sistemas y una que solo se requiere para Windows.
Para aplicaciones Linux C:
Este es mi código modificado de una respuesta anterior que sigue mis prácticas de código C y devuelve un búfer aleatorio de cualquier tamaño (con los códigos de retorno adecuados, etc.). Asegúrese de llamar urandom_open()
una vez al comienzo de su programa.
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;
}
}
Mi solución minimalista debería funcionar para números aleatorios en el rango [min, max)
. Úselo 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 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;
}
srand()
cada vez que quiera llamar rand()
es una idea terrible. Como time()
normalmente devuelve un valor en segundos, llamar a esta función rápidamente devolverá el mismo valor "aleatorio".
random()
función de Unix .