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?
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 .
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;
}
}
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:
randombytes
de libsodium/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.
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;
}
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 .
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:
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
.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;
}
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.
Examinez ISAAC (Indirection, Maj, Accumuler, Ajouter et Compter). Il est uniformément distribué et a une durée moyenne de cycle de 2 ^ 8295.
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.
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é.
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());
}
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.
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)
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.
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.
#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]);
}
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
#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;
}
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:
man srand
);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:
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.
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);
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.
#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;
}
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()
etsrand()
dans la bibliothèque Linux C utilisent le même générateur de nombres aléatoires querandom(3)
etsrandom(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. (Utilisezrandom(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.
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)
.