复制java.util.Random类型的实例变量以创建处于相同状态的对象

复制java.util.Random类型的实例变量以创建处于相同状态的对象,java,algorithm,random,copy,clone,Java,Algorithm,Random,Copy,Clone,我正在实现一个(SA)算法,在这里我需要复制状态(例如,记住迄今为止最好的解决方案) 我实现了一个复制方法,因为不鼓励使用java的clone() SA是一种启发式算法,因此下一步是随机确定的。这是通过使用Random对象完成的,我也想复制它 虽然算法不需要它,但我希望副本具有完全相同的状态。 但只有这样,如果我在对象创建之后直接创建一个“副本”,并用相同的种子初始化它 但是,如果在复制过程之前对随机对象执行一些操作,则random对象的固有状态(即种子)会发生变化,并且复制行为也会不同 那么如

我正在实现一个(SA)算法,在这里我需要复制状态(例如,记住迄今为止最好的解决方案)

我实现了一个复制方法,因为不鼓励使用java的
clone()

SA是一种启发式算法,因此下一步是随机确定的。这是通过使用
Random
对象完成的,我也想复制它

虽然算法不需要它,但我希望副本具有完全相同的状态。 但只有这样,如果我在对象创建之后直接创建一个“副本”,并用相同的种子初始化它

但是,如果在复制过程之前对随机对象执行一些操作,则
random
对象的固有状态(即种子)会发生变化,并且复制行为也会不同

那么如何获得
java.util.Random
实例的精确副本呢?


示例

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

我认为您应该在
State
类中存储,不仅要存储seed,还要存储对
nextInt()
的调用次数。这是由于
Random
生成伪随机数字序列这一本质事实造成的。即:

伪随机数生成器可以使用种子状态从任意启动状态启动。在使用该状态初始化之后,它将始终生成相同的序列

让我先解释一下,给你看一个例子:

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

  }
由于两个随机实例都以相同的种子开始,所以我总是得到相同的数字序列

因此,在复制对象时,您应该将新的
随机
初始化到源代码的同一种子(您已经这样做了),并“使用”源对象中已经使用的
nextInt()
的调用(这就是为什么您必须保留该数字)

完成此操作后,对副本调用
nextInt()
,将得到与源对象相同的结果。我希望我的代码足以重构您的代码,让您了解我的解决方案


为了更好地理解伪随机数生成器(PRNG),也称为确定性随机位生成器(DRBG),您可以看看这个

我提出了自己的解决方案。它主要覆盖
Random
中的
next()
(因为所有其他方法都依赖于该方法),以及其他一些东西来保持一致性

它提供了调用此方法的实例的精确副本(创建随机实例的副本是否有意义是另一个主题…^^)。它应该表现得像它的超级类,至少这是我的意图

请随意添加您的想法

因为其他问题是关于获取种子的:可以很容易地在我的解决方案中添加
getSeed()
方法。或者
getInitialSeed()
getCurrentSeed()


我还做了一个测试,比较了CopyableRandom和Random。它产生了同样的结果

long seed = System.nanoTime();

Random cr  = new CopyableRandom(seed);
Random cmp = new Random(seed);

我知道这是一个老生常谈的问题,有一个公认的答案,但我自己在寻找答案时遇到了这个问题,最终我采取了不同的方法

看到mike在上面提到的
Random
实现了
Serializable
,我就用它来制作副本:

/**
 * 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());
}
它的性能可能不如mike的
CopyableRandom
,但它简单得多,足以进行我的小单元测试


(我有一个现有的单元测试,用一个已知的种子从<代码>随机开始,然后执行一系列操作;我试图在测试中间添加一点,并且想要一个<代码>随机的副本;调用<代码> NExtCon()或类似的测试中,以获得/A种子将改变种子,炸毁剩余的测试。真的我只是想要一些东西,比如“代码>随机。GET S播种())/<代码> < /P>我猜在java文档中更容易指定,我接受了我自己的答案,因为我提供的解决方案很容易(重新)使用。正如我所说的,每个数字生成的随机变化的内在状态。好的,我没有想过让

随机
执行“干运行”,直到状态相同。嗯,但我不知道我该怎么看这个干跑步…在什么意义上?我的解决方案允许您也实现类似于previousInt()的东西,如果需要,只需创建一个新的随机变量,并进行(n-1)“干运行”,假设n是nextInt()的当前调用数。在本例中,您甚至不需要状态的副本来执行回滚。尽管如此,您的方法还是创建了一个1:1的
Random
。但是我不确定,这是否是一个性能问题,因为我在10000-20000跑的区域。不知道一个
nextInt()
调用有多贵。我也不确定“复制”一个随机项是否是一个好的做法。试想一下,如果您有许多此对象的副本,并且担心性能,则始终可以将多次随机运行的结果存储到静态数组中,然后使用从0访问该数组并增加索引来模拟nextInt。当然,早期优化是一个坏主意的99.9倍:)。但如前所述,这只是一个猜测。我添加了我自己的解决方案,该解决方案不需要“干运行”,也不向希望使用和复制
Random
的客户端添加任何逻辑。到目前为止,我看到的唯一问题是
Random
实现了
Serializable
。我想我应该做点什么。Sun建议抛出一个
NotSerializableException
,如果想要阻止它。。。
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");
  }
}
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
long seed = System.nanoTime();

Random cr  = new CopyableRandom(seed);
Random cmp = new Random(seed);
/**
 * 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());
}