35

I am creating a deep clone for some object. The object contains a Random.

Is it good practice to retrieve the seed from the Random? If so, how? There isn't a Random.getSeed().

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Marnix
  • 6,384
  • 4
  • 43
  • 78
  • 2
    I provided a way to get a clean copy of a Random object (resp. of its subclass), I derived a class `CopyableRandom extends Random implements Copyable` therefore. http://stackoverflow.com/a/18531276/1809463 – mike Aug 30 '13 at 11:36

7 Answers7

25

A much more simple way of getting a seed is to generate one and store that as the seed. I am using this method for a game and want to give the player the option to generate the exact same world if he wishes too. So first I create a Random object without a seed then let that one generate a random number and use that in another random object as the seed. Whenever the player want the seed of the level I have it stored somewhere. By default the game is still 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);
Madmenyo
  • 8,389
  • 7
  • 52
  • 99
  • 1
    This could be used for creating two Randoms with the same state. When cloning, generate a seed with rand.nextLong() and seed both the original Random and its copy with that seed. From then on, both the original and the copy generate the same numbers. – Markus Klinik Jul 10 '20 at 07:20
9

What you can do is get the system time yourself, then seed the random number generator with it and store it somewhere or print it so you can use it later.

long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
Asher E
  • 139
  • 1
  • 2
9

This can be done via reflection, although there is a small quirk:

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);

The magic number 0x5DEECE66DL comes from the source code of Random.java, where seeds are given an "initial scramble" of:

private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1;
//...
private static long initialScramble(long seed) {
    return (seed ^ multiplier) & mask;
}

which XOR's them with a random number and truncates them to 48 bits. Thus, to recreate the seed state, we have to XOR the seed we pulled.

KevinL
  • 1,238
  • 11
  • 28
8

It can be a good practice depending on your purpose. For most purposes you don't need to retrieve the current seed. For example if your purpose is to have two Random generators which generate the same sequence of values, then you don't need to retrieve the random seed: you just create those two Random objects with the same (pre-set) seed.

Java doesn't provide a standard way of retrieving the seed from a Random object. If you really need that number, you may work around it: serialize your Random object, serialize another Random object (with a different seed), find the 8 bytes where these two strings differ, and retrieve the seed value from those 8 bytes.

Here is how to do it with serialization:

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.");
  }
}
pts
  • 80,836
  • 20
  • 110
  • 183
  • Take a look at my solution. It easier to get the seed there. http://stackoverflow.com/questions/18493319/copy-instance-variable-of-type-java-util-random-to-create-object-in-same-state/18531276#18531276 – mike Aug 30 '13 at 12:56
5

A Random is intended to be random. Usually you want two Random to produce different numbers rather than to produce the same numbers.

You can copy a Random using serialisation/de-serialisation and get the "seed" field using reflection. (But I doubt you should be doing either)

Unless the sequence is critical to you, you can take the view that the clone of a Random is itself or any new Random()

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 14
    Apologies for the thread necromancy, but there's at least one non-obscure use case for preserving the sequence of a random number generator across executions I can name off the top of my head: video games, particularly strategic ones where "bad luck" is something the player is supposed to manage. A notable example is X-COM, which originally persisted the random generator state in the save game, to prevent players from quicksaving before every shot their soldiers take, and reloading if it misses. (The expansion has a setting in the configuration to enable "save scumming" explicitly.) – millimoose Oct 12 '15 at 20:25
  • @millimoose in that case, you can keep reseeding the Random even second or every 100 ms, that way you can replay the last 100 ms + – Peter Lawrey Oct 13 '15 at 19:36
  • 2
    That would do nothing against save-scumming - the player could get different results depending on which 100ms interval it was undertaken in, which for all intents and purposes would achieve nothing. In a video game that emphasises long-term strategy - as opposed to gambling - and allows saving and loading, you don't want to prevent the player knowing the result of the next "roll". You want the inverse - the player shouldn't to be able to avoid fate, and "reroll" every time until the outcome is beneficial. (E.g. reload repeatedly until your soldier hits, or until the alien misses.) – millimoose Oct 14 '15 at 01:36
  • 3
    Another case where this is useful is when using a random number generator to generate data for testing. I write tests such that they set the seed that so that every run is the same; otherwise I can't reproduce any bugs that are found. It would also be useful to be able to find the current seed value so that the sequence could be resumed where it left off. – Chris Westin Mar 31 '20 at 21:15
  • 2
    There are a tons of cases where you want to recreate a Random object with same seed. Poor anwer: you just need to initialize Random with a random.nextLong() seed, and take note of it. – Jack Dec 03 '20 at 22:18
3

Interesting paradox... I would not call the cloned Random object random - as a workaround you could try this: when you're cloning your object you can set seed by yourself in both Random instances with the same value.

Lukasz
  • 7,572
  • 4
  • 41
  • 50
1

The reason I'm here is that I need to remember the seed in case I need to recreate what happened in a particular program run. The code I used is:

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

Although this isn't quite what is asked for, I think it is probably what is required.