我正在为某个对象创建深度克隆。该对象包含Random
。
从Random
检索种子是一种好习惯吗?如果是这样,怎么样?没有Random.getSeed()
。
答案 0 :(得分:11)
你可以做的是自己获取系统时间,然后随机播种随机数生成器并将其存储在某处或打印出来以便以后使用它。
long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
答案 1 :(得分:9)
获取种子的一种更简单的方法是生成种子并将其存储为种子。我正在使用这种方法进行游戏,并希望玩家可以选择在他希望的情况下生成完全相同的世界。所以首先我创建一个没有种子的Random对象,然后让它生成一个随机数,并在另一个随机对象中使用它作为种子。每当玩家想要关卡的种子时,我就把它存放在某个地方。默认情况下,游戏仍然是随机的。
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);
答案 2 :(得分:8)
根据您的目的,这可能是一种很好的做法。在大多数情况下,您无需检索当前种子。例如,如果你的目的是让两个Random生成器生成相同的值序列,那么你不需要检索随机种子:你只需用相同的(预设)种子创建那两个Random对象。
Java没有提供从Random对象检索种子的标准方法。如果你真的需要这个数字,你可以解决它:序列化你的Random对象,序列化另一个Random对象(使用不同的种子),找到这两个字符串不同的8个字节,并从这8个字节中检索种子值。 / p>
以下是使用序列化的方法:
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.");
}
}
答案 3 :(得分:5)
随机是随机的。通常你想要两个随机数产生不同的数字而不是产生相同的数字。
您可以使用序列化/反序列化复制Random,并使用反射获取“种子”字段。 (但我怀疑你应该做什么)
除非序列对您至关重要,否则您可以认为Random的克隆本身或任何new Random()
答案 4 :(得分:4)
这可以通过反思来完成,虽然有一个小小的怪癖:
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);
幻数0x5DEECE66DL
来自Random.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位。因此,为了重建种子状态,我们必须对我们拉出的种子进行异或。
答案 5 :(得分:3)
有趣的悖论......我不会将克隆的Random
对象随机调用 - 作为一种解决方法,您可以尝试这样做:当您克隆对象时,您可以自己设置种子在具有相同值的Random
个实例中。
答案 6 :(得分:1)
我在这里的原因是我需要记住种子,以防我需要重新创建特定程序运行中发生的事情。我使用的代码是:
long seed = random.nextLong();
random.setSeed(seed);
虽然这并不是我要求的,但我认为可能是需要的。