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


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


453
2018-05-04 22:07


origine


Réponses:


Remarque: Ne pas utiliser rand() pour la sécurité. Si vous avez besoin d'un numéro sécurisé cryptographiquement, voir cette réponse au lieu.

#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

551
2018-05-04 22:18



le rand() fonctionner dans <stdlib.h> retourne un nombre entier pseudo-aléatoire entre 0 et RAND_MAX. Vous pouvez utiliser srand(unsigned int seed) mettre une graine.

C'est une pratique courante d'utiliser le % opérateur en conjonction avec rand() pour obtenir une gamme différente (mais gardez à l'esprit que cela jette quelque peu l'uniformité). Par exemple:

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

Si vous vraiment se soucient de l'uniformité, vous pouvez faire quelque chose comme ça:

/* 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;
  }
}

201
2018-05-04 22:17



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

Comme indiqué dans comment générer en toute sécurité des nombres aléatoires dans divers langages de programmation, vous voulez faire l'une des choses 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.


37
2017-09-13 17:16



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


24
2018-05-31 08:14



Permet de passer par là. Nous utilisons d'abord la fonction srand () pour graver le randomizer. Fondamentalement, l'ordinateur peut générer des nombres aléatoires basés sur le nombre qui est 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 <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;
}

24
2018-01-30 07:41



La fonction standard C est rand(). C'est assez bon pour traiter des cartes pour le solitaire, mais c'est affreux. De nombreuses implémentations de rand() faire 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 en calculant 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 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 polarisation modulo. Supposons que nous ayons des nombres entiers aléatoires à 4 chiffres rand() % 10000, mais rand() ne peut que renvoyer 0 à 32767 (comme dans Microsoft Windows). Chaque numéro de 0 à 2767 apparaîtrait 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, parce que les valeurs 30000 de 2768 à 32767 correspondent uniformément aux 10000 valeurs de 0 à 9999.

Pour les flotteurs, nous voulons 53 bits aléatoires, car un double détient 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() pourrait renvoyer seulement 31 bits, ou seulement 15 bits dans Windows.

OpenSSL RAND_bytes() graines lui-même, 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, parce qu'ils doivent être copiés du noyau. 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 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.
  • API Java pour java.util.Random décrit des algorithmes permettant de supprimer le biais des nombres entiers aléatoires et de regrouper 53 bits en flottants aléatoires.

13
2017-07-08 01:34



Si votre système prend en charge le arc4random famille de fonctions Je recommanderais d'utiliser ces plutôt la norme rand fonction.

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

arc4random_stir lit les données de /dev/urandom et transmet les données à arc4random_addrandom de plus randomiser son pool de nombres aléatoires interne.

arc4random_addrandom est utilisé par arc4random_stir pour remplir son pool de nombres aléatoires internes en fonction des données qui lui ont été 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 <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;
}

le urandom_init la fonction ouvre le /dev/urandom périphérique, et met le descripteur de fichier dans urandom_fd.

le urandom la fonction est fondamentalement la même qu'un appel à rand, sauf plus sûr, et il 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 Est-ce que avoir 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) compatibles avec POSIX, et en tant que tels, devraient fonctionner sur la plupart, sinon tous les systèmes compatibles POSIX.

Notes: Une lecture de /dev/urandom ne bloquera pas si l'entropie est insuffisante, de sorte que les valeurs générées dans de telles circonstances peuvent ne pas être cryptographiquement sécurisées. Si vous êtes inquiet à ce sujet, alors utilisez /dev/random, qui bloquera toujours si l'entropie est insuffisante.

Si vous êtes sur un autre système (Windows), utilisez rand ou une API interne non portable spécifique à Windows, dépendante de la plate-forme.

Wrapper fonction 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;
}

9
2017-08-08 22:38