web-dev-qa-db-fra.com

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

Existe-t-il une fonction permettant de générer un nombre aléatoire aléatoire en C? Ou devrais-je utiliser une bibliothèque tierce?

482
Kredns

Note: N'utilisez pas Rand() pour la sécurité. Si vous avez besoin d’un numéro cryptographiquement sécurisé, voyez cette réponse à la place.

#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: Sous Linux, vous préférerez peut-être utiliser random et srandom .

596
Łukasz Lew

La fonction Rand() dans <stdlib.h> renvoie un entier pseudo-aléatoire compris entre 0 et Rand_MAX. Vous pouvez utiliser srand(unsigned int seed) pour définir une graine.

Il est courant d’utiliser l’opérateur % conjointement avec Rand() pour obtenir une plage différente (même s’il faut garder à l’esprit que cela gâche quelque peu l’uniformité). Par exemple:

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

Si vous vraiment vous souciez de l'uniformité, vous pouvez faire 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;
  }
}
226

Si vous avez besoin de caractères aléatoires ou d'entiers sécurisés:

Comme indiqué dans comment générer en toute sécurité des nombres aléatoires dans divers langages de programmation , vous devez effectuer l'une des opérations suivantes:

  • Utiliser l'API randombytes de libsodium
  • Ré-implémentez ce dont vous avez besoin depuis avec sysrandom vous-même, très soigneusement
  • Plus généralement, utilisez /dev/urandom , pas /dev/random. Pas OpenSSL (ou d'autres PRNG d'espace utilisateur).

Par exemple:

#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() est cryptographiquement sécurisé et impartial.

43
Scott Arciszewski

Passons à travers cela. Nous utilisons d’abord la fonction srand () pour initialiser le randomiseur. Fondamentalement, l’ordinateur peut générer des nombres aléatoires en fonction du nombre fourni à srand (). Si vous donniez la même valeur de départ, les mêmes nombres aléatoires seraient générés à chaque fois. 

Par conséquent, nous devons associer le randomiseur à une valeur en constante évolution. Nous faisons cela en lui fournissant la valeur de l'heure actuelle avec la fonction time ().

Maintenant, lorsque 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;
}
28
Abhay Budakoti

Si vous avez besoin de nombres pseudo-aléatoires de meilleure qualité que ce que stdlib fournit, consultez Mersenne Twister . C'est plus rapide aussi. Les exemples d’implémentation sont nombreux, par exemple ici .

25
MH114

La fonction standard C est Rand(). C'est assez bon de distribuer des cartes pour le solitaire, mais c'est affreux. De nombreuses implémentations de Rand() parcourent une courte liste de nombres et les bits les plus faibles ont des cycles plus courts. La façon dont certains programmes appellent Rand() est affreuse et il est difficile de calculer une bonne graine pour qu'elle passe à srand().

Le meilleur moyen de générer des nombres aléatoires en C consiste à utiliser une bibliothèque tierce telle que OpenSSL. Par 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 les entiers aléatoires ou les flottants. OpenSSL ne donne que des octets aléatoires, j'essaie donc de reproduire comment Java ou Ruby les transformeraient en entiers ou en flottants.

Pour les entiers, nous voulons éviter le biais modulo. Supposons que nous ayons des entiers aléatoires à 4 chiffres de Rand() % 10000, mais Rand() ne peut renvoyer que 0 à 32767 (comme dans Microsoft Windows). Chaque nombre compris entre 0 et 2767 apparaît plus souvent que chaque nombre compris entre 2768 et 9999. Pour supprimer le biais, nous pouvons réessayer Rand() tant que la valeur est inférieure à 2768, car les valeurs 30000 de 2768 à 32767 correspondent uniformément aux valeurs 10 000 de 0. à 9999.

Pour les floats, nous voulons 53 bits aléatoires, car une double contient 53 bits de précision (en supposant que ce soit un double IEEE). Si nous utilisons plus de 53 bits, nous obtenons un biais d’arrondi. Certains programmeurs écrivent un code tel que Rand() / (double)Rand_MAX, mais Rand() peut ne renvoyer que 31 bits, ou seulement 15 bits sous Windows.

Rand_bytes() de OpenSSL se lance, peut-être en lisant /dev/urandom sous Linux. Si nous avons besoin de plusieurs nombres aléatoires, il serait trop lent de tous les lire à partir de /dev/urandom, car 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's Perl_seed () est un exemple de calcul de graine en C pour srand(). Il mélange des bits de l'heure actuelle, l'ID de processus et certains pointeurs, s'il ne peut pas lire /dev/urandom.
  • Arc4random_uniform () explique le biais modulo.
  • API Java pour Java.util.Random décrit des algorithmes permettant de supprimer les biais des entiers aléatoires et de placer 53 bits dans des flottants aléatoires.
16
George Koehler

Si votre système prend en charge la famille de fonctions arc4random , je vous recommanderais de les utiliser à 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 non signé sur 32 bits.

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

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

arc4random_stir lit les données à partir de /dev/urandom et les transmet à arc4random_addrandom afin de randomiser en outre son pool de nombres aléatoires internes.

arc4random_addrandom est utilisé par arc4random_stir pour remplir son pool de nombres aléatoires interne en fonction des données qui lui sont transmises.

Si vous ne possédez pas ces fonctions mais que 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 chose qu’un appel à Rand, à l’exception de plus sûr, et renvoie une long (facilement modifiable).

Cependant, /dev/urandom peut être un peu lent, il est donc recommandé de l’utiliser en tant que graine pour un autre générateur de nombres aléatoires. 

Si votre système n'a pas de /dev/urandom, mais que est-ce que a un /dev/random ou un fichier similaire, vous pouvez simplement changer le chemin d'accès passé à open dans urandom_init. Les appels et les API utilisés dans urandom_init et urandom sont (je crois) compatibles avec POSIX et, en tant que tels, devraient fonctionner sur la plupart des systèmes compatibles POSIX, sinon tous.

Remarques: Une lecture à partir de /dev/urandom ne sera PAS bloquée si l'entropie est insuffisante, les valeurs générées dans de telles circonstances risquent donc de ne pas être cryptographiquement sûres. Si cela vous inquiète, utilisez /dev/random, qui bloquera toujours si l'entropie est insuffisante.

Si vous utilisez un autre système (Windows, par exemple), utilisez Rand ou une API non portable spécifique à la plate-forme interne à Windows.

Fonction wrapper pour les appels urandom, Rand ou arc4random:

#define Rand_IMPL /* urandom(see large code block) | Rand | arc4random */

int myRandom(int bottom, int top){
    return (Rand_IMPL() % (top - bottom)) + bottom;
}
9
Élektra

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

La différence entre Rand et random est que random renvoie un nombre aléatoire 32 bits beaucoup plus utilisable et que Rand renvoie généralement un nombre 16 bits. Les pages de manuel BSD montrent que les bits inférieurs de Rand sont cycliques et prévisibles. Par conséquent, Rand est potentiellement inutile pour les petits nombres.

8
dreamlax

Examinez ISAAC (Indirection, Maj, Accumuler, Ajouter et Compter). Il est uniformément distribué et a une durée moyenne de cycle de 2 ^ 8295.

7
geofftnz

Vous voulez utiliser Rand(). Remarque ( TRÈS IMPORTANT ): veillez à définir le germe pour la fonction Rand. Si vous ne le faites pas, vos nombres aléatoires sont pas vraiment aléatoires . C'est très, très, très important. Heureusement, vous pouvez généralement utiliser une combinaison de la minuterie et de la date pour obtenir une bonne graine. 

4
Paul Sonier

FWIW, la réponse est que oui, il existe une fonction stdlib.h appelée Rand; cette fonction est principalement adaptée à la vitesse et à la distribution, et non à l'imprévisibilité. Presque toutes les fonctions aléatoires intégrées pour différents langages et frameworks utilisent cette fonction par défaut. Il existe également des générateurs de nombres aléatoires "cryptographiques" beaucoup moins prévisibles, mais beaucoup plus lents. Ceux-ci devraient être utilisés dans toute sorte d'application liée à la sécurité.

4
tylerl

J'espère que c'est un peu plus aléatoire que d'utiliser simplement 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
kenorb

Bien, STL est C++, pas C, donc je ne sais pas ce que vous voulez. Si vous voulez C, cependant, il existe les fonctions Rand() et srand():

int Rand(void);

void srand(unsigned seed);

Celles-ci font toutes deux partie de la norme ANSI C. Il existe également la fonction random():

long random(void);

Pour autant que je sache, random() n'est pas la norme ANSI C. Une bibliothèque tierce n'est peut-être pas une mauvaise idée, mais tout dépend de la quantité aléatoire d'un nombre que vous devez générer.

3
Chris Lutz

C Programme pour générer un nombre aléatoire compris 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 est inclusif ou dites r [lowerLimit, upperLimit)  

3
Shivam K Thakkar

C'est un bon moyen 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

Deuxième sortie: 61

Sortie la troisième fois: 65

Vous pouvez remplacer les valeurs après randnum par le nombre que vous choisissez et générer un nombre aléatoire pour vous entre ces deux nombres.

3
coderperson

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

Vous pouvez également capturer un nombre aléatoire de n'importe quel service en ligne tel que random.org.

2
Namit Sinha
#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
Muhammad Sadiq

Sur les CPU x86_64 modernes, vous pouvez utiliser le générateur de nombre aléatoire de 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
Serge Rogatch
#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
Bisu vs Utsab

J'ai eu un problème sérieux avec le générateur de nombres pseudo aléatoires dans mon application récente: j'ai appelé à plusieurs reprises mon programme C via un script pyhton et j'utilisais comme code de départ le code suivant:

srand(time(NULL))

Cependant, depuis:

  • Rand générera la même séquence pseudo-aléatoire et donnera la même graine dans srand (voir man srand);
  • Comme déjà indiqué, la fonction time ne change que de seconde en seconde: si votre application est exécutée plusieurs fois au cours de la même seconde, time renverra 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. mélangez la sortie temporelle avec d'autres informations changeant lors des exécutions (dans mon application, le nom de la sortie):

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

    J'ai utilisé djb2 comme fonction de hachage.

  2. Augmenter la résolution temporelle. clock_gettime était disponible sur ma plate-forme, je l'utilise donc:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
    
  3. Utilisez 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) la meilleure répartition aléatoire des semences, mais elle ne peut créer une différence que si vous utilisez une application très rapide . À mon avis, l'option 2 est un pari sûr.

0
Koldar
#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;
}
0
shellhub

Malgré toutes les suggestions de personnes Rand() ici, vous ne voulez pas utiliser Rand() sauf si vous devez le faire! Les nombres aléatoires que Rand() produit sont souvent très mauvais. Pour citer la page de manuel Linux:

Les versions de Rand() et srand() dans la bibliothèque Linux C utilisent le même générateur de nombres aléatoires que random(3) et srandom(3); les bits de poids faible doivent donc être aussi aléatoires que les bits de poids fort. Toutefois, sur les anciennes implémentations de Rand () et sur les implémentations actuelles de différents systèmes, les bits de poids faible sont beaucoup moins aléatoires que les bits de poids fortN'utilisez pas cette fonction dans des applications destinées à être portables lorsqu'un bon caractère aléatoire est requis. (Utilisez random(3) à la place.

En ce qui concerne la portabilité, random() est également défini depuis un certain temps par le standard POSIX. Rand() est plus ancien, il figurait déjà dans la première spécification POSIX.1 (Norme IEEE 1003.1-1988), alors que random() est apparu pour la première fois dans POSIX.1-2001 (Norme IEEE 1003.1-2001), alors que le standard POSIX actuel est déjà POSIX.1. -2008 (norme IEEE 1003.1-2008), qui a fait l’objet d’une mise à jour il y a un an (norme IEEE 1003.1-2008, édition 2016). Je considère donc que random() est très portable.

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

Cette famille de fonctions générera des nombres pseudo-aléatoires à l'aide d'un algorithme congruentiel linéaire et d'une arithmétique entière sur 48 bits.

Et la bonne source pseudo-aléatoire est la fonction arc4random() qui est disponible sur de nombreux systèmes. N’appartenant à aucune norme officielle, elle est apparue dans BSD vers 1997, mais vous pouvez la trouver sur des systèmes tels que Linux et macOS/iOS.

0
Mecki

Entendre une bonne explication de la raison pour laquelle utiliser Rand() pour produire des nombres aléatoires uniformément répartis dans une plage donnée est une mauvaise idée, j'ai décidé de jeter un coup d'œil à la façon dont le résultat est asymétrique. Mon cas de test était juste lancer 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 à quel point vos numéros aléatoires doivent être uniformes, mais ce qui précède semble suffisamment uniforme pour la plupart des besoins.

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

0
Mouse