116 votes

Comment générer un nombre aléatoire à partir de l'intérieur d'une fourchette

C'est un suivi à partir d'un déjà posté la question:

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

Je veux être en mesure de générer un nombre aléatoire à partir de l'intérieur d'une gamme donnée, comme 1 à 6 pour imiter les faces d'un dé.

Comment pourrais-je aller sur le faire?

177voto

Ryan Reich Points 651

Toutes les réponses sont mathématiquement faux. De retour rand() % N n'est pas uniformément donner un nombre dans l'intervalle [0, N) moins N divise la longueur de l'intervalle dans lequel rand() retours (c'est à dire une puissance de 2). En outre, on n'a aucune idée de savoir si les modules d' rand() sont indépendants: il est possible qu'ils vont 0, 1, 2, ..., ce qui est uniforme, mais pas très aléatoire. La seule hypothèse, il semble raisonnable de faire est qu' rand() met une distribution de Poisson: tous les deux ne se chevauchent pas les sous-intervalles de même taille sont équiprobables et indépendants. Pour un ensemble fini de valeurs, ce qui implique une distribution uniforme et s'assure également que les valeurs de rand() sont bien dispersés.

Cela signifie que la seule façon correcte de l'évolution de la gamme de rand() est de le diviser en boîtes; par exemple, si RAND_MAX == 11 et que vous voulez une gamme d' 1..6, vous devez attribuer {0,1} 1, {2,3} à 2, et ainsi de suite. Ce sont disjoints, de même taille intervalles et sont donc uniformément et indépendamment distribués.

La proposition de recourir à la division flottante est mathématiquement plausible, mais souffre de arrondissement des questions de principe. Peut - double est élevé suffisamment de précision pour le faire fonctionner; peut-être pas. Je ne sais pas et je ne veux pas le comprendre; en tout cas, la réponse est dépendant du système.

La bonne façon est d'utiliser l'arithmétique des nombres entiers. Qui est, vous voulez quelque chose comme ce qui suit:

#include <stdlib.h> // For random(), RAND_MAX

// Assumes 0 <= range <= RAND_MAX
// Returns in the half-open interval [0, max]
long random_at_most(long max) {
  unsigned long
    // max <= RAND_MAX < ULONG_MAX, so this is okay.
    num_bins = (unsigned long) max + 1,
    num_rand = (unsigned long) RAND_MAX + 1,
    bin_size = num_rand / num_bins,
    defect   = num_rand % bin_size;

  long x;
  // This is carefully written not to overflow
  while (num_rand - defect <= (unsigned long)(x = random()));

  // Truncated division is intentional
  return x/bin_size;
}

La boucle est nécessaire pour obtenir une parfaite uniformité de la distribution. Par exemple, si vous êtes donné des nombres aléatoires de 0 à 2 et que vous voulez seulement ceux de 0 à 1, vous continuez à tirer jusqu'à ce que vous n'obtenez pas un 2; il n'est pas difficile de vérifier que cela donne 0 ou 1 avec une probabilité égale. Cette méthode est également décrite dans le lien que la nsa a donné dans leur réponse, bien codé différemment. Je suis à l'aide d' random() plutôt que d' rand() comme il a une meilleure distribution (comme l'a noté la page de man pour rand()).

Si vous souhaitez obtenir des valeurs aléatoires à l'extérieur de la plage par défaut [0, RAND_MAX], alors vous devez faire quelque chose de délicat. Peut-être le moyen le plus efficace est de définir une fonction random_extended() qui tire n bits (à l'aide d' random_at_most()) et des rendements en [0, 2**n), puis appliquez random_at_most() avec random_extended() à la place de random() (et 2**n - 1 à la place de RAND_MAX) pour tirer une valeur aléatoire à moins de 2**n, en supposant que vous avez un type numérique qui peut contenir une valeur de ce type. Enfin, bien sûr, vous pouvez obtenir les valeurs en [min, max] l'aide min + random_at_most(max - min + 1), y compris les valeurs négatives.

35voto

theJPster Points 121

A la suite de @Ryan Reich réponse, j'ai pensé que je voudrais vous offrir mes nettoyé version. La première vérification de limites n'est pas nécessaire compte tenu de la deuxième vérification de limites, et je l'ai fait itératif plutôt que récursive.

unsigned int rand_interval(unsigned int min, unsigned int max)
{
    int r;
    const unsigned int range = max - min;
    const unsigned int buckets = RAND_MAX / range;
    const unsigned int limit = buckets * range;

    /* Create equal size buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    do
    {
        r = rand();
    } while (r >= limit);

    return min + (r / buckets);
}

22voto

Sattar Points 71

Voici une formule si vous connaissez les valeurs max et min d'une plage, et vous voulez générer des nombres inclusif entre la gamme:

r = (rand() % (max+1-min))+min

17voto

nos Points 102226
unsigned int
randr(unsigned int min, unsigned int max)
{
       double scaled = (double)rand()/RAND_MAX;

       return (max - min +1)*scaled + min;
}

Voir ici pour d'autres options.

12voto

Armstrongest Points 6450

Ne serait-il pas faire:

srand(time(NULL));
int r = ( rand() % 6 ) + 1;

% est l'opérateur de modulo. Essentiellement, il suffit de diviser par 6 et renvoyer le reste... à partir de 0 - 5

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X