rango - numeros aleatorios negativos y positivos en java
¿Cómo obtengo la semilla de un Random en Java? (7)
A Random está destinado a ser aleatorio. Usualmente quieres dos Random para producir diferentes números en lugar de producir los mismos números.
Puede copiar un Aleatorio usando la serialización / deserialización y obtener el campo "semilla" usando reflexión. (Pero dudo que deberías estar haciendo lo mismo)
A menos que la secuencia sea crítica para usted, puede considerar que el clon de un Random es él mismo o cualquier new Random()
Estoy creando un clon profundo para algún objeto. El objeto contiene un Random
.
¿Es una buena práctica recuperar la semilla del Random
? ¿Si es así, cómo? No hay un Random.getSeed()
.
Esto se puede hacer a través de la reflexión, aunque hay una pequeña peculiaridad:
Random r = ...; //this is the random you want to clone
long theSeed;
try
{
Field field = Random.class.getDeclaredField("seed");
field.setAccessible(true);
AtomicLong scrambledSeed = (AtomicLong) field.get(r); //this needs to be XOR''d with 0x5DEECE66DL
theSeed = scrambledSeed.get();
}
catch (Exception e)
{
//handle exception
}
Random clonedRandom = new Random(theSeed ^ 0x5DEECE66DL);
El número mágico 0x5DEECE66DL
proviene del código fuente de Random.java , donde a las semillas se les da una "mezcla inicial" de:
private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1;
//...
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask;
}
que XOR los incluye con un número aleatorio y los trunca a 48 bits. Por lo tanto, para recrear el estado de la semilla, tenemos que XOR la semilla que extraemos.
Interesante paradoja ... No llamaría Random
objeto aleatorio clonado, como solución alternativa podría intentarlo: cuando está clonando su objeto, puede establecer la semilla usted mismo en ambas instancias Random
con el mismo valor.
La razón por la que estoy aquí es porque necesito recordar la semilla en caso de que necesite recrear lo que sucedió en una ejecución particular del programa. El código que utilicé es:
long seed = random.nextLong();
random.setSeed(seed);
Aunque esto no es exactamente lo que se pide, creo que es probablemente lo que se requiere.
Lo que puede hacer es obtener la hora del sistema usted mismo, luego siembre el generador de números aleatorios con él y guárdelo en algún lugar o imprímalo para poder usarlo más adelante.
long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
Puede ser una buena práctica dependiendo de tu propósito. Para la mayoría de los propósitos, no es necesario recuperar la semilla actual. Por ejemplo, si su objetivo es tener dos generadores aleatorios que generen la misma secuencia de valores, entonces no es necesario recuperar la semilla aleatoria: simplemente crea esos dos objetos aleatorios con la misma semilla (preestablecida).
Java no proporciona una forma estándar de recuperar la semilla de un objeto aleatorio. Si realmente necesita ese número, puede solucionarlo: serialice su objeto Aleatorio, serialice otro objeto Aleatorio (con una semilla diferente), encuentre los 8 bytes donde difieren estas dos cadenas y recupere el valor inicial de esos 8 bytes.
Aquí es cómo hacerlo con serialización:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Random;
public class SeedGetter {
static long getSeed(Random random) {
byte[] ba0, ba1, bar;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(new Random(0));
ba0 = baos.toByteArray();
baos = new ByteArrayOutputStream(128);
oos = new ObjectOutputStream(baos);
oos.writeObject(new Random(-1));
ba1 = baos.toByteArray();
baos = new ByteArrayOutputStream(128);
oos = new ObjectOutputStream(baos);
oos.writeObject(random);
bar = baos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("IOException: " + e);
}
if (ba0.length != ba1.length || ba0.length != bar.length)
throw new RuntimeException("bad serialized length");
int i = 0;
while (i < ba0.length && ba0[i] == ba1[i]) {
i++;
}
int j = ba0.length;
while (j > 0 && ba0[j - 1] == ba1[j - 1]) {
j--;
}
if (j - i != 6)
throw new RuntimeException("6 differing bytes not found");
// The constant 0x5DEECE66DL is from
// http://download.oracle.com/javase/6/docs/api/java/util/Random.html .
return ((bar[i] & 255L) << 40 | (bar[i + 1] & 255L) << 32 |
(bar[i + 2] & 255L) << 24 | (bar[i + 3] & 255L) << 16 |
(bar[i + 4] & 255L) << 8 | (bar[i + 5] & 255L)) ^ 0x5DEECE66DL;
}
public static void main(String[] args) {
Random random = new Random(12345);
if (getSeed(random) != 12345)
throw new RuntimeException("Bad1");
random.nextInt();
long seed = getSeed(random);
if (seed == 12345)
throw new RuntimeException("Bad2");
Random random2 = new Random(seed);
if (random.nextInt() != random2.nextInt())
throw new RuntimeException("Bad3");
System.out.println("getSeed OK.");
}
}
Una forma mucho más simple de obtener una semilla es generar una y almacenarla como semilla. Estoy usando este método para un juego y quiero darle al jugador la opción de generar exactamente el mismo mundo si así lo desea. Así que primero creo un objeto aleatorio sin una semilla, luego dejo que ese genere un número aleatorio y lo use en otro objeto aleatorio como semilla. Cada vez que el jugador quiere la semilla del nivel lo tengo almacenado en alguna parte. Por defecto, el juego sigue siendo aleatorio.
Random rand = new Random();
//Store a random seed
long seed = rand.nextLong();
//Set the Random object seed
rand.setSeed(seed);
//do random stuff...
//Wonder what the seed is to reproduce something?
System.out.println(seed);