¿Cómo generar un int aleatorio en C?

Respuestas:

663

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 .

Łukasz Lew
fuente
194
+1 por simplicidad, pero probablemente sea una buena idea enfatizar que srand () solo debe llamarse una vez . Además, en una aplicación con subprocesos, es posible que desee asegurarse de que el estado del generador se almacena por subproceso y sembrar el generador una vez para cada subproceso.
RBerteig 05 de
41
@trusktr, es complicado. Aquí hay una razón: 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.
RBerteig
99
@trusktr para un generador congruencial lineal simple (que es lo que 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.
RBerteig
21
Evite una advertencia del compilador con un elenco:srand((unsigned int)time(NULL));
GiovaMaster
99
Tenga en cuenta que esta sigue siendo una forma débil de ver el PRNG. El año pasado, un virus de tipo cryptolocker en Linux cometió el error de sembrar con el tiempo, y esto redujo drásticamente el espacio de búsqueda. Todo lo que tenía que hacer era tener una idea decente de cuándo se produjo la infección y luego probar las semillas de esa época. Lo último que escuché es que la mejor fuente de aleatoriedad es / dev / urandom, que supuestamente proviene de una combinación de fuentes caóticas como las temperaturas en el hardware. Sin embargo, si todo lo que realmente desea es que su programa actúe de manera diferente en cada ejecución, la solución anterior está bien.
Jemenake
238

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;
  }
}
Laurence Gonsalves
fuente
18
Es una práctica común , pero no es la correcta. Mira esto y esto .
Lazer
35
@Lazer: Por eso dije "aunque ten en cuenta que esto quita un poco la uniformidad".
Laurence Gonsalves
3
@AbhimanyuAryan The %es el operador de módulo. Le da el resto de una división entera, por x % nlo que siempre le dará un número entre 0y n - 1(siempre xy cuando nsean positivos). Si todavía lo encuentra confuso, intente escribir un programa que icuente de 0 a 100, e imprima i % npara algunos nde su elección más pequeños que 100.
Laurence Gonsalves
2
@necromancer Seguí adelante y agregué una solución perfectamente uniforme.
Laurence Gonsalves
2
@Lazer, el segundo enlace que publicaste en realidad aún no es perfectamente uniforme. Lanzar a un doble y viceversa no ayuda. El primer enlace que publicaste tiene una solución perfectamente uniforme, aunque se repetirá mucho para los límites superiores pequeños. Agregué una solución perfectamente uniforme a esta respuesta que no debería repetirse tanto incluso para los límites superiores pequeños.
Laurence Gonsalves
53

Si necesita caracteres aleatorios o enteros seguros:

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:

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.

Scott Arciszewski
fuente
¿Se debe sembrar libsodium RNG antes de llamar a randombytes_buf?
user2199593
Solo llame sodium_init()en algún momento. No te preocupes por el RNG, usa el núcleo.
Scott Arciszewski
Nota: Aprobé la edición reciente sodium_init()aunque no sea necesariamente parte de mi ejemplo porque es un detalle importante.
Scott Arciszewski
29

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;
}
Abhay Budakoti
fuente
12
Código agradable, pero no es una buena idea llamar 'srand (time (NULL));'. Este método produce el mismo número cuando se llama en un bucle for.
RayOldProf
1
Las ediciones sugeridas que involucran código a menudo se rechazan. Alguien hizo uno aquí con el comentario "el algoritmo estaba equivocado. Podría producir números mayores que el máximo". No he evaluado el reclamo yo mismo.
Martin Smith
1
@Martin Smith Problemas: 1) debe ser 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.
chux - Restablece a Monica
1
Volver a colocarlo así hará que esta función produzca el mismo número si se llama varias veces en el mismo segundo. Si realmente desea volver a sembrarlo, vuelva a hacerlo solo una vez por segundo.
Élektra
1
Menor: hi_num = max_num + 1;carece de protección contra el desbordamiento.
chux - Restablece a Mónica el
25

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

MH114
fuente
2
+1: Parece genial pero solo estaba haciendo un juego de adivinanzas. Si fuera a usar un generador de números aleatorios en una aplicación comercial, definitivamente lo usaría.
Kredns 01 de
44
No use un Mersenne Twister, use algo bueno como xoroshiro128 + o PCG. (Enlace relevante.)
Veedrac
17

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 doublecontiene 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/urandomen 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:

George Koehler
fuente
Gracias por esta respuesta extendida. Tenga en cuenta que de las 24 respuestas actuales a esta pregunta, usted fue el único con una interpretación adicional para tratar float/ double, por lo que he aclarado la pregunta para mantener los intnúmeros para evitar que sea demasiado amplia. Hay otras preguntas que tratan específicamente de C float/ doublevalores aleatorios, por lo que puede volver a publicar su segunda mitad de su respuesta a preguntas tales como stackoverflow.com/questions/13408990/...
Coeur
11

Si su sistema admite la arc4randomfamilia de funciones, recomendaría usarlas en lugar de la randfunción estándar .

La arc4randomfamilia 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_bufpone contenido aleatorio en su parámetro buf : void *. La cantidad de contenido está determinada por el bytes : size_tparámetro.

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

arc4random_stirlee datos /dev/urandomy los transfiere para arc4random_addrandomaleatorizar adicionalmente su grupo interno de números aleatorios.

arc4random_addrandomes utilizado por arc4random_stirpara 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_initfunción abre el /dev/urandomdispositivo y coloca el descriptor de archivo urandom_fd.

La urandomfunció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/urandompuede 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/randomarchivo o similar, entonces sólo tiene que cambiar la ruta pasó a openen urandom_init. Las llamadas y las API utilizadas en urandom_inity urandomson (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/urandomNO 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 rando alguna API interna no portátil dependiente de la plataforma específica de Windows.

Envoltorio para la función urandom, rando arc4randomlas llamadas:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}
Élektra
fuente
8

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. randes POSIX, randomes una función de especificación BSD.

La diferencia entre randy randomes que randomdevuelve un número aleatorio de 32 bits mucho más utilizable, y randgeneralmente devuelve un número de 16 bits. Las páginas de manual de BSD muestran que los bits más bajos randson cíclicos y predecibles, por lo que randes potencialmente inútil para números pequeños.

dreamlax
fuente
3
@Neil: dado que todas las respuestas hasta ahora mencionan el STL, sospecho que la pregunta se editó rápidamente para eliminar una referencia innecesaria.
Michael Burr
rand () no es inútil para números pequeños: puede desplazarlos y usar solo los bits altos más aleatorios si realmente lo necesita.
Chris Lutz
@Chris, puede hacerlo si se conoce el tamaño del número aleatorio, pero si el tamaño requerido del número aleatorio cambia durante el tiempo de ejecución (como barajar una matriz dinámica, etc.) sería difícil evitar tal advertencia.
dreamlax
No puedo encontrar ninguna función aleatoria aquí :-(
kasia.b
@ kasia.b en ese enlace, hay extern int rand(void);y extern void srand(unsigned int);.
RastaJedi
7

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.

geofftnz
fuente
2
ISAAC es un RNG interesante debido a su velocidad, pero aún no ha recibido atención criptográfica seria.
user2398029
4

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 randnuma los números que elija, y generará un número aleatorio para usted entre esos dos números.

codificador
fuente
4

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.

Paul Sonier
fuente
66
Dos puntos a) sus números aleatorios no son "verdaderamente" aleatorios, sin importar cómo se siembra el generador. Y b) es muy conveniente que la secuencia pseudoaleatoria sea siempre la misma en muchas circunstancias, por ejemplo, para pruebas.
16
Si es MUY IMPORTANTE que su número sea verdaderamente aleatorio, no debería usar la función rand ().
tylerl
1
Los valores de rand no son "verdaderamente" aleatorios, sin importar si establece la semilla o no. Dada una semilla conocida, la secuencia es predecible. La generación de números aleatorios "verdaderamente" es difícil. No hay entropía involucrada con rand.
dreamlax
2
Por supuesto que lo harán: la biblioteca sembrará el generador para usted (probablemente a cero, pero esa es una semilla válida).
44
Ah, pero el algoritmo conocido / semilla conocida es esencial para depurar cualquier programa que use números aleatorios. No es inusual registrar la semilla utilizada junto con una ejecución de simulación para que pueda recrearse para un análisis más detallado. No llamar a srand () es equivalente a llamar a srand (1).
RBerteig 05 de
4

FWIW, la respuesta es que sí, hay una stdlib.hfunció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.

tylerl
fuente
4

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());
}
kenorb
fuente
1
agregar srand (rand ()) no aumenta la aleatoriedad de la secuencia si este programa se ejecuta varias veces en 1 segundo. time (NULL) aún devolverá el mismo valor para cada uno de ellos, el primer rand () devolverá el mismo largo y la segunda llamada a srand () tendrá el mismo valor, lo que dará como resultado que todavía tenga la misma secuencia aleatoria. El uso de la dirección de argc podría ayudar, solo si se garantiza que esta dirección será diferente en cada ejecución del programa, lo que no siempre es cierto.
theferrit32
3

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.

Chris Lutz
fuente
3

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)

Shivam K. Thakkar
fuente
@Pang Eso es lo que mencioné claramente ENTRE 9 y 50, NO DE 9 y 50.
Shivam K. Thakkar
2
Su operación de módulo introdujo un sesgo.
jww
2

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.

Namit Sinha
fuente
1
También puede obtener un número aleatorio de cualquier servicio en línea como random.org Bounty si incluye una forma portátil y eficiente de hacer esto en C.
MD XF
2
#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]);
}
Muhammad Sadiq
fuente
luego defina en la parte superior con variables @ b4hand
Muhammad Sadiq
En el momento en que hice ese comentario, no tenía permisos de edición universales y, en general, los cambios de código que cambiaban la respuesta real serían rechazados. Si no te importa arreglar tu respuesta, puedo hacerlo.
b4hand
Si su intención era producir una permutación aleatoria de valores únicos, este código todavía tiene un error. Produce el valor 84 dos veces y no produce el valor 48. Además, no genera el generador de números aleatorios, por lo que la secuencia es la misma en cada ejecución.
b4hand
1
las variables ayb están definidas en este código. Está libre de errores. No hay sintaxis ni error lógico.
Muhammad Sadiq
Incorrecto. Ya he confirmado a mano la salida como mencioné.
b4hand
1
#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;
}
Bisu vs Utsab
fuente
1

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
Serge Rogatch
fuente
1
#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;
}
shellhub
fuente
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).

Ratón
fuente
rand () puede fallar en otras pruebas de aleatoriedad, como las pruebas intransigentes . rand () difiere de una plataforma a otra; Los valores rand () de GNU / Linux pueden ser mejores que los valores de BSD o Windows.
George Koehler
Esta no es una forma válida de probar la aleatoriedad.
Veedrac
Depende del propósito y del modelo de amenaza / riesgo. Para RNG criptográficamente fuerte, seguro, use RDRAND (o RDSEED). Para un simple lanzador de dados (no a nivel de casino) en mi humilde opinión, lo anterior debería ser suficiente. La palabra clave es " suficientemente buena ".
Ratón el
0

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:

  • 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 dentro del mismo segundo, timedevolverá el mismo valor cada vez.

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

  1. 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.

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

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
  3. 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.

Koldar
fuente
Incluso con estas heurísticas, no confíe en rand () para obtener datos criptográficos.
domenukk
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.
Koldar
0

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()y srand()en la Biblioteca Linux C usan 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 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 su random(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.

Mecki
fuente
random()no existe en Windows
Björn Lindqvist
@ BjörnLindqvist Windows tampoco es un sistema POSIX; Es prácticamente el único sistema en el mercado que no admite al menos las API POSIX básicas (que incluso admiten sistemas como iOS). Windows solo es compatible, 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 _WIN32es 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.
Mecki
0

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;
    }
}
Brad Grissom
fuente
-2

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));
} 
Michał Ziobro
fuente
-3

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;
}
Usuario
fuente
16
Este código no es bueno. Llamar 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".
Blastfurnace
3
Esta función se confundiría con la random()función de Unix .
George Koehler