web-dev-qa-db-ja.com

Javaのランダムからシードを取得するにはどうすればよいですか?

あるオブジェクトのディープクローンを作成しています。オブジェクトにはRandomが含まれています。

Randomからシードを取得することをお勧めしますか?もしそうなら、どのように? Random.getSeed()はありません。

27
Marnix

ランダムはランダムであることが意図されています。通常、2つのランダムで同じ数を生成するのではなく、異なる数を生成する必要があります。

シリアル化/逆シリアル化を使用してランダムをコピーし、リフレクションを使用して「シード」フィールドを取得できます。 (しかし、私はあなたがどちらかをすべきだとは思わない)

シーケンスが重要でない限り、ランダムのクローンはそれ自体または任意のnew Random()であるという見方をすることができます。

4
Peter Lawrey

自分でシステム時刻を取得し、乱数ジェネレーターにシードしてどこかに保存するか、後で使用できるように印刷することができます。

long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
10
Asher E

シードを取得するはるかに簡単な方法は、シードを生成してそれをシードとして保存することです。私はこの方法をゲームに使用しており、プレイヤーが望む場合はまったく同じ世界を生成するオプションをプレイヤーに提供したいと考えています。したがって、最初にシードなしでランダムオブジェクトを作成し、次にそのオブジェクトに乱数を生成させ、それを別のランダムオブジェクトでシードとして使用します。プレイヤーがレベルのシードを必要とするときはいつでも、私はそれをどこかに保存します。デフォルトでは、ゲームはまだランダムです。

    Random Rand = new Random();
    //Store a random seed
    long seed = Rand.nextLong();
    //Set the Random object seed
    Rand.setSeed(seed);

    //do random stuff...

    //Wonder what the seed is to reproduce something?
    System.out.println(seed);
9
Madmenyo

目的によっては、良い習慣になる場合があります。ほとんどの場合、現在のシードを取得する必要はありません。たとえば、同じ値のシーケンスを生成する2つのランダムジェネレーターを使用することが目的の場合、ランダムシードを取得する必要はありません。同じ(プリセット)シードを使用して2つのランダムオブジェクトを作成するだけです。

Javaは、ランダムオブジェクトからシードを取得する標準的な方法を提供していません。その番号が本当に必要な場合は、回避できます。Randomオブジェクトをシリアル化し、別のRandomオブジェクト(異なるシードを使用)をシリアル化し、これら2つの文字列が異なる8バイトを見つけ、それらの8バイトからシード値を取得します。

シリアル化でそれを行う方法は次のとおりです。

import Java.io.ByteArrayOutputStream;
import Java.io.IOException;
import Java.io.ObjectOutputStream;
import Java.util.Random;
public class SeedGetter {
  static long getSeed(Random random) {
    byte[] ba0, ba1, bar;
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
      ObjectOutputStream oos = new ObjectOutputStream(baos);
      oos.writeObject(new Random(0));
      ba0 = baos.toByteArray();
      baos = new ByteArrayOutputStream(128);
      oos = new ObjectOutputStream(baos);
      oos.writeObject(new Random(-1));
      ba1 = baos.toByteArray();
      baos = new ByteArrayOutputStream(128);
      oos = new ObjectOutputStream(baos);
      oos.writeObject(random);
      bar = baos.toByteArray();
    } catch (IOException e) {
      throw new RuntimeException("IOException: " + e);
    }
    if (ba0.length != ba1.length || ba0.length != bar.length)
      throw new RuntimeException("bad serialized length");
    int i = 0;
    while (i < ba0.length && ba0[i] == ba1[i]) {
      i++;
    }
    int j = ba0.length;
    while (j > 0 && ba0[j - 1] == ba1[j - 1]) {
      j--;
    }
    if (j - i != 6)
      throw new RuntimeException("6 differing bytes not found");
    // The constant 0x5DEECE66DL is from
    // http://download.Oracle.com/javase/6/docs/api/Java/util/Random.html .
    return ((bar[i] & 255L) << 40 | (bar[i + 1] & 255L) << 32 |
            (bar[i + 2] & 255L) << 24 | (bar[i + 3] & 255L) << 16 |
            (bar[i + 4] & 255L) << 8 | (bar[i + 5] & 255L)) ^ 0x5DEECE66DL;
  }
  public static void main(String[] args) {
    Random random = new Random(12345);
    if (getSeed(random) != 12345)
      throw new RuntimeException("Bad1");
    random.nextInt();
    long seed = getSeed(random);
    if (seed == 12345)
      throw new RuntimeException("Bad2");
    Random random2 = new Random(seed);
    if (random.nextInt() != random2.nextInt())
      throw new RuntimeException("Bad3");
    System.out.println("getSeed OK.");
  }
}
8
pts

これは、小さな癖がありますが、リフレクションを介して行うことができます。

Random r = ...;  //this is the random you want to clone
long theSeed;
try
{
    Field field = Random.class.getDeclaredField("seed");
    field.setAccessible(true);
    AtomicLong scrambledSeed = (AtomicLong) field.get(r);   //this needs to be XOR'd with 0x5DEECE66DL
    theSeed = scrambledSeed.get();
}
catch (Exception e)
{
    //handle exception
}
Random clonedRandom = new Random(theSeed ^ 0x5DEECE66DL);

魔法の数0x5DEECE66DLRandom.Java のソースコードから取得されます。ここで、シードには次の「初期スクランブル」が与えられます。

private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1;
//...
private static long initialScramble(long seed) {
    return (seed ^ multiplier) & mask;
}

xORはそれらを乱数で処理し、48ビットに切り捨てます。したがって、シード状態を再作成するには、プルしたシードをXORする必要があります。

4
KevinL

興味深いパラドックス...クローンされたRandomオブジェクトランダム-を回避策として呼び出すことはありません。これを試すことができます。オブジェクトをクローンするときに、両方で自分でシードを設定できます。同じ値のRandomインスタンス。

3
Lukasz

私がここにいる理由は、特定のプログラムの実行で起こったことを再現する必要がある場合に備えて、シードを覚えておく必要があるためです。私が使用したコードは次のとおりです。

long seed = random.nextLong();
random.setSeed(seed);

これは求められていることではありませんが、おそらく必要なことだと思います。

1