3

I'm implementing a simulated annealing (SA) algorithm, where I need to copy states (e. g. to remember best solution so far).

I implemented a copy method, since it's discouraged to use java's clone().

SA is a heuristic algorithm, so the next step to take is determined randomly. This is done by using a Random object, which I want to copy too.

Although it's not requiered by the algorithm, I want the copy to have exactly the same state. But this is only the case, if I make a 'copy' direct after object creation and initialize it with the same seed.

But if I perform some operations on the random before the copy process , the intrinsic state (i. e. the seed) of theRandom object changes and the copy behaves differently.

So how can I get an exact copy of an instance of java.util.Random?


EXAMPLE

public class State
{
  private final Random r;
  private final long seed;

  private Object currentOperand;

  public State()
  {
    this(System.nanoTime(), null);
  }

  private State(long seed, Object currentOperand)
  {
    this.seed = seed;
    this.r = new Random(seed);
    this.currentOperand = currentOperand;
  }

  public State copy()
  {
    return new State(seed, currentOperand);
  }

  public void doSth()
  {
    /* operation with random operand */
    currentOperand = r.nextInt(100);
  }

  public void redo()
  {
    // redo then set to null
    currentOperand = null;
  }

  /* for completeness' sake... since it's simulated annealing */
  public int computeEnergy() { return 0; }
}
mike
  • 4,929
  • 4
  • 40
  • 80
  • 2
    http://stackoverflow.com/questions/6001368/how-do-i-get-the-seed-from-a-random-in-java – NPE Aug 28 '13 at 16:34
  • I'll guess it would be easier to specify in the java doc contract, that not the exact state is copied and to specify what is possible than following the approach in the linked answer. – mike Aug 28 '13 at 16:44
  • I accepted my own answer, since the solution I provided was easy to (re)use. – mike Sep 02 '13 at 14:04

3 Answers3

3

I came up with an own solution. It mainly overrides next() in Random (since all other methods rely on that one), and some other stuff to keep the consistency.

It delivers an exact copy of the instance this method was invoked on (whether it makes sense to make a copy of a random instance is another topic...^^). It should exactly behave like its super class, at least that was my intention.

Feel free to add your thoughts!

Since other questions were about getting the seed: One could easily add a getSeed() method to my solution. Or getInitialSeed(), getCurrentSeed().

/* Bounded parameter type since a class that implements this interface
 * should only be able to create copies of the same type (or a subtype).
 */
public interface Copyable<T extends Copyable<T>>
{
  public T copy();
}

public class CopyableRandom extends Random implements Copyable<CopyableRandom>
{
  private final AtomicLong seed = new AtomicLong(0L);

  private final static long multiplier = 0x5DEECE66DL;
  private final static long addend = 0xBL;
  private final static long mask = (1L << 48) - 1;

  public CopyableRandom() { this(++seedUniquifier + System.nanoTime()); }
  private static volatile long seedUniquifier = 8682522807148012L;

  public CopyableRandom(long seed) { this.seed.set((seed ^ multiplier) & mask); }

  /* copy of superclasses code, as you can seed the seed changes */
  @Override
  protected int next(int bits)
  {
    long oldseed, nextseed;
    AtomicLong seed_ = this.seed;
    do
    {
      oldseed = seed_.get();
      nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed_.compareAndSet(oldseed, nextseed));
    return (int) (nextseed >>> (48 - bits));
  }

  /* necessary to prevent changes to seed that are made in constructor */
  @Override
  public CopyableRandom copy() { return new CopyableRandom((seed.get() ^ multiplier) & mask); }

  public static void main(String[] args)
  {
    CopyableRandom cr = new CopyableRandom();

    /* changes intern state of cr */
    for (int i = 0; i < 10; i++)
      System.out.println(cr.nextInt(50));

    Random copy = cr.copy()

    System.out.println("\nTEST: INTEGER\n");
    for (int i = 0; i < 10; i++)
      System.out.println("CR\t= " + cr.nextInt(50) + "\nCOPY\t= " + copy.nextInt(50) + "\n");

    Random anotherCopy = (copy instanceof CopyableRandom) ? ((CopyableRandom) copy).copy() : new Random();
    System.out.println("\nTEST: DOUBLE\n");
    for (int i = 0; i < 10; i++)
      System.out.println("CR\t= " + cr.nextDouble() + "\nA_COPY\t= " + anotherCopy.nextDouble() + "\n");
  }
}

And here the output of the main method:

19
23
26
37
41
34
17
28
29
6

TEST: INTEGER

CR      = 3
COPY    = 3

CR      = 18
COPY    = 18

CR      = 25
COPY    = 25

CR      = 9
COPY    = 9

CR      = 24
COPY    = 24

CR      = 5
COPY    = 5

CR      = 15
COPY    = 15

CR      = 5
COPY    = 5

CR      = 30
COPY    = 30

CR      = 26
COPY    = 26


TEST: DOUBLE

CR      = 0.7161924830704971
A_COPY  = 0.7161924830704971

CR      = 0.06333509362539957
A_COPY  = 0.06333509362539957

CR      = 0.6340753697524675
A_COPY  = 0.6340753697524675

CR      = 0.13546677259518425
A_COPY  = 0.13546677259518425

CR      = 0.37133033932410586
A_COPY  = 0.37133033932410586

CR      = 0.796277965335522
A_COPY  = 0.796277965335522

CR      = 0.8610310118615391
A_COPY  = 0.8610310118615391

CR      = 0.793617231340077
A_COPY  = 0.793617231340077

CR      = 0.3454111197621874
A_COPY  = 0.3454111197621874

CR      = 0.25314618087856255
A_COPY  = 0.25314618087856255

I also had a test where I compared CopyableRandom against Random. It yielded the same results.

long seed = System.nanoTime();

Random cr  = new CopyableRandom(seed);
Random cmp = new Random(seed);
mike
  • 4,929
  • 4
  • 40
  • 80
  • Only problem I see so far is that `Random` implements `Serializable`. I think I should do sth about it. Sun recommends to throw a `NotSerializableException` if one wants to prevent it... – mike Aug 30 '13 at 14:16
  • This may result in a different sequence of Gaussian samples being returned because the normal distribution is sampled two at a time on each second call of the nextGaussian() method, with one of the samples remembered for the next call. – Benjamin Berger Oct 11 '21 at 14:32
3

I know this is an old question with an accepted answer, but I came across this while looking for an answer myself, and I wound up taking a different approach.

Seeing mike's note above that Random implements Serializable, I just used that to make the copy:

/**
 * Uses serialization to create a copy of the given Random, needed for
 * repeatability in some tests.
 */
public static Random cloneRandom(Random src) throws Exception {
    ByteArrayOutputStream bo = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bo);
    oos.writeObject(src);
    oos.close();
    ObjectInputStream ois = new ObjectInputStream(
            new ByteArrayInputStream(bo.toByteArray()));
    return (Random)(ois.readObject());
}

It probably doesn't perform as well as mike's CopyableRandom, but it's a lot simpler, and sufficent for my little unit test.

(I had an existing unit test which started with a Random with a known seed & then performed a series of operations; I was trying to add a bit in the middle of the test, and wanted a copy of the Random; calling nextLong() or similar in the middle of the test to get the/a seed was going to change the seed, blowing up the rest of the test. Really I just wanted something like Random.getSeed().)

kuhrusty
  • 141
  • 1
  • 5
1

I think you should store in your State class, not only starting seed but also number of calls to nextInt() you already did. This is due to the intrisic fact that Random generates pseudo-random sequence of numbers. That is:

A pseudo random number generator can be started from an arbitrary starting state using a seed state. It will always produce the same sequence thereafter when initialized with that state

Let me explain show you with a sample first:

public static void main(String[] args){

     Random r = new Random(42);      
     Random s = new Random(42);

     for(int i = 0; i < 5; i++){
       System.out.println("First random " +r.nextInt());
     }

     for(int i = 0; i < 5; i++){
       System.out.println("Second random " +s.nextInt());
     }

  }

which result is:

First random -1170105035
First random 234785527
First random -1360544799
First random 205897768
First random 1325939940
Second random -1170105035
Second random 234785527
Second random -1360544799
Second random 205897768
Second random 1325939940

Since both Random instances start with same seed, I always get the same sequence of numbers.

So, when copying object you should init the new Random to the same seed of source (you already do this) and "consume" as much calls of nextInt() that you already used in source object (this is why you have to keep that number).

After done this, calling the nextInt() on the copy will give you the same result that source object. I hope my code is enough to refactor yours and let you understand my solution.

To understand better pseudorandom number generators (PRNG), also known as a deterministic random bit generators (DRBG), you can look at this Wikipedia article

giampaolo
  • 6,906
  • 5
  • 45
  • 73
  • As I said the intrinsic state of the random changes per number generation. Okay, did not think of having the `Random` perform 'dry runs' till the state is same. Hmm, but don't know what I should think of this dry running... – mike Aug 29 '13 at 21:35
  • In what sense? My solution allows you to get implement also something like previousInt() if you need, just create a new Random and go for (n-1) 'dry runs' assuming n is current number of calls of nextInt(). In this case, you don't even need a copy of State to do the rollback. – giampaolo Aug 29 '13 at 21:55
  • The state class was just a simplified example. Nevertheless, your approach creates a 1:1 copy of `Random`. But I not sure, if this dry running could be a performance issue, since I'm in the area of 10,000 - 20,000 runs. Don't know how expensive a `nextInt()` call is. I also am not sure if it's good practice to 'copy' a random at all. – mike Aug 29 '13 at 22:44
  • Ok. Just to try a guess, if you have many copy of this objects, and you are worried about performances, you can always store into a static array the result of many runs of a Random, and then simulate nextInt using accessing that array from 0 and incrementing an index. Of course, early optimization is 99.9% times a bad idea :). But as said, it's just a guess. – giampaolo Aug 30 '13 at 05:07
  • 1
    I added my own solution that works without 'dry running' and adds no logic to the client that wants to use and copy a `Random`. – mike Aug 30 '13 at 11:01