Saltar al contenido

¿Cómo genero un número aleatorio entre 0 y 1 en C #?

Posteriormente a mirar en diversos repositorios y páginas webs al concluir nos encontramos con la resolución que te enseñamos aquí.

Solución:

Según la documentación, Next devuelve un número entero aleatorio entre el mínimo (incluido) y el máximo (exclusivo):

Valor devuelto

Un entero de 32 bits con signo mayor o igual que minValue y menor que maxValue; es decir, el rango de valores devueltos incluye minValue pero no maxValue. Si minValue es igual a maxValue, se devuelve minValue.

El único número entero que cumple

0 <= x < 1

es 0, por lo tanto siempre obtienes el valor 0. En otras palabras, 0 es el único entero que está dentro del intervalo medio cerrado [0, 1).

So, if you are actually interested in the integer values 0 or 1, then use 2 as upper bound:

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

If instead you want to get a decimal between 0 and 1, try:

var n = random.NextDouble();

Hope this helps 🙂

You could, but you should do it this way:

double test = random.NextDouble();

If you wanted to get random integer ( 0 or 1), you should set upper bound to 2, because it is exclusive

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

Every single answer on this page regarding doubles is wrong, which is sort of hilarious because everyone is quoting the documentation. If you generate a double using NextDouble(), you will not get a number between 0 and 1 inclusive of 1, you will get a number from 0 to 1 exclusive of 1.

To get a double, you would have to do some trickery like this:

public double NextRandomRange(double minimum, double maximum)

     Random rand = new Random();
     return rand.NextDouble() * (maximum - minimum) + minimum;

and then call

NextRandomRange(0,1 + Double.Epsilon);

Seems like that would work, doesn't it? 1 + Double.Epsilon should be the next biggest number after 1 when working with doubles, right? This is how you would solve the problem with ints.

Wellllllllllllllll.........

I suspect that this will not work correctly, since the underlying code will be generating a few bytes of randomness, and then doing some math tricks to fit it in the expected range. The short answer is that Logic that applies to ints doesn't quite work the same when working with floats.

Lets look, shall we? (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();
  

What the hell is 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, starting to get somewhere. MBIG btw, is Int32.MaxValue(2147483647 or 2^31-1), making the division work out to:

InternalSample()*0.0000000004656612873077392578125;

Ok, what the hell is 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--;  si (retVal <0) retVal + = MBIG;  SeedArray[locINext]= retVal;  inext = locINext;  inextp = locINextp;  return retVal;  

Bueno ... eso es algo. Pero, ¿de qué se trata este SeedArray e inext?

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

Entonces las cosas comienzan a encajar. Semilla array es un array de ints que se utiliza para generar valores a partir de. Si observa la función init def, verá que se están haciendo muchos trucos y adiciones de bits para aleatorizar un array de 55 valores con valores iniciales cuasialeatorios.

  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 (All hail Knuth!) and so we're skipping over the 0th 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, volviendo a InternalSample (), ahora podemos ver que los dobles aleatorios se generan tomando la diferencia de dos entradas codificadas de 32 bits, fijando el resultado en el rango de 0 a 2147483647 - 1 y luego multiplicando el resultado por 1 / 2147483647. Se realizan más trucos para codificar la lista de valores semilla ya que usa valores, pero eso es esencialmente todo.

(¡Es interesante notar que la posibilidad de obtener cualquier número en el rango es aproximadamente 1 / r EXCEPTO para 2 ^ 31-2, que es 2 * (1 / r)! Entonces, si cree que algún codificador idiota está usando RandNext ( ) para generar números en una máquina de video póquer, ¡siempre debes apostar a 2 ^ 32-2! Esta es una de las razones por las que no usamos Random para nada importante ...)

entonces, si la salida de InternalSample () es 0, multiplicamos eso por 0.0000000004656612873077392578125 y obtenemos 0, el extremo inferior de nuestro rango. si obtenemos 2147483646, terminamos con 0.9999999995343387126922607421875, por lo que la afirmación de que NextDouble produce un resultado de [0,1) is...sort of right? It would be more accurate to say it is int he range of [0,0.9999999995343387126922607421875].

Mi solución anterior sugerida caería de bruces, ya que double.Epsilon = 4.94065645841247E-324, que es MUCHO más pequeño que 0.0000000004656612873077392578125 (la cantidad que agregaría a nuestro resultado anterior para obtener 1).

Irónicamente, si no fuera por la resta de uno en el método InternalSample ():

if (retVal == MBIG) retVal--;

podríamos llegar a 1 en los valores devueltos que regresan. Entonces, o copia todo el código en la clase Random y omite la línea retVal--, o multiplica la salida de NextDouble () por algo como 1.0000000004656612875245796924106 para estirar ligeramente la salida para incluir 1 en el rango. En realidad, probar ese valor nos acerca mucho, pero no sé si los pocos cientos de millones de pruebas que ejecuté simplemente no produjeron 2147483646 (bastante probable) o si hay un error de punto flotante en la ecuación. Sospecho de lo primero. Es poco probable que millones de pruebas arrojen un resultado que tenga una probabilidad de 1 entre 2 mil millones.

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

TLDR? Los rangos inclusivos con dobles aleatorios son complicados ...

Finalizando este artículo puedes encontrar las interpretaciones de otros creadores, tú de igual forma tienes el poder mostrar el tuyo si te apetece.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *