Java使用种子生成随机数

62

这是我的代码,用一个种子作为参数来生成随机数:

double randomGenerator(long seed) {
    Random generator = new Random(seed);
    double num = generator.nextDouble() * (0.5);

    return num;
}

每次我给出一个种子并尝试生成100个数字时,它们都是相同的。
我该如何解决这个问题?


4
那就是种子应该做的事情。您只为一个种子生成一个数字。您想要实现什么? - Peter Lawrey
4
您可以使用System.currentTimeMillis()作为随机数生成器的种子来生成随机结果。不提供种子也会产生随机结果。如果不提供种子,我不确定随机数生成器是否会在某个时候开始重复。 - WVrock
7个回答

92
如果您提供相同的种子,那就很正常。这是一项重要的功能,可以用于测试。
请查看此文章,了解伪随机数生成和种子:Pseudorandom number generator

伪随机数生成器(PRNG),也称为确定性随机位生成器DRBG,是一种算法,用于生成近似于随机数属性的数字序列。该序列在本质上不是真正的随机数,因为它完全由一个相对较小的初始值集合(称为PRNG的状态,其中包括真正的随机种子)确定。

如果您想要不同的序列(通常情况下,不调整或调试算法时),应该调用零参数构造函数,该构造函数使用nanoTime尝试每次获得不同的种子。当然,这个Random实例应该在您的方法之外保留。
您的代码应该像这样:
private Random generator = new Random();
double randomGenerator() {
    return generator.nextDouble()*0.5;
}

毫秒是非常长的时间,使用System.nanoTime()会更安全。 - kukis
2
@kukis 没关系。这只是一个数字,在每次程序启动时都不相同。因为你的程序只设置种子一次。 - Denys Séguret
5
使用 "new Random()" 等价或更好,参见源码: public Random() { this(seedUniquifier() ^ System.nanoTime()); } - dermoritz
假设我在一个类中定义了new Random(42),而在另一个类中我再次只是使用new Random(),但结果却不同...是否有任何技巧可以全局设置随机种子,并且对于每个类,即使我初始化了新的Random(),也能获得相同的值。如果有人能回答这个问题,那将非常有帮助。 - Gaurav Koradiya
@GauravKoradiya 这将是一个问题,通常会违反设计原则。因此没有办法。您可以将种子或 Random 实例传递到需要它的位置。 - Denys Séguret

40

简单的方法是使用:

Random rand = new Random(System.currentTimeMillis());

这是生成随机数的最佳方式。


6
特殊情况下,当您的代码运行速度过快时,可能会以相同的种子开始。 - wutzebaer
正如@dermoritz所指出的那样:使用“new Random()”更好,请参见源代码:public Random() { this(seedUniquifier() ^ System.nanoTime()); } - Jason Smith

11

你不应该在方法范围内创建新的Random。将其设置为类成员:

public class Foo {
   private Random random 

   public Foo() {
       this(System.currentTimeMillis());
   }

   public Foo(long seed) {
       this.random = new Random(seed);
   }

   public synchronized double getNext() {
        return generator.nextDouble();
   }
}

这只是一个例子。我认为以这种方式包装Random没有任何价值。可以将其放入使用它的类中。


这个回答几乎完全与问题无关。 - Chris Mountford
7
虽然这只是一次代码重构,看起来似乎与之前无关,但实际上它确实相关。原先的代码在每次方法调用时都重新创建了一个随机数生成器,因此 nextDouble 总是返回随机序列中的 第一个 双精度浮点数。而这个答案只会创建一次随机数生成器,每次调用 getNext 时会逐步遍历随机数序列并返回不同的数值。 - Brandon

8

这就是伪随机数生成器的原理。这些数字并不真正随机,它们是通过一个确定性算法生成的,但是根据不同的种子,生成的数字序列会有所不同。由于你总是使用相同的种子,因此你总是得到相同的序列。


2
问题在于您再次播种了随机生成器。每次播种它,随机数生成器的初始状态都会被重置,并且您生成的第一个随机数将是初始状态后的第一个随机数。

1
这里的一些示例创建了一个新的Random实例,但这是不必要的。也没有理由像某个解决方案那样使用synchronized。相反,应该利用ThreadLocalRandom类上的方法:
double randomGenerator() {
    return ThreadLocalRandom.current().nextDouble(0.5);
}

0
如果您想使用一个种子生成多个数字,可以这样做:

例如:

public double[] GenerateNumbers(long seed, int amount) {
    double[] randomList = new double[amount];
    for (int i=0;i<amount;i++) {
        Random generator = new Random(seed);
        randomList[i] = Math.abs((double) (generator.nextLong() % 0.001) * 10000);
        seed--;
    }
    return randomList;
}

如果您使用相同的种子,它将显示相同的列表。


1
你只需要一次性种子PRG。 - Jason Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接