如何从Java中的Random中获取种子?

Mar*_*nix 27 java random clone random-seed

我正在为某个对象创建一个深度克隆.该对象包含一个Random.

从中检索种子是一种好习惯Random吗?如果是这样,怎么样?没有Random.getSeed().

Mad*_*nyo 11

获得种子的一种更简单的方法是生成种子并将其存储为种子.我正在使用这种方法进行游戏,并希望玩家可以选择在他希望的情况下生成完全相同的世界.所以首先我创建一个没有种子的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);
Run Code Online (Sandbox Code Playgroud)


小智 9

您可以做的是自己获取系统时间,然后随机播种随机数生成器并将其存储在某处或打印它以便以后使用它.

long rgenseed = System.currentTimeMillis();
Random rgen = new Random();
rgen.setSeed(rgenseed);
System.out.println("Random number generator seed is " + rgenseed);
Run Code Online (Sandbox Code Playgroud)

  • 种子在内部使用随机实例进行更改. (9认同)

pts*_*pts 8

根据您的目的,这可能是一种很好的做法.在大多数情况下,您无需检索当前种子.例如,如果您的目的是让两个Random生成器生成相同的值序列,那么您不需要检索随机种子:您只需使用相同(预设)种子创建这两个Random对象.

Java没有提供从Random对象检索种子的标准方法.如果你真的需要这个数字,你可以解决它:序列化你的Random对象,序列化另一个Random对象(使用不同的种子),找到这两个字符串不同的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.");
  }
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*rey 5

随机意图是随机的.通常,您希望两个Random生成不同的数字而不是生成相同的数字.

您可以使用序列化/反序列化复制随机数并使用反射获取"种子"字段.(但我怀疑你应该做什么)

除非序列对您至关重要,否则您可以认为Random的克隆本身或任何 new Random()

  • 对于线程坏死的道歉,但是至少有一个非模糊的用例来保存执行中随机数生成器的序列我可以说出我的头脑:视频游戏,特别是那些"运气不好"的战略游戏玩家应该管理.一个值得注意的例子是X-COM,它最初在保存游戏中保持随机生成器状态,以防止玩家在他们的士兵每次射击之前快速节省,并且如果它未命中则重新加载.(扩展在配置中有一个设置,以显式启用"保存浮渣".) (12认同)
  • 这对于拯救浮渣没有任何作用 - 玩家可以得到不同的结果,这取决于它所承担的100ms间隔,这对于所有意图和目的都没有任何作用.在强调长期策略的视频游戏中 - 与赌博相反 - 并且允许保存和加载,您不希望阻止玩家知道下一个"滚动"的结果.你想要反过来 - 玩家不应该能够避免命运,并且每次"重新滚动"直到结果有益.(例如重复重装,直到你的士兵击中,或直到外星人错过.) (2认同)
  • 另一种有用的情况是使用随机数生成器生成用于测试的数据。我编写测试,以便它们设置种子,以便每次运行都是相同的;否则我无法重现发现的任何错误。能够找到当前的种子值也很有用,这样序列就可以从中断的地方恢复。 (2认同)
  • 在很多情况下,您想要使用相同的种子重新创建随机对象。糟糕的答案:您只需要使用 random.nextLong() 种子初始化 Random,并记下它。 (2认同)

Kev*_*inL 5

尽管有一个小怪癖,但这可以通过反射来完成:

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);
Run Code Online (Sandbox Code Playgroud)

幻数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;
}
Run Code Online (Sandbox Code Playgroud)

使用异或对其进行异或,然后将其截断为48位。因此,要重新创建种子状态,我们必须对提取的种子进行XOR。