Я создаю глубокий клон для некоторого объекта. Объект содержит Random
.
Является ли хорошей практикой извлечение начального числа из Random
? Если да, то как? Нет Random.getSeed()
.
Я создаю глубокий клон для некоторого объекта. Объект содержит Random
.
Является ли хорошей практикой извлечение начального числа из Random
? Если да, то как? Нет Random.getSeed()
.
Random должен быть случайным. Обычно вы хотите, чтобы два Random производили разные числа, а не одинаковые числа.
Вы можете скопировать Random, используя сериализацию/десериализацию, и получить поле «seed», используя отражение. (Но я сомневаюсь, что вы должны это делать)
Если последовательность не критична для вас, вы можете считать, что клоном Random является он сам или любой new 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);
Что вы можете сделать, так это получить системное время самостоятельно, затем заполнить его генератором случайных чисел и сохранить его где-нибудь или распечатать, чтобы вы могли использовать его позже.
long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
Это может быть хорошей практикой в зависимости от вашей цели. В большинстве случаев вам не нужно извлекать текущее начальное число. Например, если ваша цель состоит в том, чтобы иметь два генератора Random, которые генерируют одну и ту же последовательность значений, вам не нужно извлекать случайное начальное число: вы просто создаете эти два объекта Random с одним и тем же (заранее установленным) начальным числом.
Java не предоставляет стандартный способ получения начального числа из объекта Random. Если вам действительно нужно это число, вы можете обойти его: сериализовать свой объект Random, сериализовать другой объект 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 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, где семенам дается «начальная схватка»:
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 для исходного состояния, которое мы извлекли.
Интересный парадокс... Я бы не назвал клонированный объект Random
случайным - в качестве обходного пути вы можете попробовать следующее: когда вы клонируете свой объект, вы можете самостоятельно установить начальное значение в обоих экземплярах Random
с одинаковыми ценность.
Причина, по которой я здесь, заключается в том, что мне нужно помнить семя на случай, если мне понадобится воссоздать то, что произошло в конкретном запуске программы. Код, который я использовал:
long seed = random.nextLong();
random.setSeed(seed);
Хотя это не совсем то, о чем просят, я думаю, что это, вероятно, то, что требуется.
CopyableRandom extends Random implements Copyable<CopyableRandom>
. stackoverflow.com/a/18531276/1809463 - person mike   schedule 30.08.2013