¿Generando un número aleatorio entre en C?

He visto muchas preguntas sobre SO sobre este tema en particular, pero ninguna de ellas tiene una respuesta para mí, así que pensé en hacer esta pregunta.

Quería generar un número aleatorio entre [-1, 1]. ¿Como puedo hacer esto?

Utilice -1+2*((float)rand())/RAND_MAX

rand() genera enteros en el rango [0,RAND_MAX] inclusive, por lo tanto, ((float)rand())/RAND_MAX devuelve un número de punto flotante en [0,1] . Obtenemos números aleatorios de [-1,1] al sumrlos a -1 .

EDITAR (agregando partes relevantes de la sección de comentarios)

Sobre las limitaciones de este método:

((float)rand())/RAND_MAX devuelve un porcentaje (una fracción de 0 a 1). Entonces, como el rango entre -1 a 1 es 2 enteros, multiplico esa fracción por 2 y luego la agrego al número mínimo que desea, -1. Esto también le informa sobre la calidad de sus números aleatorios, ya que solo tendrá números aleatorios únicos RAND_MAX .

Si todo lo que tiene es la biblioteca estándar de C, entonces las respuestas de otras personas son sensatas. Si tiene la funcionalidad POSIX disponible para usted, considere usar la familia de funciones drand48 () . En particular:

 #define _XOPEN_SOURCE 600 /* Request non-standard functions */ #include  double f = +1.0 - 2.0 * drand48(); double g = -1.0 + 2.0 * drand48(); 

Tenga en cuenta que el manual dice:

Las funciones drand48 () y erand48 () devolverán valores de punto flotante no negativos, de doble precisión, distribuidos uniformemente en el intervalo [0.0,1.0).

Si necesita estrictamente [-1.0,+1.0] (a diferencia de [-1.0,+1.0) ), entonces se enfrenta a un problema muy delicado sobre cómo ampliar el rango.

Las funciones drand48() te dan una aleatoriedad considerablemente mayor que la implementación típica de rand() . Sin embargo, si necesita aleatoriedad criptográfica, ninguno de estos es apropiado; debe buscar ‘PRNG criptográficamente fuerte’ (PRNG = generador de números pseudoaleatorios).

Hace un tiempo tuve una pregunta similar y pensé que podría ser más eficiente generar la parte fraccionaria directamente. Realicé algunas búsquedas y encontré un interesante rand de punto flotante rápido que no usa división o multiplicación de punto flotante o se puede hacer un lanzamiento int-> float con un conocimiento íntimo de la representación interna de un flotador:

 float sfrand( void ) { unsigned int a=(rand()<<16)|rand(); //we use the bottom 23 bits of the int, so one //16 bit rand() won't cut it. a=(a&0x007fffff) | 0x40000000; return( *((float*)&a) - 3.0f ); } 

La primera parte genera un flotador aleatorio de [2 ^ 1,2 ^ 2), resta 3 y tienes [-1, 1). Por supuesto, esto puede ser demasiado íntimo para algunas aplicaciones / desarrolladores, pero era justo lo que estaba buscando. Este mecanismo funciona bien para cualquier rango que tenga una potencia de 2 de ancho.

Para empezar, necesitarás la función de biblioteca C rand() . Esto está en el archivo de encabezado stdlib.h , por lo que debe poner:

 #include  

cerca del comienzo de su código. rand() generará un entero aleatorio entre cero y RAND_MAX por lo que al dividirlo por RAND_MAX / 2 , obtendrá un número entre cero y 2 inclusive. Resta uno, y estarás en tu rango objective de -1 a 1.

Sin embargo, si simplemente hace int n = rand() / (RAND_MAX / 2) encontrará que no obtiene la respuesta que espera. Esto se debe a que tanto rand() como RAND_MAX / 2 son enteros, por lo que se utiliza la aritmética de enteros. Para evitar que esto suceda, algunas personas usan un molde flotante, pero yo recomendaría evitar los lanzamientos multiplicando por 1.0 .

También debe sembrar su generador de números aleatorios utilizando la función srand() . Para obtener un resultado diferente cada vez, las personas a menudo activan el generador basándose en la hora del reloj, haciendo srand(time(0)) .

Entonces, en general tenemos:

 #include  srand(time(0); double r = 1.0 * rand() / (RAND_MAX / 2) - 1; 

Si bien la respuesta aceptada está bien en muchos casos, omitirá “cualquier otro número”, porque está expandiendo un rango de valores ya discretos en 2 para cubrir el intervalo [-1, 1]. De manera similar, si tuviera un generador de números aleatorios que pudiera generar un número entero de [0, 10] y quisiera generar [0, 20], simplemente multiplicando por 2 abarcará el rango, pero no podrá cubrir el rango (Se omitirían todos los números impares).

Probablemente tenga el grano suficiente para sus necesidades, pero tiene este inconveniente, que podría ser estadísticamente significativo (y perjudicial) en muchas aplicaciones, en particular simulaciones de monte carlo y sistemas que dependen en gran medida de las condiciones iniciales.

Un método que sea capaz de generar cualquier número de punto flotante representable de -1 a 1 inclusive debe basarse en generar una secuencia a1.a2 a3 a4 a5 … hasta el límite de su precisión de punto flotante, que es la única manera de poder Para generar cualquier flotador posible en el rango. (es decir, siguiendo la definición de los números reales)

De la “The Standard Library C”

int rand(void) : devuelve el número pseudoaleatorio en el rango 0 a RAND_MAX

RAND_MAX – Valor máximo devuelto por rand() .

Asi que:

rand() devolverá un número pseudoaleatorio en el rango 0 a RAND_MAX

rand() / RANDMAX devolverá un número pseudoaleatorio en el rango de 0 a 1

2*( rand() / RANDMAX ) devolverá un número pseudoaleatorio en el rango de 0 a 2

2*( rand() / RANDMAX ) -1 devolverá un número pseudoaleatorio en el rango -1 a 1

Como otros ya han señalado, cualquier bash de simplemente transformar el rango de la función ‘rand ()’ de [0, RAND_MAX] en el [-1, +1] deseado producirá un generador de números aleatorios que solo puede generar un conjunto discreto de -puntos de valores. Para un generador de punto flotante, la densidad de estos valores puede ser insuficiente en algunas aplicaciones (si el valor definido por la implementación de RAND_MAX no es lo suficientemente grande). Si esto es un problema, uno puede boost la densidad mencionada exponencialmente utilizando dos o más llamadas ‘rand ()’ en lugar de una.

Por ejemplo, al combinar los resultados de dos llamadas consecutivas a ‘rand ()’, se puede obtener un número pseudoaleatorio en [0, (RAND_MAX + 1) ^ 2 – 1] rango

 #define RAND_MAX2 ((RAND_MAX + 1ul) * (RAND_MAX + 1) - 1) unsigned long r2 = (unsigned long) rand() * (RAND_MAX + 1) + rand(); 

y luego use el mismo método para transformarlo en un número de punto flotante en el rango [-1, +1]

 double dr2 = r2 * 2.0 / RAND_MAX2 - 1; 

Al usar este método, se pueden acumular tantas llamadas ‘rand ()’ como sea necesario, por supuesto, vigilando el desbordamiento de enteros.

Como nota al margen, este método de combinación de llamadas ‘rand ()’ consecutivas no produce generadores de números pseudoaleatorios de muy alta calidad, pero podría funcionar perfectamente para muchos propósitos.