2010-07-29 62 views
40

¿Cuál es la mejor forma de generar un flotante aleatorio en C#?La mejor forma de generar un flotante aleatorio en C#

Actualización: Quiero números flotantes aleatorios de float.Minvalue a float.Maxvalue. Estoy usando estos números en pruebas unitarias de algunos métodos matemáticos.

+20

4.0 - aleatorio elegido por el lanzamiento de dados de coma flotante. –

+5

@ JesseC.Slicer Olvidó proporcionar el enlace: http://xkcd.com/221/ – jpmc26

Respuesta

48

Mejor enfoque, sin valores enloquecidos, distributed with respect to the representable intervals on the floating-point number line (eliminado "uniforme", como con respecto a una línea de números continua es decididamente no uniforme):

static float NextFloat(Random random) 
{ 
    double mantissa = (random.NextDouble() * 2.0) - 1.0; 
    double exponent = Math.Pow(2.0, random.Next(-126, 128)); 
    return (float)(mantissa * exponent); 
} 

Otro enfoque que le dará algunos valores locos (distribución uniforme de patrones de bits), potencialmente útiles para la formación de pelusa:

static float NextFloat(Random random) 
{ 
    var buffer = new byte[4]; 
    random.NextBytes(buffer); 
    return BitConverter.ToSingle(buffer,0); 
} 

enfoque útil mínimo:

static float NextFloat(Random random) 
{ 
    // Not a uniform distribution w.r.t. the binary floating-point number line 
    // which makes sense given that NextDouble is uniform from 0.0 to 1.0. 
    // Uniform w.r.t. a continuous number line. 
    // 
    // The range produced by this method is 6.8e38. 
    // 
    // Therefore if NextDouble produces values in the range of 0.0 to 0.1 
    // 10% of the time, we will only produce numbers less than 1e38 about 
    // 10% of the time, which does not make sense. 
    var result = (random.NextDouble() 
        * (Single.MaxValue - (double)Single.MinValue)) 
        + Single.MinValue; 
    return (float)result; 
} 

flotante línea de número de punto a partir de: Intel Architecture Software Developer's Manual Volume 1: Basic Architecture. El eje Y es logarítmica (base-2), ya que los números de coma flotante binario consecutivos no difieren de forma lineal.

Comparison of distributions, logarithmic Y-axis

+0

No parece haber un método BitConverter.GetSingle. ¿Quieres decir ToSingle? – KrisTrip

+0

Eso sería lo que quiero decir ... – user7116

+1

El uso de bytes aleatorios puede terminar fácilmente con un valor NaN, y la distribución puede ser no uniforme. (No me gustaría predecir la distribución.) –

20

¿Alguna razón para no usar Random.NextDouble y luego convertir a float? Eso le dará un flotador entre 0 y 1.

Si desea una forma diferente de "mejor", deberá especificar sus requisitos. Tenga en cuenta que Random no debe utilizarse para asuntos delicados, como finanzas o seguridad, y generalmente debe reutilizar una instancia existente en su aplicación, o una por hilo (ya que Random no es seguro para subprocesos).

EDIT: Como se sugiere en los comentarios, para convertir a un rango de float.MinValue, float.MaxValue:

// Perform arithmetic in double type to avoid overflowing 
double range = (double) float.MaxValue - (double) float.MinValue; 
double sample = rng.NextDouble(); 
double scaled = (sample * range) + float.MinValue; 
float f = (float) scaled; 

EDIT: Ahora que usted ha mencionado que esto es para la unidad de pruebas, no estoy seguro de que es una enfoque ideal. Probablemente debería probar con valores concretos en su lugar, asegurándose de probar con muestras en cada una de las categorías relevantes: infinitos, NaNs, números denormales, números muy grandes, cero, etc.

+0

Creo que NextDouble solo proporciona valores de 0.0 a 1.0 y quiero más rango que eso (como desde float.MinValue a float.MaxValue) Supongo que debería haber especificado :) – KrisTrip

+3

¿Simplemente multiplíquelo por (MaxValue-MinValue) y agregue MinValue a él? Algo así como Random.NextDouble * (float.MaxValue-float.MinValue) + float.MinValue – Xzhsh

+0

La distribución de valores que produce es bimodal, creo que tiene que ver con qué tan grandes son los valores en el rango. – user7116

1

Me tomó un enfoque ligeramente diferente a los demás

static float NextFloat(Random random) 
{ 
    double val = random.NextDouble(); // range 0.0 to 1.0 
    val -= 0.5; // expected range now -0.5 to +0.5 
    val *= 2; // expected range now -1.0 to +1.0 
    return float.MaxValue * (float)val; 
} 

Los comentarios explican lo que estoy haciendo. Obtenga el siguiente doble, convierta ese número a un valor entre -1 y 1 y luego multiplique eso con float.MaxValue.

0

Otra solución es hacer esto:

static float NextFloat(Random random) 
{ 
    float f; 
    do 
    { 
     byte[] bytes = new byte[4]; 
     random.NextBytes(bytes); 
     f = BitConverter.ToSingle(bytes, 0); 
    } 
    while (float.IsInfinity(f) || float.IsNaN(f)); 
    return f; 
} 
5

Una versión más ... (Creo que éste es bastante buena)

static float NextFloat(Random random) 
{ 
    (float)(float.MaxValue * 2.0 * (rand.NextDouble()-0.5)); 
} 

//inline version 
float myVal = (float)(float.MaxValue * 2.0 * (rand.NextDouble()-0.5)); 

creo que esto ...

  • es el segundo más rápido (ver referencias)
  • se distribuye uniformemente

y una más versión ... (no tan bueno, pero la publicación de todos modos)

static float NextFloat(Random random) 
{ 
    return float.MaxValue * ((rand.Next()/1073741824.0f) - 1.0f); 
} 

//inline version 
float myVal = (float.MaxValue * ((rand.Next()/1073741824.0f) - 1.0f)); 

Creo que esto ...

  • es el más rápido (ver comparativas)
  • se distribuye uniformemente, sin embargo, porque Next() es un valor aleatorio de 31 bits, solo devolverá 2^31 valores. (50% de los valores vecino tendrá el mismo valor)

Prueba de la mayoría de las funciones de esta página: (i7, lanzamiento, sin depurar, 2^28 bucles)

Sunsetquest1: min: 3.402823E+38 max: -3.402823E+38 time: 3096ms 
SimonMourier: min: 3.402823E+38 max: -3.402819E+38 time: 14473ms 
AnthonyPegram:min: 3.402823E+38 max: -3.402823E+38 time: 3191ms 
JonSkeet:  min: 3.402823E+38 max: -3.402823E+38 time: 3186ms 
Sixlettervar: min: 1.701405E+38 max: -1.701410E+38 time: 19653ms 
Sunsetquest2: min: 3.402823E+38 max: -3.402823E+38 time: 2930ms 
+1

¿Estás seguro de que será más rápido porque es una potencia de dos? Asumiría que se lanzaría implícitamente a un flotador * (que no representaría el valor exactamente, luego usaría la división de flotación regular). * – ideasman42

+0

¡Gracias por notar eso, fue una buena captura! Tiene razón en que se convirtió en flotador primero, por lo que el compilador no pudo usar una instrucción de cambio. Quité el "es más rápido porque es una potencia de dos". Como puede ver, hice varios otros cambios, incluyendo agregar una nueva función y también hacer benchmarks. – Sunsetquest

0

Aquí hay otra manera en que se me ocurrió: Digamos que quieres obtener un valor flotante entre 5.5 y 7, con 3 decimales.

float myFloat; 
int myInt; 
System.Random rnd = new System.Random(); 

void GenerateFloat() 
{ 
myInt = rnd.Next(1, 2000); 
myFloat = (myInt/1000) + 5.5f; 
} 

De esa manera usted siempre obtendrá un número mayor que 5,5 y un número menor que 7.

0

Yo prefiero usar el siguiente código para generar un número decimal hasta puño punto decimal. puede copiar pegar la 3ra línea para agregar más números después del punto decimal al agregar ese número en la cadena "combinada". Puede establecer el valor mínimo y máximo cambiando el 0 y 9 a su valor preferido.

Random r = new Random(); 
string beforePoint = r.Next(0, 9).ToString();//number before decimal point 
string afterPoint = r.Next(0,9).ToString();//1st decimal point 
//string secondDP = r.Next(0, 9).ToString();//2nd decimal point 
string combined = beforePoint+"."+afterPoint; 
decimalNumber= float.Parse(combined); 
Console.WriteLine(decimalNumber); 

Espero que te haya ayudado.

Cuestiones relacionadas