Comment générer un int aléatoire en C?
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
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;
}
}
si vous avez besoin de caractères aléatoires ou entiers sécurisés:
Comme il en est question dans la manière sécuritaire de générer des nombres aléatoires dans divers langages de programmation , vous aurez envie de faire une des actions suivantes:
- Use libsodium 's
randombytes
API - Re-mettre en œuvre ce que vous avez besoin de libsodium de sysrandom mise en œuvre vous-même, très soigneusement
- plus largement, utiliser
/dev/urandom
151990920" , pas/dev/random
. Non OpenSSL (ou autres userspace PRNGs).
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 cryptographique sécurisé et impartiale.
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;
}
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 .
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.
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;
}
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.
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.
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é.
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());
}
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.
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.
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]
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.
#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]);
}
#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;
}
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
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)
.
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:
-
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.
-
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);
-
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.
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()
etsrand()
de la bibliothèque C de Linux utilisent le même générateur de nombres aléatoires querandom(3)
etsrandom(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. ( utilisezrandom(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.
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));
}
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;
}