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
hsnsd 10 may. 2019 a las 11:32

6 respuestas

La mejor respuesta

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.

8
OH GOD SPIDERS 10 may. 2019 a las 09:30

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.

3
Mark Jeronimus 10 may. 2019 a las 12:34

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);
3
Peter Walser 10 may. 2019 a las 13:56

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));
1
PopHip 10 may. 2019 a las 09:03

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;
1
DodgyCodeException 10 may. 2019 a las 09:05

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;

0
Nav_cfc 10 may. 2019 a las 08:47