Я создаю глубокий клон для некоторого объекта. Объект содержит Random
.
Хорошо ли извлекать семя из Random
? Если да, то как? Не существует Random.getSeed()
.
Я создаю глубокий клон для некоторого объекта. Объект содержит Random
.
Хорошо ли извлекать семя из Random
? Если да, то как? Не существует Random.getSeed()
.
Случайный должен быть случайным. Обычно вы хотите, чтобы два Random производили разные числа, а не для получения одинаковых чисел.
Вы можете скопировать случайное использование сериализации/де-сериализации и получить поле "семя" с помощью отражения. (Но я сомневаюсь, что вы тоже должны это делать)
Если последовательность не является критичной для вас, вы можете принять представление о том, что клон Random это сам или любой new Random()
Что вы можете сделать, так это получить системное время, затем сгенерировать генератор случайных чисел и сохранить его где-нибудь или распечатать, чтобы вы могли использовать его позже.
long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
Это может быть хорошей практикой в зависимости от вашей цели. Для большинства целей вам не нужно извлекать текущее семя. Например, если ваша цель состоит в том, чтобы иметь два генератора случайных чисел, которые генерируют одну и ту же последовательность значений, тогда вам не нужно извлекать случайное семя: вы просто создаете эти два случайных объекта с тем же (предварительно заданным) семенем.
Java не предоставляет стандартный способ извлечения семени из объекта Random. Если вам действительно нужен этот номер, вы можете обойти его: сериализуйте свой случайный объект, сериализуйте другой случайный объект (с другим семенем), найдите 8 байтов, где эти две строки отличаются, и получите начальное значение из этих 8 байтов.
Вот как это сделать с сериализацией:
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.");
}
}
Более простой способ получить семя - это создать его и сохранить в качестве семени. Я использую этот метод для игры и хочу дать игроку возможность генерировать точный мир, если он того пожелает. Поэтому сначала я создаю объект Random без семени, а затем позволяю ему генерировать случайное число и использовать его в другом случайном объекте в качестве семени. Всякий раз, когда игрок хочет семя уровня, которое я где-то храню. По умолчанию игра по-прежнему случайна.
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);
Интересный парадокс... Я бы не назвал клонированный объект Random
случайным - в качестве обходного пути вы могли бы попробовать это: когда вы клонируете свой объект, вы можете установить семя самостоятельно в обоих экземплярах Random
с тем же значение.
Это можно сделать с помощью отражения, хотя есть небольшая причуда:
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);
Магическое число 0x5DEECE66DL
происходит от исходного кода Random.java, где семена получают "начальную схватку":/p >
private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1;
//...
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask;
}
который XOR их со случайным числом и обрезает их до 48 бит. Таким образом, чтобы воссоздать состояние семени, мы должны XOR затраченное семя.
Причина, по которой я здесь, заключается в том, что мне нужно запомнить семя, если мне нужно воссоздать то, что произошло в конкретном запуске программы. Используемый мной код:
long seed = random.nextLong();
random.setSeed(seed);
Хотя это не совсем то, о чем просят, я думаю, что это, вероятно, то, что требуется.