3

状態をコピーする必要があるシミュレーテッド アニーリング(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; }
}
4

3 に答える 3

3

私は独自の解決策を思いつきました。これは主に (他のすべてのメソッドがそれに依存しているため) オーバーライドnext()Random、一貫性を保つために他のいくつかのものをオーバーライドします。

このメソッドが呼び出されたインスタンスの正確なコピーを提供します (ランダムなインスタンスのコピーを作成することが理にかなっているのかどうかは別のトピックです...^^)。少なくともそれが私の意図でした。

あなたの考えを自由に追加してください!

他の質問はシードの取得に関するものだったので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);
于 2013-08-30T11:00:06.753 に答える
1

Stateシードを開始するだけでなく、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());
     }

  }

結果は次のとおりです。

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

両方の Random インスタンスが同じシードで始まるため、常に同じ数列を取得します。

したがって、オブジェクトをコピーするときはRandom、ソースの同じシードに新しいものを初期化して (既にこれを行っています) 、ソース オブジェクトで既に使用した呼び出しを「消費」するnextInt()必要があります (これが、その数を保持する必要がある理由です)。

これを行った後、コピーで を呼び出すと、nextInt()ソース オブジェクトと同じ結果が得られます。私のコードがあなたのコードをリファクタリングし、私のソリューションを理解してもらうのに十分であることを願っています.

決定論的ランダム ビット ジェネレーター (DRBG) とも呼ばれる疑似乱数ジェネレーター (PRNG) をよりよく理解するには、このウィキペディアの記事を参照してください。

于 2013-08-29T20:54:08.183 に答える