Как я могу получить семя от Random в Java? - PullRequest
27 голосов
/ 14 мая 2011

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

. Хорошая ли практика - извлечь семя из Random?Если так, то как?Там нет Random.getSeed().

Ответы [ 7 ]

11 голосов
/ 21 июня 2012

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

long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
9 голосов
/ 17 июля 2015

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

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

Java не предоставляет стандартный способ получения начального числа из объекта Random.Если вам действительно нужно это число, вы можете обойти его: сериализовать ваш объект Random, сериализовать другой объект Random (с другим начальным числом), найти 8 байтов, в которых эти две строки различаются, и извлечь начальное значение из этих 8 байт.1003 *

Вот как это сделать с сериализацией:

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.");
  }
}
5 голосов
/ 14 мая 2011

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

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

Если последовательность не является критической для вас, вы можете принять мнение, что клон Случайного является самим собой или любым

4 голосов
/ 26 марта 2015

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

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 отобранного семени.

3 голосов
/ 14 мая 2011

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

1 голос
/ 06 марта 2016

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

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

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

...