39 votes

Comment puis-je réduire les nombres renvoyés par rand() ?

Le code suivant affiche un nombre aléatoire chaque seconde :

int main ()
{
    srand(time(NULL)); // Sème le générateur de nombres avec l'heure d'exécution.

    while (true)
    {
        int rawRand = rand();

        std::cout << rawRand % 101 << std::endl; 

        sleep(1);
    }
}

Comment pourrais-je réduire la taille de ces nombres pour qu'ils soient toujours dans la plage de 0 à 100 ?

29 votes

Int GetRandom() { return 59; /*Un nombre parfaitement choisi au hasard*/}

5 votes

Serait-ce une référence à xkcd que je vois? :P

1 votes

Naah, je l'ai vu dans beaucoup trop d'endroits pour dire la source exacte, et de pouvoir m'abstenir d'écrire cela.

84voto

Blastfurnace Points 8160

Si vous utilisez C++ et que vous vous souciez d'une bonne distribution, vous pouvez utiliser TR1 C++11 .

#include 

std::random_device rseed;
std::mt19937 rgen(rseed()); // mersenne_twister
std::uniform_int_distribution idist(0,100); // [0,100]

std::cout << idist(rgen) << std::endl;

21 votes

Tandis que c'est la bonne façon d'obtenir une distribution uniforme de nombres aléatoires, cela ne répond pas à la question de MaxPM qui ne parle pas de l'obtention d'une bonne distribution mais demande "Comment réduire à l'échelle des nombres de rand()".

0 votes

random_device ne fonctionnera pas toujours : il renvoie 34992116121 à chaque fois dans mon cas.

1 votes

@AbcAeffchen: C'est malheureux, quelle version/compiler utilisez-vous? Vous pourriez rencontrer le même problème que cette autre question SO.

32voto

Konrad Rudolph Points 231505

Tous les exemples postés jusqu'à maintenant donnent en réalité des résultats mal répartis. Exécutez souvent le code et créez une statistique pour voir comment les valeurs deviennent biaisées.

Une meilleure façon de générer une distribution de nombres aléatoires réellement uniforme dans n'importe quelle plage [0, N] est la suivante (en supposant que rand suit effectivement une distribution uniforme, ce qui est loin d'être évident) :

unsigned result; 
do {
    result = rand();
} while (result > N);

Bien sûr, cette méthode est lente mais elle produit une bonne distribution. Une manière légèrement plus intelligente de procéder est de trouver le plus grand multiple de N qui est plus petit que RAND_MAX et d'utiliser cela comme limite supérieure. Ensuite, on peut tranquillement prendre le result % (N + 1).

Pour une explication pourquoi la méthode du modulo naïve est mauvaise et pourquoi la méthode ci-dessus est meilleure, consultez l'excellent article de Julienne sur l'utilisation de rand.

1 votes

En fait, il est généralement admis qu'un générateur de nombres pseudo-aléatoires produit des nombres uniformément distribués. Une méthode légèrement plus judicieuse peut être trouvée dans java.util.Random#nextInt(int) par exemple.

3 votes

Vous pouvez facilement faire beaucoup mieux en faisant while(result > (RAND_MAX - RAND_MAX % N)) et ensuite en divisant par RAND_MAX/N. Vous jetez beaucoup moins de nombres pour un petit N mais conservez la distribution uniforme.

0 votes

@Joey : La méthode Java est incroyablement intelligente mais assez difficile à expliquer et je ne m'attendrais pas à ce qu'un programmeur débutant (ou même intermédiaire) sans connaissances spéciales en statistiques la comprenne. Je pense que ma méthode, tout en donnant une distribution correcte, reste assez facile à expliquer et à mettre en œuvre.

27voto

Justin Niessner Points 144953

~~int rawRand = rand() % 101;

Voir (pour plus de détails) :

rand - C++ Référence

D'autres ont également souligné que cela ne vous donnera pas la meilleure distribution possible de nombres aléatoires. Si ce genre de chose est important dans votre code, vous devriez faire :

int rawRand = (rand() * 1.0 / RAND_MAX) * 100;~~ 

ÉDITION

Trois ans plus tard, je fais une édition. Comme d'autres l'ont mentionné, rand() pose de nombreux problèmes. Évidemment, je ne peux pas recommander son utilisation alors qu'il existe de meilleures alternatives à l'avenir. Vous pouvez en savoir plus sur les détails et les recommandations ici :

rand() Considéré Comme Nocif | GoingNative 2013

21 votes

Veuillez ne pas utiliser cette méthode en pratique - c'est mauvais.

1 votes

Notez que vous obtiendrez une distribution légèrement inégale de cette façon. Les nombres inférieurs se produisent un peu plus souvent de cette manière. Pour une bonne manière de résoudre ce problème, jetez un oeil à java.util.Random#nextInt(int).

4 votes

Comme je l'ai dit précédemment, utiliser la méthode modulo n'est pas tout à fait un hasard parfait. 100 nombres, et uint a 648 plages complètes de 0 à 100, et une plage de 0 à 87. Les chiffres de 0 à 87 ont donc légèrement plus de chances de se produire que les chiffres de 88 à 100.

7voto

dr jimbob Points 6876

Vous pouvez faire

cout << rawRand % 100 << endl; // Sorties entre 0 et 99

cout << rawRand % 101 << endl; // sorties entre 0 et 100

Pour les personnes qui votent négativement; notez qu'une minute après la publication initiale, j'ai laissé le commentaire:

Depuis http://www.cplusplus.com/reference/clibrary/cstdlib/rand "Remarquez cependant que cette opération de modulo ne génère pas un nombre aléatoire véritablement uniformément réparti dans l'étendue (car dans la plupart des cas, les nombres plus bas sont légèrement plus probables), mais c'est généralement une bonne approximation pour de courtes plages".

Avec des entiers sur 64 bits et en utilisant 100 nombres en sortie, les nombres de 0 à 16 sont représentés avec 1.00000000000000000455 % des nombres (une précision relative d'environ 1%, de l'ordre de 10-18), tandis que les nombres de 17 à 99 sont représentés avec 0.99999999999999999913 % des nombres. Oui, ce n'est pas parfaitement réparti, mais une très bonne approximation pour de petites plages.

Remarquez également, où est-ce que l'OP demande des nombres uniformément distribués? Pour tout ce que nous savons, ils sont utilisés à des fins où de petites déviations n'ont pas d'importance (par ex., autre chose que la cryptographie -- et s'ils utilisent les nombres pour la cryptographie, cette question est bien trop naïve pour qu'ils écrivent leur propre cryptographie).

ÉDIT - Pour les personnes qui se soucient vraiment d'avoir une distribution uniforme de nombres aléatoires, le code suivant fonctionne. Notez que ce n'est pas nécessairement optimal car avec des entiers aléatoires sur 64 bits, il faudra deux appels à rand() une fois tous les 10^18 appels.

unsigned N = 100; // veut des nombres de 0 à 99
unsigned long randTruncation = (RAND_MAX / N) * N; 
// inclure chaque nombre les N fois en s'assurant que rawRand est entre 0 et randTruncation - 1 ou le régénérer.
unsigned long rawRand = rand();

while (rawRand >= randTruncation) {
    rawRand = rand();  
// avec un entier de 64 bits et une plage de 0 à 99, il sera nécessaire de générer deux nombres aléatoires
// environ 1 fois tous les (2^63)/16 ~ 10^18 fois (1 million million de fois)

// avec un entier de 32 bits et une plage de 0 à 99, il sera nécessaire de générer deux nombres aléatoires 
// une fois tous les 46 millions de fois.

}
cout << rawRand % N << stdl::endl;

2 votes

À partir de cplusplus.com/reference/clibrary/cstdlib/rand "Noter cependant que cette opération modulo ne génère pas un nombre aléatoire réellement uniformément distribué dans l'étendue (puisque dans la plupart des cas, les nombres plus bas sont légèrement plus probables), mais c'est généralement une bonne approximation pour de courtes étendues."

4voto

Dirk Eddelbuettel Points 134700

Voir man 3 rand -- vous devez mettre à l'échelle en divisant par RAND_MAX pour obtenir la plage [0, 1] après quoi vous pouvez multiplier par 100 pour obtenir votre plage cible.

0 votes

Intéressant. Ce méthode a-t-elle des avantages par rapport à la méthode du modulo ?

0 votes

Il produit des valeurs statistiquement "meilleures" que l'utilisation du modulo.

3 votes

Eh bien, cela dépend de la médiocrité de rand() au départ. C'est généralement assez médiocre, cependant.

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