Я создаю глубокий клон для некоторого объекта. Объект содержит 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);
Хотя это не совсем то, о чем просят, я думаю, что это, вероятно, то, что требуется.