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


Réponses:


663

Remarque : ne pas utiliser rand()pour la sécurité. Si vous avez besoin d'un numéro cryptographiquement sécurisé, consultez plutôt cette réponse .

#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 aléatoire et aléatoire .


194
+1 pour plus de simplicité, mais c'est probablement une bonne idée de souligner que srand () ne doit être appelé qu'une seule fois . En outre, dans une application filetée, vous souhaiterez peut-être vous assurer que l'état du générateur est stocké par thread et amorcer le générateur une fois pour chaque thread.
RBerteig

41
@trusktr, c'est compliqué. Voici une raison: time()ne change qu'une fois par seconde. Si vous amorcez time(), pour chaque appel vers rand(), vous obtiendrez la même valeur pour chaque appel pendant une seule seconde. Mais la raison principale est que les propriétés rand()et les fonctions de ce type sont mieux connues pour le cas d'utilisation où elles sont introduites exactement une fois par exécution, et non à chaque appel. Dépendre du "caractère aléatoire" avec des propriétés non testées ou non prouvées conduit à des problèmes.
RBerteig

9
@trusktr pour un simple générateur congruentiel linéaire (ce qui est rand()généralement le cas) avec l'ensemencement rand()n'aurait au mieux aucun effet, et au pire romprait les qualités connues du générateur. C'est un sujet profond. Commencez par lire Knuth Vol 2 Chapitre 3 sur les nombres aléatoires comme la meilleure introduction aux mathématiques et aux pièges.
RBerteig

21
Évitez un avertissement du compilateur avec un casting:srand((unsigned int)time(NULL));
GiovaMaster

9
Gardez à l'esprit que c'est toujours une façon faible de voir le PRNG. L'année dernière, un virus de type cryptolocker sur Linux a fait l'erreur d'ensemencer avec le temps, ce qui a considérablement réduit l'espace de recherche. Tout ce que vous aviez à faire était de vous faire une idée décente du moment où l'infection s'est produite, puis d'essayer des graines à cette époque. La dernière fois que j'ai entendu, la meilleure source d'aléatoire est / dev / urandom, qui est censée provenir d'un mélange de sources chaotiques comme les températures sur le matériel. Si tout ce que vous voulez vraiment, cependant, c'est que votre programme agisse différemment à chaque exécution, la solution ci-dessus est très bien.
Jemenake

238

La rand()fonction dans <stdlib.h>renvoie un entier pseudo-aléatoire 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 annule 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;
  }
}

18
C'est une pratique courante , mais pas la bonne. Voir ceci et cela .
Lazer

35
@Lazer: C'est pourquoi j'ai dit "mais gardez à l'esprit que cela annule quelque peu l'uniformité".
Laurence Gonsalves du

3
@AbhimanyuAryan Le %est l'opérateur de module. Il vous donne le reste d'une division entière, x % nvous donnera donc toujours un nombre compris entre0 et n - 1(tant que xet nsont tous deux positifs). Si vous trouvez toujours cela déroutant, essayez d'écrire un programme qui icompte de 0 à 100 et imprime i % npour certains nde votre choix moins de 100.
Laurence Gonsalves

2
@necromancer Je suis allé de l'avant et j'ai ajouté une solution parfaitement uniforme.
Laurence Gonsalves

2
@Lazer, le deuxième lien que vous avez publié n'est toujours pas parfaitement uniforme. Lancer en double et en arrière n'aide pas. Le premier lien que vous avez publié a une solution parfaitement uniforme, bien qu'il boucle beaucoup pour les petites limites supérieures. J'ai ajouté une solution parfaitement uniforme à cette réponse qui ne devrait pas autant boucler même pour les petites limites supérieures.
Laurence Gonsalves

53

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

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

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.


libsodium RNG doit-il être amorcé avant d'appeler randombytes_buf?
user2199593

Il suffit d'appeler sodium_init()à un moment donné. Ne vous inquiétez pas pour le RNG, il utilise celui du noyau.
Scott Arciszewski

Remarque: j'ai approuvé la modification récente sodium_init()même si elle ne fait pas nécessairement partie de mon exemple car c'est un détail important.
Scott Arciszewski

29

Passons 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 toujours. Nous le faisons 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;
}

12
Nice Code, mais ce n'est pas une bonne idée d'appeler 'srand (time (NULL));'. cette méthode produit le même nombre lorsqu'elle est appelée dans une boucle for.
RayOldProf

1
Les modifications suggérées impliquant du code sont souvent rejetées. Quelqu'un en a fait un ici avec le commentaire "l'algorithme était faux. Pourrait produire des nombres plus grands que le maximum". Je n'ai pas évalué la réclamation moi-même.
Martin Smith

1
@Martin Smith Problèmes: 1) devrait être else{ low_num=max_num; hi_num=min_num+1;2) échoue quand hi_num - low_num > INT_MAX. 3) Omet des valeurs dans la situation rare INT_MAX > hi_num - low_num > RAND_MAX.
chux

1
Si vous la réintroduisez comme ceci, cette fonction produira le même nombre si elle est appelée plusieurs fois dans la même seconde. Si vous voulez vraiment le réensemencer, alors ne réensemencez qu'une fois par seconde.
Élektra

1
Mineure: hi_num = max_num + 1;manque de protection contre le débordement.
chux

25

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


2
+1: Ça a l'air cool mais je faisais juste un jeu de devinettes. Si j'allais utiliser un générateur de nombres aléatoires dans une application métier, j'utiliserais certainement cela.
Kredns

4
N'utilisez pas de Mersenne Twister, utilisez quelque chose de bon comme xoroshiro128 + ou PCG. (Lien pertinent.)
Veedrac

17

La fonction C standard est rand(). C'est assez bon pour distribuer des cartes pour le solitaire, mais c'est horrible. De nombreuses implémentations font rand()défiler une courte liste de nombres, et les bits bas ont des cycles plus courts. La façon dont certains programmes appellent rand()est horrible, et calculer une bonne graine à 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. 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 nombres entiers ou flottants aléatoires. OpenSSL ne donne que des octets aléatoires, donc j'essaie d'imiter comment Java ou Ruby les transformerait en nombres entiers ou flottants.

Pour les entiers, nous voulons éviter le biais modulo . Supposons que nous ayons obtenu des nombres entiers aléatoires à 4 chiffres rand() % 10000, mais rand()ne pouvons renvoyer que 0 à 32767 (comme c'est le cas dans Microsoft Windows). Chaque nombre de 0 à 2767 apparaîtra plus souvent que chaque nombre de 2768 à 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 correspondent uniformément aux 10000 valeurs de 0 à 9999.

Pour les flottants, nous voulons 53 bits aléatoires, car a doublecontient 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'arrondi. Certains programmeurs écrivent du code comme rand() / (double)RAND_MAX, mais rand()peuvent renvoyer seulement 31 bits, ou seulement 15 bits sous Windows.

Les RAND_bytes()graines d'OpenSSL lui-même, peut-être en lisant /dev/urandomsous Linux. Si nous avons besoin de nombreux nombres aléatoires, il serait trop lent de les lire tous /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.

En savoir plus sur les nombres aléatoires:

  • Perl_seed () de Perl est un exemple de la façon de calculer une graine en C pour srand(). Il mélange les bits de l'heure actuelle, l'ID du processus et 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 des algorithmes pour supprimer le biais des entiers aléatoires et compresser 53 bits dans des flottants aléatoires.

Merci pour cette réponse étendue. Notez que sur les 24 réponses actuelles à cette question, vous étiez la seule à avoir une interprétation supplémentaire à traiter avec float/ double, j'ai donc clarifié la question pour vous en tenir aux intchiffres pour éviter de la rendre trop large. Il y a d'autres questions C traitant spécifiquement des valeurs float/ doublealéatoires, vous pouvez donc vouloir republier votre deuxième moitié de votre réponse à des questions telles que stackoverflow.com/questions/13408990/…
Cœur

11

Si votre système prend en charge la arc4randomfamille de fonctions, je recommanderais de les utiliser à la place de la randfonction standard .

La arc4randomfamille 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 aléatoire.

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

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

arc4random_stirlit /dev/urandomet transmet les données à arc4random_addrandompour randomiser en plus son pool de nombres aléatoires internes.

arc4random_addrandomest utilisé par arc4random_stirpour remplir son pool de nombres aléatoires internes en fonction des données qui lui sont transmises.

Si vous ne disposez pas de 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 urandom_initfonction ouvre l' /dev/urandomappareil et place le descripteur de fichier urandom_fd.

La urandomfonction est fondamentalement la même qu'un appel à rand, sauf plus sécurisée, et elle renvoie un long(facilement modifiable).

Cependant, cela /dev/urandompeut être un peu lent, il est donc recommandé de l'utiliser comme source pour un générateur de nombres aléatoires différent.

Si votre système ne possède pas de fichier /dev/urandom, mais possède un /dev/randomfichier similaire, vous pouvez simplement modifier le chemin d'accès passé à openin urandom_init. Les appels et les API utilisés dans urandom_initet urandomsont (je crois) compatibles POSIX, et en tant que tels, devraient fonctionner sur la plupart, sinon sur tous les systèmes compatibles POSIX.

Remarques: Une lecture depuis /dev/urandomne bloquera PAS s'il n'y a pas suffisamment d'entropie disponible, donc les valeurs générées dans de telles circonstances peuvent être cryptographiquement non sécurisées. Si cela vous inquiète, utilisez alors /dev/random, qui bloquera toujours si l'entropie est insuffisante.

Si vous êtes sur un autre système (c'est-à-dire Windows), utilisez randou une API non portable spécifique à la plate-forme spécifique à Windows.

Wrapper fonction pour urandom, randou des arc4randomappels:

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

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

8

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. randest POSIX, randomest une fonction de spécification BSD.

La différence entre randet randomest qu'elle randomrenvoie un nombre aléatoire 32 bits beaucoup plus utilisable et randrenvoie généralement un nombre 16 bits. Les pages de manuel BSD montrent que les bits inférieurs de randsont cycliques et prévisibles, donc randpotentiellement inutiles pour les petits nombres.


3
@Neil - puisque toutes les réponses mentionnent jusqu'à présent la STL, je soupçonne que la question a été éditée rapidement pour supprimer une référence inutile.
Michael Burr

rand () n'est pas inutile pour les petits nombres - vous pouvez les décaler en bits et utiliser uniquement les bits les plus aléatoires si vous en avez vraiment besoin.
Chris Lutz

@Chris, vous pouvez le faire si la taille du nombre aléatoire est connue, mais si la taille requise du nombre aléatoire change pendant l'exécution (comme la lecture aléatoire d'un tableau dynamique, etc.), il serait difficile de contourner une telle mise en garde.
dreamlax

Je ne trouve aucune fonction aléatoire ici :-(
kasia.b

@ kasia.b dans ce lien, il y a extern int rand(void);et extern void srand(unsigned int);.
RastaJedi

7

Jetez un œil à ISAAC (Indirection, Shift, Accumulate, Add et Count). Son uniformément distribué et a une longueur moyenne de cycle de 2 ^ 8295.


2
ISAAC est un RNG intéressant en raison de sa vitesse mais n'a pas encore reçu une attention cryptographique sérieuse.
user2398029

4

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

Sortie la deuxième fois: 61

Sortie la troisième fois: 65

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


4

Vous voulez utiliser rand(). Remarque ( TRÈS IMPORTANT ): assurez-vous de définir la valeur de départ pour la fonction rand. Sinon, 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 minuterie du système et de la date pour obtenir une bonne graine.


6
Deux points a) vos nombres aléatoires ne sont pas "vraiment" aléatoires, peu importe la façon dont vous amorcez le générateur. Et b) il est très pratique que la séquence pseudo-aléatoire soit toujours la même dans de nombreuses circonstances - pour les tests, par exemple.

16
s'il est TRÈS IMPORTANT que votre numéro soit vraiment aléatoire, vous ne devriez pas utiliser la fonction rand ().
tylerl

1
Les valeurs de rand ne sont pas du tout "vraiment" aléatoires, que vous définissiez ou non la graine. Étant donné une graine connue, la séquence est prévisible. La génération "vraiment" de nombres aléatoires est difficile. Il n'y a aucune entropie impliquée avec rand.
dreamlax

2
Bien sûr, ils le feront - le générateur est amorcé pour vous par la bibliothèque (probablement à zéro, mais c'est une graine valide).

4
Ah, mais l'algorithme connu / la graine connue est essentiel pour déboguer tout programme qui utilise des nombres aléatoires. Il n'est pas inhabituel de consigner la graine utilisée avec une simulation afin de pouvoir la recréer pour une analyse plus détaillée. Ne pas appeler srand () équivaut à appeler srand (1).
RBerteig

4

FWIW, la réponse est que oui, il existe une stdlib.hfonction appelée rand; cette fonction est réglée principalement pour la vitesse et la distribution, pas 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" qui sont beaucoup moins prévisibles, mais fonctionnent beaucoup plus lentement. Ceux-ci doivent être utilisés dans toute sorte d'application liée à la sécurité.


4

Nous espérons que c'est un peu plus aléatoire que de simplement 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());
}

1
l'ajout de srand (rand ()) n'augmente pas le caractère aléatoire de la séquence si ce programme est exécuté plusieurs fois en 1 seconde. time (NULL) renverra toujours la même valeur pour chacun d'eux, le premier rand () renverra le même long et le deuxième appel à srand () aura la même valeur, ce qui aura toujours la même séquence aléatoire. L'utilisation de l'adresse d'argc pourrait aider, seulement s'il est garanti que cette adresse sera différente à chaque exécution du programme, ce qui n'est pas toujours vrai.
theferrit32

3

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

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

long random(void);

Mais pour autant que je sache, ce 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 façon dont un nombre aléatoire doit être généré.


3

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

c'est-à-dire que lowerLimit est inclusif ou dites r ∈ [lowerLimit, upperLimit)


@Pang C'est ce que j'ai clairement mentionné ENTRE 9 et 50 et non DE 9 et 50.
Shivam K. Thakkar

2
Votre opération modulo a introduit un biais.
2017

2

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 n'importe quel service en ligne comme random.org.


1
Vous pouvez également attraper un nombre aléatoire à partir de n'importe quel service en ligne comme random.org Bounty si vous incluez un moyen portable et efficace de le faire en C.
MD XF

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

puis définissez en haut avec les variables @ b4hand
Muhammad Sadiq

Au moment où j'ai fait ce commentaire, je n'avais pas d'autorisations d'édition universelles, et généralement les changements de code qui changeaient la réponse réelle seraient rejetés. Si vous ne vous souciez pas de corriger votre réponse, je peux le faire.
b4hand

Si votre intention était de produire une permutation aléatoire de valeurs uniques, ce code contient toujours un bogue. Il produit la valeur 84 deux fois et ne produit pas la valeur 48. De plus, il n'amorce pas le générateur de nombres aléatoires, la séquence est donc la même à chaque exécution.
b4hand

1
les variables a et b sont définies dans ce code. Il est sans erreur .. il n'y a pas de syntaxe ni d'erreur logique aussi.
Muhammad Sadiq

Incorrect. J'ai déjà confirmé la sortie comme je l'ai mentionné.
b4hand

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

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

En entendant une bonne explication de pourquoi utiliser rand()pour produire des nombres aléatoires uniformément distribués dans une plage donnée est une mauvaise idée, j'ai décidé de jeter un coup d'œil à la fausseté réelle de la sortie. Mon cas de test était un lancer de dés correct. 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 vous avez besoin que vos nombres aléatoires soient uniformes, mais ce qui précède semble suffisamment 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).


rand () peut échouer à d'autres tests de hasard, tels que les tests purs et durs . rand () diffère d'une plateforme à l'autre; Les valeurs rand () de GNU / Linux peuvent être meilleures que celles de BSD ou de Windows.
George Koehler

Ce n'est pas un moyen valide de tester le caractère aléatoire.
Veedrac

Dépend de l'objectif et du modèle de menace / risque. Pour un RNG cryptographiquement solide, utilisez RDRAND (ou RDSEED). Pour un simple lanceur de dés (pas au niveau du casino) à mon humble avis, ce qui précède devrait suffire. Le mot clé est " assez bien ".
Souris

0

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é à plusieurs reprises mon programme C via un script pyhton et j'utilisais comme graine le code suivant:

srand(time(NULL))

Cependant, puisque:

  • rand générera la même séquence pseudo-aléatoire donnera la même graine dans srand (voir man srand);
  • Comme déjà indiqué, 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, timeelle renverra la même valeur à chaque fois.

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

  1. mélanger la sortie de temps 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. Augmentez la résolution temporelle. Sur ma plateforme, clock_gettimeétait disponible, donc je l'utilise:

    #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 (pour autant que je sache) la meilleure randomité des graines, mais elle peut créer une différence uniquement sur une application très rapide. À mon avis, l'option 2 est une valeur sûre.


Même avec ces heuristiques, ne comptez pas sur rand () pour les données cryptographiques.
domenukk

rand()ne doit pas être utilisé pour des données cryptographiques, je suis d'accord. Au moins pour moi, mon application n'impliquait pas de données cryptographiques, donc pour moi, c'était correct la méthode donnée.
Koldar

0

Malgré toutes les suggestions des gens rand()ici, vous ne voulez pas l'utiliser rand()sauf si vous le devez! Les nombres aléatoires qui rand()produisent 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), donc les bits de poids faible doivent être aussi aléatoires que les bits de poids fort. Cependant, sur les anciennes implémentations rand () et sur les implémentations actuelles sur différents systèmes, les bits d'ordre inférieur sont beaucoup moins aléatoires que les bits d'ordre supérieur . N'utilisez pas cette fonction dans des applications destinées à être portables lorsqu'un bon caractère aléatoire est nécessaire. ( Utilisez à la random(3)place. )

En ce qui concerne la portabilité, random()est également défini par la norme POSIX depuis un certain temps maintenant. rand()est plus ancien, il figurait déjà dans la première spécification POSIX.1 (IEEE Std 1003.1-1988), alors qu'il random()est apparu pour la première fois dans POSIX.1-2001 (IEEE Std 1003.1-2001), mais le standard POSIX actuel 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). Je considère random()donc que c'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 à l'aide d'un algorithme congruentiel linéaire et d'une arithmétique entière sur 48 bits.

Et la très bonne source pseudo aléatoire est la arc4random()fonction 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.


random()n'existe pas sous Windows.
Björn Lindqvist

@ BjörnLindqvist Windows n'est pas non plus un système POSIX; c'est à peu près le seul système sur le marché qui ne prend pas en charge au moins les API POSIX de base (qui prennent même en charge des systèmes verrouillés comme iOS). Windows prend uniquement en charge rand()car il est également requis par la norme C. Pour toute autre chose, vous avez besoin d'une solution spéciale pour Windows uniquement, comme d'habitude. #ifdef _WIN32est la phrase que vous verrez le plus souvent dans le code multiplateforme qui veut prendre en charge Windows.En général, il existe une solution qui fonctionne avec tous les systèmes et une qui est requise pour Windows uniquement.
Mecki

0

Pour les applications Linux C:

Il s'agit de mon code retravaillé à partir d'une réponse ci-dessus qui suit mes pratiques de code C et renvoie un tampon aléatoire de toute taille (avec des codes de retour appropriés, etc.). Assurez-vous d'appeler urandom_open()une fois au début de votre programme.

int gUrandomFd = -1;

int urandom_open(void)
{
    if (gUrandomFd == -1) {
        gUrandomFd = open("/dev/urandom", O_RDONLY);
    }

    if (gUrandomFd == -1) {
        fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
                  errno, strerror(errno));
        return -1;
    } else {
        return 0;
    }
}


void urandom_close(void)
{
    close(gUrandomFd);
    gUrandomFd = -1;
}


//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
    int ret = 0; // Return value

    if (gUrandomFd == -1) {
        fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
        return -1;
    }

    ret = read(gUrandomFd, buf, size);

    if (ret != size) {
        fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
                 ret, size);
        return -1;
    } else {
        return 0;
    }
}

-2

Ma solution minimaliste devrait fonctionner pour des nombres aléatoires dans la plage [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));
} 

-3

Essayez ceci, je l'ai rassemblé à partir de certains des concepts déjà référencé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;
}

16
Ce code n'est pas bon. Appeler srand()chaque fois que vous voulez appeler rand()est une idée terrible. Étant donné time()que renvoie généralement une valeur en secondes, l' appel rapide de cette fonction renvoie la même valeur "aléatoire".
Blastfurnace

3
Cette fonction se confondrait avec la random()fonction d'Unix .
George Koehler
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.