Подтвердить что ты не робот

Как получить семя из Random на Java?

Я создаю глубокий клон для некоторого объекта. Объект содержит Random.

Хорошо ли извлекать семя из Random? Если да, то как? Не существует Random.getSeed().

4b9b3361

Ответ 1

Случайный должен быть случайным. Обычно вы хотите, чтобы два Random производили разные числа, а не для получения одинаковых чисел.

Вы можете скопировать случайное использование сериализации/де-сериализации и получить поле "семя" с помощью отражения. (Но я сомневаюсь, что вы тоже должны это делать)

Если последовательность не является критичной для вас, вы можете принять представление о том, что клон Random это сам или любой new Random()

Ответ 2

Что вы можете сделать, так это получить системное время, затем сгенерировать генератор случайных чисел и сохранить его где-нибудь или распечатать, чтобы вы могли использовать его позже.

long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);

Ответ 3

Это может быть хорошей практикой в ​​зависимости от вашей цели. Для большинства целей вам не нужно извлекать текущее семя. Например, если ваша цель состоит в том, чтобы иметь два генератора случайных чисел, которые генерируют одну и ту же последовательность значений, тогда вам не нужно извлекать случайное семя: вы просто создаете эти два случайных объекта с тем же (предварительно заданным) семенем.

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.");
  }
}

Ответ 4

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

Ответ 5

Интересный парадокс... Я бы не назвал клонированный объект Random случайным - в качестве обходного пути вы могли бы попробовать это: когда вы клонируете свой объект, вы можете установить семя самостоятельно в обоих экземплярах Random с тем же значение.

Ответ 6

Это можно сделать с помощью отражения, хотя есть небольшая причуда:

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 затраченное семя.

Ответ 7

Причина, по которой я здесь, заключается в том, что мне нужно запомнить семя, если мне нужно воссоздать то, что произошло в конкретном запуске программы. Используемый мной код:

long seed = random.nextLong();
random.setSeed(seed);

Хотя это не совсем то, о чем просят, я думаю, что это, вероятно, то, что требуется.