问题描述
我正在实施 模拟退火 (SA) 算法,我需要在其中复制状态(例如,记住迄今为止最好的解决方案).
I'm implementing a simulated annealing (SA) algorithm, where I need to copy states (e. g. to remember best solution so far).
我实现了一个复制方法,因为不鼓励使用 java 的 clone().
I implemented a copy method, since it's discouraged to use java's clone().
SA 是一种启发式算法,因此下一步要采取的措施是随机确定的.这是通过使用 Random 对象来完成的,我也想复制它.
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.
但如果我在复制过程之前对随机数执行一些操作,Random 对象的内在状态(即种子)会发生变化,并且复制的行为会有所不同.
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.
那么我怎样才能获得 java.util.Random 实例的精确副本?
So how can I get an exact copy of an instance of 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() (因为所有其他方法都依赖于那个),以及其他一些保持一致性的东西.
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.
请随意添加您的想法!
由于其他问题与获取种子有关:可以轻松地将 getSeed() 方法添加到我的解决方案中.或 getInitialSeed()、getCurrentSeed().
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(" TEST: INTEGER "); for (int i = 0; i < 10; i++) System.out.println("CR = " + cr.nextInt(50) + " COPY = " + copy.nextInt(50) + " "); Random anotherCopy = (copy instanceof CopyableRandom) ? ((CopyableRandom) copy).copy() : new Random(); System.out.println(" TEST: DOUBLE "); for (int i = 0; i < 10; i++) System.out.println("CR = " + cr.nextDouble() + " A_COPY = " + anotherCopy.nextDouble() + " "); } }
这里是main方法的输出:
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
<小时>
我还进行了一项测试,将 CopyableRandom 与 Random 进行了比较.它产生了相同的结果.
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);