La solution de Mark (la solution acceptée) est presque parfaite.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
édité le 25 mars 16 à 23:16
Mark Amery 39k21170211
Cependant, il a une mise en garde qui rejette 1 ensemble valide de résultats dans tout scénario où RAND_MAX
( RM
) est 1 de moins qu'un multiple de N
(Où N
= le nombre de résultats valides possibles).
c'est-à-dire, lorsque le «nombre de valeurs rejetées» ( D
) est égal à N
, alors il s'agit en fait d'un ensemble valide ( V)
, et non d'un ensemble non valide ( I
).
Ce qui en est la cause à un moment donné, Mark perd de vue la différence entre N
et Rand_Max
.
N
est un ensemble dont les membres valides sont composés uniquement d'entiers positifs, car il contient un nombre de réponses qui seraient valides. (par exemple: Set N
= {1, 2, 3, ... n }
)
Rand_max
Cependant, il s'agit d'un ensemble qui (tel que défini pour nos besoins) comprend un nombre quelconque d'entiers non négatifs.
Dans sa forme la plus générique, ce qui est défini ici Rand Max
est l'ensemble de tous les résultats valides, qui pourrait théoriquement inclure des nombres négatifs ou des valeurs non numériques.
Par conséquent, Rand_Max
est mieux défini comme l'ensemble des «réponses possibles».
Cependant, il N
opère par rapport au nombre de valeurs dans l'ensemble des réponses valides, donc même tel que défini dans notre cas spécifique, il y Rand_Max
aura une valeur inférieure au nombre total qu'il contient.
En utilisant la solution de Mark, les valeurs sont rejetées lorsque: X => RM - RM% N
EG:
Ran Max Value (RM) = 255
Valid Outcome (N) = 4
When X => 252, Discarded values for X are: 252, 253, 254, 255
So, if Random Value Selected (X) = {252, 253, 254, 255}
Number of discarded Values (I) = RM % N + 1 == N
IE:
I = RM % N + 1
I = 255 % 4 + 1
I = 3 + 1
I = 4
X => ( RM - RM % N )
255 => (255 - 255 % 4)
255 => (255 - 3)
255 => (252)
Discard Returns $True
Comme vous pouvez le voir dans l'exemple ci-dessus, lorsque la valeur de X (le nombre aléatoire que nous obtenons de la fonction initiale) est 252, 253, 254 ou 255, nous la rejetons même si ces quatre valeurs comprennent un ensemble valide de valeurs renvoyées .
IE: lorsque le nombre de valeurs rejetées (I) = N (le nombre de résultats valides), un ensemble valide de valeurs de retour sera rejeté par la fonction d'origine.
Si nous décrivons la différence entre les valeurs N et RM comme D, c'est-à-dire:
D = (RM - N)
Puis, à mesure que la valeur de D diminue, le pourcentage de relances inutiles dues à cette méthode augmente à chaque multiplicatif naturel. (Lorsque RAND_MAX n'est PAS égal à un nombre premier, cela est une préoccupation valable)
PAR EXEMPLE:
RM=255 , N=2 Then: D = 253, Lost percentage = 0.78125%
RM=255 , N=4 Then: D = 251, Lost percentage = 1.5625%
RM=255 , N=8 Then: D = 247, Lost percentage = 3.125%
RM=255 , N=16 Then: D = 239, Lost percentage = 6.25%
RM=255 , N=32 Then: D = 223, Lost percentage = 12.5%
RM=255 , N=64 Then: D = 191, Lost percentage = 25%
RM=255 , N= 128 Then D = 127, Lost percentage = 50%
Étant donné que le pourcentage de relances nécessaires augmente à mesure que N se rapproche de RM, cela peut être une préoccupation valable pour de nombreuses valeurs différentes en fonction des contraintes du système exécutant le code et des valeurs recherchées.
Pour annuler cela, nous pouvons apporter un amendement simple, comme indiqué ici:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
Cela fournit une version plus générale de la formule qui tient compte des particularités supplémentaires de l'utilisation du module pour définir vos valeurs maximales.
Exemples d'utilisation d'une petite valeur pour RAND_MAX qui est un multiplicatif de N.
Version Mark'original:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X >= (RAND_MAX - ( RAND_MAX % n ) )
When X >= 2 the value will be discarded, even though the set is valid.
Version généralisée 1:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X > (RAND_MAX - ( ( RAND_MAX % n ) + 1 ) % n )
When X > 3 the value would be discarded, but this is not a vlue in the set RAND_MAX so there will be no discard.
De plus, dans le cas où N doit être le nombre de valeurs dans RAND_MAX; dans ce cas, vous pouvez définir N = RAND_MAX +1, sauf si RAND_MAX = INT_MAX.
En boucle, vous pouvez simplement utiliser N = 1, et toute valeur de X sera acceptée, cependant, et mettez une instruction IF pour votre multiplicateur final. Mais vous avez peut-être du code qui peut avoir une raison valable de retourner un 1 lorsque la fonction est appelée avec n = 1 ...
Il peut donc être préférable d'utiliser 0, qui fournirait normalement une erreur Div 0, lorsque vous souhaitez avoir n = RAND_MAX + 1
Version généralisée 2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
Ces deux solutions résolvent le problème avec des résultats valides inutilement rejetés qui se produiront lorsque RM + 1 est un produit de n.
La deuxième version couvre également le scénario de cas de bord lorsque vous avez besoin de n pour égaler l'ensemble total possible de valeurs contenues dans RAND_MAX.
L'approche modifiée dans les deux est la même et permet une solution plus générale au besoin de fournir des nombres aléatoires valides et de minimiser les valeurs rejetées.
Recommencer:
La solution générale de base qui prolonge l'exemple de Mark:
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) ) );
x %= n;
La solution générale étendue qui permet un scénario supplémentaire de RAND_MAX + 1 = n:
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) ) );
x %= n;
} else {
x = rand();
}
Dans certaines langues (en particulier les langues interprétées), le calcul de l'opération de comparaison en dehors de la condition while peut conduire à des résultats plus rapides car il s'agit d'un calcul unique, quel que soit le nombre de réessais requis. YMMV!
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x; // Resulting random number
int y; // One-time calculation of the compare value for x
if n != 0 {
y = RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n)
do {
x = rand();
} while (x > y);
x %= n;
} else {
x = rand();
}
RAND_MAX%n == n - 1
_ est(RAND_MAX + 1) % n == 0
. Lors de la lecture de code, j'ai tendance à comprendre% something == 0
comme «également divisible» plus facilement que les autres façons de le calculer. Bien sûr, si votre stdlib C ++ aRAND_MAX
la même valeur queINT_MAX
,(RAND_MAX + 1)
cela ne fonctionnerait sûrement pas; le calcul de Mark reste donc la mise en œuvre la plus sûre.