Comment générer un int aléatoire en C?

Est-il une fonction pour générer un random int nombre en C? Ou devrai-je utiliser une bibliothèque tierce?

467
demandé sur Cœur 2009-05-05 02:07:59

24 réponses

Note : N'utilisez pas rand() comme garantie. Si vous avez besoin d'un numéro cryptographique sécurisé, voir cette réponse à la place.

#include <time.h>
#include <stdlib.h>

srand(time(NULL));   // should only be called once
int r = rand();      // returns a pseudo-random integer between 0 and RAND_MAX
571
répondu Łukasz Lew 2017-05-23 12:26:36

la fonction rand() dans <stdlib.h> retourne un entier pseudo-aléatoire entre 0 et RAND_MAX . Vous pouvez utiliser srand(unsigned int seed) pour définir une graine.

il est pratique courante d'utiliser l'opérateur % en conjonction avec rand() pour obtenir une gamme différente (bien que gardez à l'esprit que cela enlève quelque peu l'uniformité). Par exemple:

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

Si vous vraiment se soucier de l'homogénéité vous pouvez faites quelque chose comme ceci:

/* 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;
  }
}
212
répondu Laurence Gonsalves 2018-04-08 22:09:14

laisse passer par ceci. Tout d'abord, nous utilisons la fonction srand() pour lancer le randomizer. Fondamentalement, l'ordinateur peut générer des nombres aléatoires basés sur le nombre qui est transmis à srand(). Si vous avez donné la même valeur de graine, alors les mêmes nombres aléatoires seraient générés à chaque fois.

par conséquent, nous devons ensemencer le randomizer avec une valeur qui change toujours. Nous le faisons en lui donnant la valeur du temps courant avec la fonction time ().

Maintenant, quand nous appelons rand(), un nouveau nombre aléatoire sera produit à chaque fois.

#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;
}
25
répondu Abhay Budakoti 2017-06-05 19:52:23

si vous avez besoin de nombres pseudo aléatoires de meilleure qualité que ce que stdlib fournit, Vérifiez Mersenne Twister . C'est plus rapide, trop. Les exemples d'implémentations sont nombreux, par exemple ici .

24
répondu MH114 2009-05-31 08:14:18

la fonction C standard est rand() . C'est assez bon pour traiter les cartes de solitaire, mais c'est horrible. De nombreuses implémentations du cycle rand() à travers une courte liste de nombres, et les bits Bas ont des cycles plus courts. La façon dont certains programmes appellent rand() est terrible, et calculer une bonne graine pour passer à srand() est difficile.

la meilleure façon de générer des nombres aléatoires en C est d'utiliser une bibliothèque tierce comme OpenSSL. Exemple,

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

pourquoi autant de code? D'autres langages comme Java et Ruby ont des fonctions pour des entiers aléatoires ou des flotteurs. OpenSSL ne donne que des octets aléatoires, donc j'essaie d'imiter comment Java ou Ruby les transformerait en entiers ou en floats.

Pour les entiers, nous voulons éviter modulo biais . Supposons que nous obtenions des nombres entiers aléatoires à 4 chiffres de rand() % 10000 , mais rand() ne peut retourner que 0 à 32767 (comme dans Microsoft Windows). Chaque nombre de 0 à 2767 apparaît plus souvent que chaque numéro de 2768 à 9999. Pour supprimer le biais, nous pouvons réessayer rand() alors que la valeur est inférieure à 2768, parce que les valeurs de 30000 de 2768 à 32767 correspondent uniformément aux valeurs de 10000 de 0 à 9999.

pour les flotteurs, nous voulons 53 bits aléatoires, parce qu'un double contient 53 bits de précision (en supposant que c'est un double IEEE). Si nous utilisons plus de 53 bits, nous obtenons un biais d'arrondissement. Quelque les programmeurs écrivent du code comme rand() / (double)RAND_MAX , mais rand() pourrait retourner seulement 31 bits, ou seulement 15 bits dans Windows.

OpenSSL RAND_bytes() se développe lui-même, peut-être en lisant /dev/urandom sous Linux. Si nous avons besoin de beaucoup de nombres aléatoires, il serait trop lent pour lire à partir de /dev/urandom , parce qu'ils doivent être copiés à partir du noyau. Il est plus rapide de permettre à OpenSSL de générer plus de nombres aléatoires à partir d'une graine.

plus sur les nombres aléatoires:

  • Perl Perl_seed() est un exemple de comment calculer une graine en C pour srand() . Il mélange des bits de l'heure courante, L'ID de processus, et quelques pointeurs, s'il ne peut pas lire /dev/urandom .
  • OpenBSD arc4random_uniform() , explique modulo biais.
  • API Java pour java.util.Aléatoire décrit des algorithmes pour supprimer le biais à partir d'entiers aléatoires, et empaqueter 53 bits dans des flotteurs aléatoires.
14
répondu George Koehler 2015-07-08 16:33:21

si votre système prend en charge la arc4random famille de fonctions je recommande l'utilisation de ceux à la place de la fonction standard rand .

la famille arc4random comprend:

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 renvoie un entier aléatoire de 32 bits non signé.

arc4random_buf met du contenu aléatoire dans son paramètre buf : void * . La quantité de contenu est déterminée par le bytes : size_t paramètre.

arc4random_uniform renvoie un entier aléatoire non signé de 32 bits qui suit la règle: 0 <= arc4random_uniform(limit) < limit , où limit est aussi un entier non signé de 32 bits.

arc4random_stir lit les données de /dev/urandom et passe les données à arc4random_addrandom pour en plus randomiser son pool interne de nombres aléatoires.

arc4random_addrandom est utilisé par arc4random_stir pour peupler son pool interne de nombres aléatoires selon les données passé.

Si vous n'avez pas ces fonctions, mais vous êtes sous Unix, vous pouvez utiliser ce code:

/* 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 fonction urandom_init ouvre le périphérique /dev/urandom et place le descripteur de fichier dans urandom_fd .

la fonction urandom est fondamentalement la même qu'un appel à rand , sauf plus sûr, et il renvoie un long (facilement modifiable).

Cependant, /dev/urandom peut être un peu lent, il est donc recommandé de l'utiliser comme une graine pour un autre générateur de nombre aléatoire.

si votre système n'a pas un /dev/urandom , mais fait ont un /dev/random ou un fichier similaire, alors vous pouvez simplement changer le chemin passé à open dans urandom_init . Les appels et les API utilisés dans urandom_init et urandom sont (je crois) conformes à POSIX, et en tant que tels, devraient fonctionner sur la plupart, si pas tous les systèmes conformes à POSIX.

Notes: une lecture de /dev/urandom ne bloquera pas s'il n'y a pas suffisamment d'entropie disponible, de sorte que les valeurs générées dans de telles circonstances peuvent être cryptographiquement non sécurisées. Si vous êtes inquiet à ce sujet, puis utiliser /dev/random , qui sera toujours bloquer s'il ya entropie insuffisante.

si vous êtes sur un autre système(c.-à-d. Windows), alors utilisez rand ou certains Windows internes spécifique plate-forme-dépendant non portable API.

fonction Wrapper pour urandom , rand , ou arc4random appels:

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

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}
9
répondu Xenon 2017-07-05 23:41:06

TSL n'existe pas en C. Vous devez appeler le rand , ou, mieux encore, random . Ceux-ci sont déclarés dans l'en-tête de la bibliothèque standard stdlib.h . rand est POSIX, random est une fonction BSD spec.

la différence entre rand et random est que random retourne un nombre aléatoire 32 bits beaucoup plus utilisable, et rand retourne typiquement un nombre 16 bits. Les pages de manuel BSD montrent que les bits inférieurs de rand sont cycliques et prévisibles, de sorte que rand est potentiellement inutile pour les petits nombres.

8
répondu dreamlax 2017-03-08 20:24:47

regarder ISAAC (Indirection, de Décalage, d'Accumuler, d'Ajouter et de Comptage). Sa distribution est uniforme et sa durée moyenne de cycle est de 2^8295.

7
répondu geofftnz 2009-05-04 22:18:23

vous voulez utiliser rand() . Note ( très IMPORTANT ): assurez-vous de définir la graine pour la fonction rand. Si vous ne le faites pas, vos nombres aléatoires sont pas vraiment aléatoire . C'est très, très, très important. Heureusement, vous pouvez généralement utiliser une combinaison du système ticks timer et la date pour obtenir une bonne graine.

4
répondu Paul Sonier 2017-03-08 20:25:27

FWIW, la réponse est que oui, il y a une fonction stdlib.h appelée rand ; cette fonction est accordée principalement pour la vitesse et la distribution, pas pour l'imprévisibilité. Presque toutes les fonctions aléatoires intégrées pour différents langages et cadres utilisent cette fonction par défaut. Il y a aussi "cryptographique" générateurs de nombres aléatoires qui sont beaucoup moins prévisibles, mais beaucoup plus lentement. Ceux-ci devraient être utilisés dans toute sorte d'application liée à la sécurité.

4
répondu tylerl 2017-03-08 20:26:25

c'est peut-être un peu plus aléatoire que d'utiliser 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());
}
4
répondu kenorb 2017-03-08 20:27:26

Eh bien, STL est C++, Pas C, donc je ne sais pas ce que vous voulez. Si vous voulez C, cependant, il ya les rand() et srand() fonctions:

int rand(void);

void srand(unsigned seed);

ils font tous les deux partie de ANSI C. Il y a aussi la fonction random() :

long random(void);

mais pour autant que je puisse dire, random() n'est pas standard ANSI C. Une bibliothèque tierce partie peut ne pas être une mauvaise idée, mais tout dépend de comment aléatoire d'un nombre que vous devez vraiment générer.

3
répondu Chris Lutz 2009-05-04 22:13:27

c'est une bonne façon d'obtenir un nombre aléatoire entre deux nombres de votre choix.

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

sortie la première fois: 39

sortie la deuxième fois: 61

sortie la troisième fois: 65

vous pouvez changer les valeurs après randnum à n'importe quel nombre que vous choisissez, et il générera un nombre aléatoire pour vous entre ces deux nombres.

3
répondu coderperson 2017-02-27 04:52:55

C Programme pour générer un nombre aléatoire entre 9 et 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 général, nous pouvons générer un nombre aléatoire entre lowerLimit et upperLimit-1

I. e lowerLimit is inclusive or say r [lowerLimit, upperLimit]

3
répondu Shivam K Thakkar 2017-11-20 14:45:21

rand() est le moyen le plus pratique pour générer des nombres aléatoires.

vous pouvez également attraper un nombre aléatoire à partir de tout service en ligne comme random.org.

2
répondu Namit Sinha 2016-08-16 17:47:08
#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]);
}
2
répondu Muhammad Sadiq 2017-03-10 03:58:41
#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;
}
1
répondu Bisu vs Utsab 2017-02-28 20:16:26

sur les CPU x86_64 modernes, vous pouvez utiliser le générateur de nombres aléatoires matériel via _rdrand64_step()

exemple de code:

#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
1
répondu Serge Rogatch 2017-11-13 19:24:43

entendre une bonne explication de la raison pour laquelle l'utilisation de rand() pour produire des nombres aléatoires distribués uniformément dans une gamme donnée est une mauvaise idée, j'ai décidé de jeter un oeil à la façon dont asymétrique la sortie est réellement. Mon cas test était juste jeter des dés. Voici le code 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);
}

et voici sa sortie:

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

Je ne sais pas comment uniforme vous avez besoin de vos numéros aléatoires pour être, mais le ci-dessus semble assez uniforme pour la plupart des besoins.

Edit: ce serait une bonne idée d'initialiser le PRNG avec quelque chose de mieux que time(NULL) .

0
répondu Mouse 2014-10-20 04:06:25

j'ai eu un sérieux problème avec pseudo générateur de nombres aléatoires dans ma récente application: j'ai appelé mon programme C de façon répétée via un script pyhton et j'utilisais comme seed le code suivant:

srand(time(NULL))

Cependant, depuis:

  • Rand générera la même séquence pseudo-aléatoire donnant la même graine dans srand (voir man srand );
  • comme déjà indiqué, la fonction de temps change seulement la seconde de la seconde: si votre application est exécuté plusieurs fois dans la même seconde, time retournera la même valeur à chaque fois.

Mon programme a généré la même séquence de nombres. Vous pouvez faire 3 choses pour résoudre ce problème:

  1. mix sortie en temps avec quelques autres informations modification sur les pistes (dans mon application, le nom de la sortie):

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

    j'ai utilisé djb2 comme mon hachage fonction.

  2. Augmenter le temps de résolution. Sur ma plate-forme, clock_gettime était disponible, donc je l'utilise:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
    
  3. utiliser les deux méthodes ensemble:

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

L'Option 3 vous assure (autant que je sache) le meilleur aléatoire de graines, mais il peut créer une différence que sur une application très rapide. À mon avis, l'option 2 est une option sûre.

0
répondu Koldar 2017-01-16 16:33:18

malgré toutes les suggestions des gens rand() ici, vous ne voulez pas utiliser rand() à moins que vous ne devez! Les nombres aléatoires que rand() produit sont souvent très mauvais. Pour citer la page du manuel de Linux:

les versions de rand() et srand() de la bibliothèque C de Linux utilisent le même générateur de nombres aléatoires que random(3) et srandom(3) , de sorte que les bits d'ordre inférieur devraient être aussi aléatoires que les bits d'ordre supérieur. Cependant, sur les bits d'ordre inférieur sont beaucoup moins aléatoires que les bits d'ordre supérieur . Ne pas utiliser cette fonction dans les applications destinées à être portables quand une bonne aléatoire est nécessaire. ( utilisez random(3) à la place. )

en ce qui concerne la portabilité, random() est également défini par la norme POSIX depuis un certain temps déjà. rand() est plus ancien, il est apparu déjà dans le premier POSIX.1 spec (IEEE Std 1003.1-1988), alors que random() est apparu pour la première fois dans POSIX.1-2001 (IEEE Std 1003.1-2001), pourtant la norme actuelle POSIX est déjà POSIX.1-2008 (IEEE Std 1003.1-2008), qui a reçu une mise à jour il y a tout juste un an (IEEE Std 1003.1-2008, Édition 2016). Donc je considérerais random() comme très portable.

POSIX.1-2001 a également introduit les fonctions lrand48() et mrand48() , voir ici :

cette famille de fonctions doit générer des nombres pseudo-aléatoires en utilisant un algorithme de congruence linéaire et une arithmétique entière de 48 bits.

et très bonne pseudo source aléatoire est la fonction arc4random() qui est disponible sur de nombreux systèmes. Ne fait pas partie d'aucune norme officielle, apparu dans BSD autour de 1997, mais vous pouvez le trouver sur des systèmes comme Linux et macOS/iOS.

0
répondu Mecki 2017-11-17 17:46:37

ma solution minimaliste devrait fonctionner pour les nombres aléatoires dans la gamme [min, max) . Utilisez srand(time(NULL)) avant d'invoquer la fonction.

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));
} 
-1
répondu Michał Ziobro 2018-05-16 13:47:22

essayez ceci, je l'ai mis ensemble à partir de certains des concepts déjà mentionnés ci-dessus:

/*    
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;
}
-3
répondu User 2017-02-28 17:21:36