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

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

Note : N’utilisez pas rand() pour la sécurité. Si vous avez besoin d’un numéro sécurisé sur le plan cryptographique, consultez plutôt cette réponse .

 #include  #include  srand(time(NULL)); // should only be called once int r = rand(); // returns a pseudo-random integer between 0 and RAND_MAX 

La fonction rand() dans 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 % en conjonction avec rand() pour obtenir une plage différente (mais gardez à l’esprit que cela déséquilibre quelque peu l’uniformité). Par exemple:

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

Si vous vous souciez vraiment 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; } } 

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

Comme indiqué dans la façon de générer en toute sécurité des nombres aléatoires dans différents langages de programmation , vous devrez effectuer l’une des opérations suivantes:

  • Utiliser l’API randombytes
  • Ré-implémentez vous-même ce que vous avez besoin de l’implémentation sysrandom de libsodium
  • Plus largement, utilisez /dev/urandom , pas /dev/random . Pas OpenSSL (ou autres PRNG d’espace utilisateur).

Par exemple:

 #include "sodium.h" int foo() { char mySsortingng[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 sécurisé et impartial du sharepoint vue cryptographique.

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

Permet de passer par là. Nous utilisons d’abord la fonction srand () pour amorcer 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 semer le randomiseur avec une valeur qui change constamment. Nous faisons cela en lui donnant la valeur de l’heure actuelle avec la fonction time ().

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

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

La fonction standard C est rand() . C’est assez bon pour dissortingbuer des cartes pour le solitaire, mais c’est affreux. De nombreuses implémentations de rand() parcourent une courte liste de nombres et les bits bas ont des cycles plus courts. La façon dont certains programmes appellent rand() est affreuse, et calculer une bonne graine à transmettre à srand() est difficile.

La meilleure façon de générer des nombres aléatoires dans C consiste à utiliser une bibliothèque tierce comme OpenSSL. Par exemple,

 #include  #include  #include  #include  /* 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(uc, sizeof(uc))) { fprintf(stderr, "Can't get random bytes!\n"); exit(1); } } while (ui < (-limit % limit)); /* ui < (2**size % limit) */ return ui % 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(uc, sizeof(uc))) { fprintf(stderr, "Can't get random bytes!\n"); exit(1); } /* 53 bits / 2**53 */ return (ui >> 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 tant 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, alors j’essaie d’imiter la manière dont Java ou Ruby les transforment en nombres 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 que renvoyer 0 à 32767 (comme dans Microsoft Windows). Chaque nombre compris entre 0 et 2767 apparaîtrait plus souvent que chaque nombre compris entre 2768 et 9999. Pour supprimer le biais, nous pouvons réessayer rand() alors que la valeur est inférieure à 2768, car les 30000 valeurs de 2768 à 32767 sont uniformément associées aux 10 000 valeurs. de 0 à 9999.

Pour les flottants, nous voulons 53 bits aléatoires, car un 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 du code comme rand() / (double)RAND_MAX , mais rand() peut ne renvoyer que 31 bits, ou seulement 15 bits dans Windows.

OpenSSL RAND_bytes() graine, peut-être en lisant /dev/urandom sous Linux. Si nous avons besoin de nombreux nombres aléatoires, il serait trop lent de les lire tous à partir de /dev/urandom , car ils doivent être copiés à partir du kernel. Il est plus rapide d’autoriser OpenSSL à générer plus de nombres aléatoires à partir d’une graine.

Plus sur les nombres aléatoires:

  • Perl_seed () est un exemple de calcul de graine dans C pour srand() . Il mélange les bits de l’heure actuelle, de l’ID du processus et de certains pointeurs, s’il ne peut pas lire /dev/urandom .
  • Arc4random_uniform () d’OpenBSD explique le biais modulo.
  • L’API Java pour java.util.Random décrit les algorithmes permettant de supprimer le biais des entiers aléatoires et de compacter 53 bits en flottants aléatoires.

Si votre système prend en charge la famille de fonctions arc4random , je vous recommanderais plutôt d’utiliser la fonction rand standard.

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 non signé de 32 bits.

arc4random_buf place 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 qui suit la règle: 0 <= arc4random_uniform(limit) < limit , où limit est également un entier de 32 bits non signé.

arc4random_stir lit les données de /dev/urandom et transmet les données à arc4random_addrandom pour 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 internes en fonction des données qui lui sont transmises.

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

 /* This is C, not C++ */ #include  #include  #include  #include  #include  #include  /* exit */ #include  /* 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 si elle est plus sécurisée, et elle retourne un long (facilement modifiable).

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

Si votre système n'a pas de /dev/urandom , mais a un fichier /dev/random ou similaire, vous pouvez simplement changer le chemin d'access passé pour l' 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, voire tous.

Notes: Une lecture de /dev/urandom ne bloquera PAS si le nombre d'entropie est insuffisant, de sorte que les valeurs générées dans de telles circonstances peuvent ne pas être cryptées. Si cela vous inquiète, utilisez /dev/random , qui bloquera toujours si l’entropie est insuffisante.

Si vous êtes sur un autre système (c.-à-d. Windows), utilisez alors une API non portable spécifique à la plate-forme 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; } 

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

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

Jetez un oeil à ISAAC (Indirection, Shift, Accumulate, Add, and Count). Son uniformément dissortingbué et a une longueur de cycle moyenne de 2 8295.

Vous voulez utiliser rand() . Remarque ( TRÈS IMPORTANT ): assurez-vous de définir la graine pour la fonction rand. Si vous ne le faites pas, vos nombres aléatoires ne 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 timer du système et de la date pour obtenir une bonne graine.

FWIW, la réponse est que oui, il y a une fonction stdlib.h appelée rand ; Cette fonction est principalement conçue pour la vitesse et la dissortingbution, et non pour l’imprévisibilité. Presque toutes les fonctions aléatoires intégrées pour divers 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. Celles-ci doivent être utilisées dans toutes les applications liées à la sécurité.

Ceci est, espérons-le, un peu plus aléatoire que l’utilisation de srand(time(NULL)) .

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

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

 int rand(void); void srand(unsigned seed); 

Celles-ci font toutes deux partie d’ANSI C. Il y a aussi la fonction random() :

 long random(void); 

Mais 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 probabilité de générer un nombre.

C’est un bon moyen d’obtenir un nombre aléatoire entre deux numéros de votre choix.

 #include  #include  #include  #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 modifier les valeurs après randnum en fonction des nombres que vous choisissez, et cela générera un nombre aléatoire pour vous entre ces deux nombres.

C Programme pour générer un nombre aléatoire entre 9 et 50

 #include  #include  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

ie lowerLimit est inclusif ou dire r ∈ [lowerLimit, upperLimit]

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

Vous pouvez également attraper un nombre aléatoire à partir de n’importe quel service en ligne comme random.org.

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

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

Exemple de code:

 #include  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 

Entendre une bonne explication de la raison pour laquelle l’utilisation de rand() pour produire des nombres aléatoires uniformément dissortingbués dans une plage donnée est une mauvaise idée, j’ai décidé de regarder à quel point la sortie est réellement biaisée. Mon cas de test était juste lancer des dés. Voici le code C:

 #include  #include  #include  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 si vos nombres aléatoires sont uniformes, mais ce qui précède semble assez uniforme pour la plupart des besoins.

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

J’ai eu un sérieux problème avec le générateur de nombres pseudo-aléatoires dans ma récente application: j’ai appelé mon programme C via un script pyhton et j’utilisais le code suivant:

 srand(time(NULL)) 

Cependant, depuis:

  • rand générera la même séquence pseudo-aléatoire en donnant la même graine dans srand (voir man srand );
  • Comme indiqué précédemment, la fonction de temps ne change que de seconde en seconde: si votre application est exécutée plusieurs fois dans la même seconde, le 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. mélanger le temps de sortie avec d’autres informations changeant sur les exécutions (dans mon application, le nom de la sortie):

     srand(time(NULL) | getHashOfSsortingng(outputName)) 

    J’ai utilisé djb2 comme fonction de hachage.

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

     #include struct timespec nanos; clock_gettime(CLOCK_MONOTONIC, &nanos) srand(nanos.tv_nsec); 
  3. Utilisez les deux méthodes ensemble:

     #include struct timespec nanos; clock_gettime(CLOCK_MONOTONIC, &nanos) srand(nanos.tv_nsec | getHashOfSsortingng(outputName)); 

L’option 3 vous assure (pour autant que je sache) la meilleure randomité de graine, mais cela peut créer une différence uniquement pour une application très rapide. A mon avis, l’option 2 est une valeur sûre.

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

Les versions de rand() et de srand() dans la bibliothèque C Linux utilisent le même générateur de nombres random(3) que random(3) et srandom(3) , donc les bits de poids faible doivent être aussi aléatoires que les bits de poids fort. Cependant, sur les anciennes implémentations de rand () et sur les implémentations actuelles sur différents systèmes, les bits de poids faible sont beaucoup moins aléatoires que les bits de poids fort . N’utilisez pas cette fonction dans des applications destinées à être portables lorsque le bon caractère aléatoire est requirejs. ( Utilisez plutôt random(3) . )

En ce qui concerne la portabilité, random() est également défini par le standard POSIX depuis un certain temps. rand() est plus ancien, il est déjà apparu 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), mais la norme POSIX actuelle est déjà POSIX.1-2008 (norme IEEE 1003.1-2008), qui a été mise à jour il y a tout juste un an (norme IEEE 1003.1-2008, édition 2016). Donc, je considère que random() est 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 sur 48 bits.

Et la source pseudo-aléatoire est la fonction arc4random() disponible sur de nombreux systèmes. Ne fait partie d’aucune norme officielle, est apparu dans BSD vers 1997, mais vous pouvez le trouver sur des systèmes comme Linux et macOS / iOS.

Ma solution minimaliste devrait fonctionner pour des nombres aléatoires dans l’intervalle [min, max) . Utilisez srand(time(NULL)) avant d’appeler 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)); } 

Essayez ceci, je le rassemble à 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; }