web-dev-qa-db-fra.com

Comment générer un nombre aléatoire compris entre et 1 en C #?

Je veux obtenir le nombre aléatoire entre 1 et 0. Cependant, je reçois 0 à chaque fois. Quelqu'un peut-il m'expliquer la raison pour laquelle moi et obtenir 0 tout le temps? C'est le code que j'ai essayé.

Random random = new Random();
int test = random.Next(0, 1);
Console.WriteLine(test);
Console.ReadKey();
8
Alanay

Selon la documentation , Next renvoie un nombre entier aléatoire compris entre le minimum (inclus) et le maximum (exclusif):

Valeur de retour

Un entier signé 32 bits supérieur ou égal à minValue et inférieur à maxValue; c'est-à-dire que la plage de valeurs de retour inclut minValue mais pas maxValue. Si minValue est égal à maxValue, minValue est renvoyé.

Le seul nombre entier qui remplit

0 <= x < 1

est 0, vous obtenez donc toujours la valeur 0. En d'autres termes, 0 est le seul entier compris dans l'intervalle semi-fermé [0, 1).

Donc, si vous êtes réellement intéressé par les valeurs entières 0 ou 1, utilisez 2 comme limite supérieure:

var n = random.Next(0, 2);

Si vous souhaitez plutôt obtenir un nombre décimal compris entre 0 et 1, essayez:

var n = random.NextDouble();

J'espère que cela t'aides :-)

13
Golo Roden

Vous pouvez, mais vous devriez le faire de cette façon:

double test = random.NextDouble();

Si vous voulez obtenir un entier aléatoire (0 ou 1), vous devez définir la limite supérieure sur 2, car il s'agit de exclusive

int test = random.Next(0, 2);
4
Maksim Simkin

Chaque réponse sur cette page concernant les doublons est fausse, ce qui est un peu hilarant parce que tout le monde cite la documentation. Si vous générez un double à l'aide de NextDouble (), vous n'obtiendrez pas un nombre compris entre 0 et 1, mais 1, vous obtiendrez un nombre compris entre 0 et 1.

Pour obtenir un double, il faudrait faire des trucs comme celui-ci:

public double NextRandomRange(double minimum, double maximum)
{
     Random Rand = new Random();
     return Rand.NextDouble() * (maximum - minimum) + minimum;
}

et ensuite appeler

NextRandomRange(0,1 + Double.Epsilon);

On dirait que ça marcherait, n'est-ce pas? 1 + Double.Epsilon devrait être le prochain plus grand nombre après 1 lorsque vous travaillez avec des doubles, non? Voici comment résoudre le problème avec ints. 

Wellllllllllllllll .........

Je suppose que cela ne fonctionnera pas correctement, car le code sous-jacent générera quelques octets d’aléatoire, puis effectuera quelques astuces mathématiques pour l’ajuster à la plage attendue. La réponse courte est que la logique qui s’applique à ints ne fonctionne pas exactement de la même manière avec des floats.

Regardons, allons-nous? ( https://referencesource.Microsoft.com/#mscorlib/system/random.cs,e137873446fcef75 )

  /*=====================================Next=====================================
  **Returns: A double [0..1)
  **Arguments: None
  **Exceptions: None
  ==============================================================================*/
  public virtual double NextDouble() {
    return Sample();
  }

Qu'est-ce que c'est que Sample ()?

  /*====================================Sample====================================
  **Action: Return a new random number [0..1) and reSeed the Seed array.
  **Returns: A double [0..1)
  **Arguments: None
  **Exceptions: None
  ==============================================================================*/
  protected virtual double Sample() {
      //Including this division at the end gives us significantly improved
      //random number distribution.
      return (InternalSample()*(1.0/MBIG));
  }

Ok, commence à aller quelque part. MBIG, en fait, est Int32.MaxValue (2147483647 ou 2 ^ 31-1), permettant à la division de fonctionner comme suit:

InternalSample()*0.0000000004656612873077392578125;

Ok, qu'est-ce que InternalSample ()?

  private int InternalSample() {
      int retVal;
      int locINext = inext;
      int locINextp = inextp;

      if (++locINext >=56) locINext=1;
      if (++locINextp>= 56) locINextp = 1;

      retVal = SeedArray[locINext]-SeedArray[locINextp];

      if (retVal == MBIG) retVal--;          
      if (retVal<0) retVal+=MBIG;

      SeedArray[locINext]=retVal;

      inext = locINext;
      inextp = locINextp;

      return retVal;
  }

Eh bien ... c'est quelque chose. Mais qu'est-ce que ce SeedArray et Inext merde tout sur?

  private int inext;
  private int inextp;
  private int[] SeedArray = new int[56];

Alors les choses commencent à tomber ensemble. Le tableau Seed est un tableau d'ints utilisé pour générer des valeurs. Si vous regardez la fonction init, vous voyez qu’il ya beaucoup d’ajouts en bits, une astuce étant faite pour randomiser un tableau de 55 valeurs avec des valeurs initiales quasi aléatoires.

  public Random(int Seed) {
    int ii;
    int mj, mk;

    //Initialize our Seed array.
    //This algorithm comes from Numerical Recipes in C (2nd Ed.)
    int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
    mj = MSEED - subtraction;
    SeedArray[55]=mj;
    mk=1;
    for (int i=1; i<55; i++) {  //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
      ii = (21*i)%55;
      SeedArray[ii]=mk;
      mk = mj - mk;
      if (mk<0) mk+=MBIG;
      mj=SeedArray[ii];
    }
    for (int k=1; k<5; k++) {
      for (int i=1; i<56; i++) {
    SeedArray[i] -= SeedArray[1+(i+30)%55];
    if (SeedArray[i]<0) SeedArray[i]+=MBIG;
      }
    }
    inext=0;
    inextp = 21;
    Seed = 1;
  }

Ok, pour en revenir à InternalSample (), nous pouvons maintenant voir que des doubles aléatoires sont générés en prenant la différence de deux bits entrés de 32 bits brouillés, en fixant le résultat dans la plage de 0 à 2147483647 - 1, puis en multipliant le résultat par 1/2147483647. Plus de ruse est faite pour brouiller la liste des valeurs de départ car elle utilise des valeurs, mais c'est tout. 

(Il est intéressant de noter que la chance d’obtenir un nombre quelconque dans la plage est très petite, 1/r SAUF pour 2 ^ 31-2, ce qui correspond à 2 * (1/r)! Donc, si vous pensez que certains développeurs stupides utilisent RandNext ( ) pour générer des numéros sur une machine de vidéo poker, vous devez toujours miser sur 2 ^ 32-2! C’est une des raisons pour lesquelles nous n’utilisons pas Aléatoire pour des tâches importantes ...) 

ainsi, si la sortie de InternalSample () est 0, nous la multiplions par 0.0000000004656612873077392578125 et obtenons 0, le bas de notre gamme. si nous obtenons 2147483646, nous nous retrouvons avec 0.9999999995343387126922607421875, donc l'affirmation que NextDouble est [0,1) est ... en quelque sorte non? Il serait plus exact de dire que sa plage est comprise entre [0,0.999999999553333121222227421875].

Ma solution suggérée ci-dessus tomberait sur son visage, puisque double.Epsilon = 4.94065645841247E-324, ce qui est beaucoup plus petit que 0.0000000004656612873077392578125 (montant que vous ajouteriez à notre résultat ci-dessus pour obtenir 1). 

Ironiquement, si ce n'était de la soustraction d'un dans la méthode InternalSample ():

if (retVal == MBIG) retVal--;

nous pourrions arriver à 1 dans les valeurs de retour qui reviennent. Donc, soit vous copiez tout le code de la classe Random et omettez la ligne retVal--, soit vous multipliez la sortie NextDouble () par quelque chose comme 1.0000000004656612875245796924106 pour étirer légèrement la sortie afin d'inclure 1 dans la plage. Tester minutieusement cette valeur nous rapproche vraiment, mais je ne sais pas si les quelques centaines de millions de tests que j'ai exécutés n'ont tout simplement pas produit 2147483646 (très probablement) ou s'il y a une erreur de virgule flottante qui se glisse dans l'équation. Je soupçonne le premier.

NextRandomRange(0,1.0000000004656612875245796924106); // try to explain where you got that number during the code review...

TLDR? Les plages incluses avec des doubles aléatoires sont délicates ...

1
Roger Hill

Vous obtenez zéro car Random.Next(a,b) renvoie un nombre compris dans la plage [a, b), qui est supérieur ou égal à a et inférieur à b.

Si vous souhaitez obtenir l'un des {0, 1}, vous devez utiliser:

var random = new Random();
var test = random.Next(0, 2);
1
George Polevoy

Parce que vous avez demandé un nombre inférieur à 1.

La documentation dit:

Valeur de retour
Un entier signé 32 bits supérieur ou égal à minValue et inférieur à maxValue; c'est-à-dire la plage de valeurs de retour inclut minValue mais pas maxValue. Si minValue est égal à maxValue, minValue est renvoyé.

0
SLaks

Réécrivez le code de cette manière si vous ciblez 0.0 à 1.0

Random random = new Random();

double test = random.NextDouble();

Console.WriteLine(test);

Console.ReadKey();
0
technoclem