Necesito generar números reales aleatorios en el rango [-0.5, 0.5]
, ambos límites inclusive .
Encontré varias formas de generar rangos similares, como
-0.5 + Math.random()
Pero el límite superior siempre es exclusivo , también lo necesito inclusivo. 0.5
debe estar dentro del rango.
6 respuestas
Una forma de lograr esto sería crear int
aleatorio de -500 a 500 y luego dividirlo por 1000.
int max = 500;
int min = -500;
int randomInt = rand.nextInt((max - min) + 1) + min;
float randomNum = randomInt / 1000.00f;
System.out.println(randomNum);
Puede cambiar la precisión agregando y eliminando ceros de los límites enteros y el divisor. (eG: crea números enteros de -5 a +5 y divide entre 10 para obtener menos precisión)
Una desventaja de esa solución es que no utiliza la precisión máxima proporcionada por los tipos de datos flotantes / dobles.
No he visto ninguna respuesta que use la manipulación de bits dentro de la representación doble IEEE-754, así que aquí hay una.
Basado en la observación de que un rollover al siguiente exponente binario es lo mismo que agregar 1
a la representación binaria (en realidad esto es por diseño):
Double.longBitsToDouble(0x3ff0000000000000L) // 1.0
Double.longBitsToDouble(0x3ffFFFFFFFFFFFFFL) // 1.9999999999999998
Double.longBitsToDouble(0x4000000000000000L) // 2.0
Se me ocurrió esto:
long l = ThreadLocalRandom.current().nextLong(0x0010000000000001L);
double r = Double.longBitsToDouble(l + 0x3ff0000000000000L) - 1.5;
Esta técnica funciona solo con rangos que abarcan un número binario (1, 2, 4, 8, 0.5, 0.25, etc.) pero para esos rangos este enfoque es posiblemente el más eficiente y preciso. Este ejemplo está ajustado para un intervalo de 1. Para los intervalos que no abarcan un intervalo binario, aún puede utilizar esta técnica para obtener un intervalo diferente. Aplique la técnica para obtener un número en el rango [0, 1] y escale el resultado al intervalo deseado. Esto tiene una pérdida de precisión insignificante, y la precisión resultante es en realidad idéntica a la de Random.nextDouble(double, double)
.
Para otros tramos, ejecute este código para encontrar el desplazamiento:
double span = 0.125;
if (!(span > 0.0) || (Double.doubleToLongBits(span) & 0x000FFFFFFFFFFFFFL) != 0)
throw new IllegalArgumentException("'span' is not a binary number: " + span);
if (span * 2 >= Double.MAX_VALUE)
throw new IllegalArgumentException("'span' is too large: " + span);
System.out.println("Offset: 0x" + Long.toHexString(Double.doubleToLongBits(span)));
Cuando conecta este desplazamiento en la segunda línea del código real, obtiene un valor en el rango [span, 2 * span]. Reste span
para obtener un valor que comience en 0.
Puede ajustar el límite superior por el valor mínimo ( epsilon ) mayor que el valor máximo que espera. Para encontrar el épsilon, comience con cualquier valor positivo y hágalo lo más pequeño posible:
double min = -0.5;
double max = 0.5;
double epsilon = 1;
while (max + epsilon / 2 > max) {
epsilon /= 2;
}
Random random = ThreadLocalRandom.current();
DoubleStream randomDoubles = random.doubles(min, max + epsilon);
Editar: alternativa sugerida por @DodgyCodeException (resultados en el mismo épsilon que el anterior):
double min = -0.5;
double max = 0.5;
double maxPlusEpsilon = Double.longBitsToDouble(Double.doubleToLongBits(max) + 1L)
Random random = ThreadLocalRandom.current();
DoubleStream randomDoubles = random.doubles(min, maxPlusEpsilon);
Dada la respuesta de CÓMO DIOS ARAÑAS, aquí hay una función lista para usar:
public static double randomFloat(double minInclusive, double maxInclusive, double precision) {
int max = (int)(maxInclusive/precision);
int min = (int)(minInclusive/precision);
Random rand = new Random();
int randomInt = rand.nextInt((max - min) + 1) + min;
double randomNum = randomInt * precision;
return randomNum;
}
Entonces
System.out.print(randomFloat(-0.5, 0.5, 0.01));
La respuesta de @OH GOD SPIDERS me dio una idea para desarrollarla en una respuesta que ofrezca mayor precisión. nextLong()
proporciona un valor entre MIN_VALUE y MAX_VALUE con una precisión más que adecuada cuando se lanza a double
.
double randomNum = (rand.nextLong() / 2.0) / Long.MAX_VALUE;
Prueba de que los límites son inclusivos:
assert (Long.MIN_VALUE/2.0)/Long.MAX_VALUE == -0.5;
assert (Long.MAX_VALUE/2.0)/Long.MAX_VALUE == 0.5;
Random.nextDouble da un valor en el rango de [0, 1]. Entonces, para asignar eso a un rango de [-0.5, 0.5] solo necesita restar 0.5.
Puede usar este código para obtener el resultado deseado double value = r.nextDouble() - 0.5;
Preguntas relacionadas
Nuevas preguntas
java
Java es un lenguaje de programación de alto nivel. Utilice esta etiqueta cuando tenga problemas para usar o comprender el idioma en sí. Esta etiqueta rara vez se usa sola y se usa con mayor frecuencia junto con [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] y [maven].