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

标签 java algorithm random copy clone

我正在实现 simulated annealing (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; }
}

最佳答案

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

它提供调用此方法的实例的精确副本(制作随机实例的副本是否有意义是另一个主题...^^)。它的行为应该完全像它的父类(super class),至少那是我的意图。

请随意添加您的想法!

因为其他问题是关于获取种子的:可以轻松地向我的解决方案添加 getSeed() 方法。或 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");
  }
}

这里是主要方法的输出:

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

我还进行了一项测试,将 CopyableRandom 与 Random 进行了比较。它产生了相同的结果。

long seed = System.nanoTime();

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

关于java - 复制 java.util.Random 类型的实例变量以创建相同状态的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18493319/

相关文章:

java - 中点公式溢出错误

c++ - 如果冒泡排序中交换的元素是连通的,则查找所有非连通元素的最大子集

facebook-graph-api - 如何计算 iOS sdk 中 Facebook 好友的数量?

java - 如何计算 JavaFX TableView 中依赖于其他行的单元格的值?

java - Android 中不安全的蓝牙连接

java - 输出 12 次表 Java

c# - 不重复随机数

java - 方法返回值在方法本身中的用法

algorithm - 查找总和为 0 mod n 的数组

c - 如果这样做的话,可以多次播种随机发生器吗?